diff --git a/src/extras/editor/linkPreview.ts b/src/extras/editor/linkPreview.ts index b75b78c..cbf88e5 100644 --- a/src/extras/editor/linkPreview.ts +++ b/src/extras/editor/linkPreview.ts @@ -1,7 +1,7 @@ import { EditorState, Plugin, PluginKey } from "prosemirror-state"; import { Popup } from "./popup"; -export { initLinkPreviewPlugin }; +export { initLinkPreviewPlugin, LinkPreviewOptions }; declare const _currentEditorInstance: { _editorCore: EditorCore; @@ -191,43 +191,57 @@ class LinkPreviewState { } } -function initLinkPreviewPlugin(options: LinkPreviewOptions) { +function initLinkPreviewPlugin( + plugins: readonly Plugin[], + options: LinkPreviewOptions, +) { const core = _currentEditorInstance._editorCore; console.log("Init BN Link Preview Plugin"); const key = new PluginKey("linkPreviewPlugin"); - const newState = core.view.state.reconfigure({ - plugins: [ - ...core.view.state.plugins, - new Plugin({ - key, - state: { - init(config, state) { - return new LinkPreviewState(state, options); + return [ + ...plugins, + new Plugin({ + key, + state: { + init(config, state) { + return new LinkPreviewState(state, options); + }, + apply: (tr, pluginState, oldState, newState) => { + pluginState.update(newState, oldState); + return pluginState; + }, + }, + props: { + handleDOMEvents: { + mousemove: (view, event) => { + const pluginState = key.getState(view.state) as LinkPreviewState; + pluginState.update(view.state); + pluginState.handleMouseMove(event); }, - apply: (tr, pluginState, oldState, newState) => { - pluginState.update(newState, oldState); - return pluginState; + keydown: (view, event) => { + const pluginState = key.getState(view.state) as LinkPreviewState; + pluginState.handleKeydown(event); + }, + wheel: (view, event) => { + const pluginState = key.getState(view.state) as LinkPreviewState; + pluginState.popup?.layoutPopup(pluginState); }, }, - props: { - handleDOMEvents: { - mousemove: (view, event) => { - const pluginState = key.getState(view.state) as LinkPreviewState; - pluginState.update(view.state); - pluginState.handleMouseMove(event); - }, - keydown: (view, event) => { - const pluginState = key.getState(view.state) as LinkPreviewState; - pluginState.handleKeydown(event); - }, - wheel: (view, event) => { - const pluginState = key.getState(view.state) as LinkPreviewState; - pluginState.popup?.layoutPopup(pluginState); - }, + }, + view: (editorView) => { + return { + update(view, prevState) { + const pluginState = key.getState(view.state) as LinkPreviewState; + pluginState.update(view.state, prevState); }, - }, - }), - ], - }); - core.view.updateState(newState); + destroy() { + const pluginState = key.getState( + editorView.state, + ) as LinkPreviewState; + pluginState.destroy(); + }, + }; + }, + }), + ]; } diff --git a/src/extras/editor/pasteMarkdown.ts b/src/extras/editor/pasteMarkdown.ts index 06f7c9b..bd7e9ee 100644 --- a/src/extras/editor/pasteMarkdown.ts +++ b/src/extras/editor/pasteMarkdown.ts @@ -7,91 +7,92 @@ declare const _currentEditorInstance: { _editorCore: EditorCore; }; -function initPasteMarkdownPlugin() { +function initPasteMarkdownPlugin(plugins: readonly Plugin[]) { const core = _currentEditorInstance._editorCore; console.log("Init BN Paste Markdown Plugin"); const key = new PluginKey("pasteDropPlugin"); - const oldPlugins = core.view.state.plugins; - const oldPastePluginIndex = oldPlugins.findIndex( + const oldPastePluginIndex = plugins.findIndex( (plugin) => plugin.props.handlePaste && plugin.props.handleDrop, ); if (oldPastePluginIndex === -1) { console.error("Paste plugin not found"); - return; + return plugins; } - const oldPastePlugin = oldPlugins[oldPastePluginIndex]; - const newState = core.view.state.reconfigure({ - plugins: [ - ...oldPlugins.slice(0, oldPastePluginIndex), - new Plugin({ - key, - props: { - handlePaste: (view, event, slice) => { - if (!event.clipboardData) { - return false; - } - const markdown = getMarkdown(event.clipboardData); + const oldPastePlugin = plugins[oldPastePluginIndex]; + return [ + ...plugins.slice(0, oldPastePluginIndex), + new Plugin({ + key, + props: { + handlePaste: (view, event, slice) => { + if (!event.clipboardData) { + return false; + } + const markdown = getMarkdown(event.clipboardData); - if (!markdown) { - // Try the old paste plugin - return oldPastePlugin.props.handlePaste?.apply(oldPastePlugin, [ - view, - event, - slice, - ]); - } + if (!markdown) { + // Try the old paste plugin + return oldPastePlugin.props.handlePaste?.apply(oldPastePlugin, [ + view, + event, + slice, + ]); + } - md2html(markdown).then((html: string) => { - const slice = window.BetterNotesEditorAPI.getSliceFromHTML( - view.state, - html, - ); - const tr = view.state.tr.replaceSelection(slice); - view.dispatch(tr); - }); - return true; - }, - handleDrop: (view, event, slice, moved) => { - if (!event.dataTransfer) { - return false; - } - - const markdown = getMarkdown(event.dataTransfer); - if (!markdown) { - // Try the old drop plugin first - return oldPastePlugin.props.handleDrop?.apply(oldPastePlugin, [ - view, - event, - slice, - moved, - ]); - } - - md2html(markdown).then((html: string) => { - const slice = window.BetterNotesEditorAPI.getSliceFromHTML( - view.state, - html, - ); - const pos = view.posAtCoords({ - left: event.clientX, - top: event.clientY, - }); - if (!pos) { - return; - } - // Insert the slice to the current position - const tr = view.state.tr.insert(pos.pos, slice); - view.dispatch(tr); - }); - - return true; - }, + md2html(markdown).then((html: string) => { + const slice = window.BetterNotesEditorAPI.getSliceFromHTML( + view.state, + html, + ); + const tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr); + }); + return true; }, - }), - ...oldPlugins.slice(oldPastePluginIndex + 1), - ], - }); - core.view.updateState(newState); + handleDrop: (view, event, slice, moved) => { + if (!event.dataTransfer) { + return false; + } + + const markdown = getMarkdown(event.dataTransfer); + if (!markdown) { + // Try the old drop plugin first + return oldPastePlugin.props.handleDrop?.apply(oldPastePlugin, [ + view, + event, + slice, + moved, + ]); + } + + md2html(markdown).then((html: string) => { + const slice = window.BetterNotesEditorAPI.getSliceFromHTML( + view.state, + html, + ); + const pos = view.posAtCoords({ + left: event.clientX, + top: event.clientY, + }); + if (!pos) { + return; + } + // Insert the slice to the current position + const tr = view.state.tr.insert(pos.pos, slice); + view.dispatch(tr); + }); + + return true; + }, + }, + view: (editorView) => { + return { + destroy() {}, + }; + }, + }), + ...plugins.slice(oldPastePluginIndex + 1), + ]; } function getMarkdown(clipboardData: DataTransfer) { diff --git a/src/extras/editor/plugins.ts b/src/extras/editor/plugins.ts new file mode 100644 index 0000000..51d5d72 --- /dev/null +++ b/src/extras/editor/plugins.ts @@ -0,0 +1,20 @@ +import { initLinkPreviewPlugin, LinkPreviewOptions } from "./linkPreview"; +import { initPasteMarkdownPlugin } from "./pasteMarkdown"; + +export { initPlugins }; + +declare const _currentEditorInstance: { + _editorCore: EditorCore; +}; + +function initPlugins(options: LinkPreviewOptions) { + const core = _currentEditorInstance._editorCore; + let plugins = core.view.state.plugins; + plugins = initLinkPreviewPlugin(plugins, options); + plugins = initPasteMarkdownPlugin(plugins); + // Collect all plugins and reconfigure the state only once + const newState = core.view.state.reconfigure({ + plugins, + }); + core.view.updateState(newState); +} diff --git a/src/extras/editorScript.ts b/src/extras/editorScript.ts index 7237a61..fb414c7 100644 --- a/src/extras/editorScript.ts +++ b/src/extras/editorScript.ts @@ -11,8 +11,7 @@ import { } from "prosemirror-model"; import { EditorState, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { initLinkPreviewPlugin } from "./editor/linkPreview"; -import { initPasteMarkdownPlugin } from "./editor/pasteMarkdown"; +import { initPlugins } from "./editor/plugins"; declare const _currentEditorInstance: { _editorCore: EditorCore; @@ -378,8 +377,7 @@ export const BetterNotesEditorAPI = { getSliceFromHTML, getNodeFromHTML, setSelection, - initLinkPreviewPlugin, - initPasteMarkdownPlugin, + initPlugins, }; // @ts-ignore diff --git a/src/utils/editor.ts b/src/utils/editor.ts index ebafdfb..1da2689 100644 --- a/src/utils/editor.ts +++ b/src/utils/editor.ts @@ -453,7 +453,7 @@ function initLinkPreview(editor: Zotero.EditorInstance) { } const EditorAPI = getEditorAPI(editor); safeCall(() => - EditorAPI.initLinkPreviewPlugin( + EditorAPI.initPlugins( Components.utils.cloneInto( { setPreviewContent: ( @@ -485,7 +485,6 @@ function initLinkPreview(editor: Zotero.EditorInstance) { ), ), ); - safeCall(() => EditorAPI.initPasteMarkdownPlugin()); } function safeCall(callback: () => void) {