diff --git a/.eslintignore b/.eslintignore index 812906926..df44b7f2d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -294,6 +294,7 @@ packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.js packages/app-desktop/gui/NoteEditor/utils/contextMenu.js packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.js packages/app-desktop/gui/NoteEditor/utils/index.js +packages/app-desktop/gui/NoteEditor/utils/markupRenderOptions.js packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.js packages/app-desktop/gui/NoteEditor/utils/resourceHandling.js packages/app-desktop/gui/NoteEditor/utils/types.js diff --git a/.gitignore b/.gitignore index bb5e41ae4..eeb871ef5 100644 --- a/.gitignore +++ b/.gitignore @@ -274,6 +274,7 @@ packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.js packages/app-desktop/gui/NoteEditor/utils/contextMenu.js packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.js packages/app-desktop/gui/NoteEditor/utils/index.js +packages/app-desktop/gui/NoteEditor/utils/markupRenderOptions.js packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.js packages/app-desktop/gui/NoteEditor/utils/resourceHandling.js packages/app-desktop/gui/NoteEditor/utils/types.js diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx index 717f300de..5136f4141 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHandle, useMemo, ForwardedRef } from 'react'; // eslint-disable-next-line no-unused-vars -import { EditorCommand, NoteBodyEditorProps, NoteBodyEditorRef } from '../../../utils/types'; +import { EditorCommand, MarkupToHtmlOptions, NoteBodyEditorProps, NoteBodyEditorRef } from '../../../utils/types'; import { commandAttachFileToBody, getResourcesFromPasteEvent } from '../../../utils/resourceHandling'; import { ScrollOptions, ScrollOptionTypes } from '../../../utils/types'; import { CommandValue } from '../../../utils/types'; @@ -29,7 +29,6 @@ const debounce = require('debounce'); import { reg } from '@joplin/lib/registry'; import ErrorBoundary from '../../../../ErrorBoundary'; -import { MarkupToHtmlOptions } from '../../../utils/useMarkupToHtml'; import useStyles from '../utils/useStyles'; import useContextMenu from '../utils/useContextMenu'; import useWebviewIpcMessage from '../utils/useWebviewIpcMessage'; diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx index 2c2dd24b3..e678c0657 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHandle, useMemo, ForwardedRef } from 'react'; -import { EditorCommand, NoteBodyEditorProps, NoteBodyEditorRef, OnChangeEvent } from '../../../utils/types'; +import { EditorCommand, MarkupToHtmlOptions, NoteBodyEditorProps, NoteBodyEditorRef, OnChangeEvent } from '../../../utils/types'; import { getResourcesFromPasteEvent } from '../../../utils/resourceHandling'; import { ScrollOptions, ScrollOptionTypes } from '../../../utils/types'; import NoteTextViewer from '../../../../NoteTextViewer'; @@ -16,7 +16,6 @@ import { MarkupToHtml } from '@joplin/renderer'; const { clipboard } = require('electron'); import { reg } from '@joplin/lib/registry'; import ErrorBoundary from '../../../../ErrorBoundary'; -import { MarkupToHtmlOptions } from '../../../utils/useMarkupToHtml'; import { EditorKeymap, EditorLanguageType, EditorSettings } from '@joplin/editor/types'; import useStyles from '../utils/useStyles'; import { EditorEvent, EditorEventType } from '@joplin/editor/events'; diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx index 1f178ad6d..61b900bd7 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react'; -import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos } from '../../utils/types'; +import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos, HtmlToMarkdownHandler } from '../../utils/types'; import { resourcesStatus, commandAttachFileToBody, getResourcesFromPasteEvent, processPastedHtml, attachedResources } from '../../utils/resourceHandling'; import useScroll from './utils/useScroll'; import styles_ from './styles'; @@ -20,7 +20,6 @@ import BaseItem from '@joplin/lib/models/BaseItem'; import setupToolbarButtons from './utils/setupToolbarButtons'; import { plainTextToHtml } from '@joplin/lib/htmlUtils'; import openEditDialog from './utils/openEditDialog'; -import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml'; import { themeStyle } from '@joplin/lib/theme'; import { loadScript } from '../../../utils/loadScript'; import bridge from '../../../../services/bridge'; @@ -30,25 +29,11 @@ import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCo import shouldPasteResources from './utils/shouldPasteResources'; import lightTheme from '@joplin/lib/themes/light'; import { Options as NoteStyleOptions } from '@joplin/renderer/noteStyle'; +import markupRenderOptions from '../../utils/markupRenderOptions'; const md5 = require('md5'); const { clipboard } = require('electron'); const supportedLocales = require('./supportedLocales'); -function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions { - return { - plugins: { - checkbox: { - checkboxRenderingType: 2, - }, - link_open: { - linkRenderingType: 2, - }, - }, - replaceResourceInternalToExternalLinks: true, - ...override, - }; -} - // In TinyMCE 5.2, when setting the body to '
', // it would end up as '

' once rendered // (an additional
was inserted). @@ -123,7 +108,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => { const { scrollToPercent } = useScroll({ editor, onScroll: props.onScroll }); usePluginServiceRegistration(ref); - useContextMenu(editor, props.plugins, props.dispatch); + useContextMenu(editor, props.plugins, props.dispatch, props.htmlToMarkdown, props.markupToHtml); const dispatchDidUpdate = (editor: any) => { if (dispatchDidUpdateIID_) shim.clearTimeout(dispatchDidUpdateIID_); @@ -978,7 +963,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => { props_onChangeRef.current = props.onChange; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - const prop_htmlToMarkdownRef = useRef(); + const prop_htmlToMarkdownRef = useRef(); prop_htmlToMarkdownRef.current = props.htmlToMarkdown; const nextOnChangeEventInfo = useRef(null); @@ -1136,7 +1121,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => { editor.insertContent(result.html); } else { // Paste regular text if (pastedHtml) { // Handles HTML - const modifiedHtml = await processPastedHtml(pastedHtml); + const modifiedHtml = await processPastedHtml( + pastedHtml, + prop_htmlToMarkdownRef.current, + markupToHtml.current, + ); editor.insertContent(modifiedHtml); } else { // Handles plain text pasteAsPlainText(pastedText); diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts index 85559ebd4..527ed15b9 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts @@ -12,6 +12,7 @@ import Setting from '@joplin/lib/models/Setting'; import Resource from '@joplin/lib/models/Resource'; import { TinyMceEditorEvents } from './types'; +import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from '../../../utils/types'; const menuUtils = new MenuUtils(CommandService.instance()); @@ -42,11 +43,11 @@ interface ContextMenuActionOptions { const contextMenuActionOptions: ContextMenuActionOptions = { current: null }; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied -export default function(editor: any, plugins: PluginStates, dispatch: Function) { +export default function(editor: any, plugins: PluginStates, dispatch: Function, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) { useEffect(() => { if (!editor) return () => {}; - const contextMenuItems = menuItems(dispatch); + const contextMenuItems = menuItems(dispatch, htmlToMd, mdToHtml); function onContextMenu(_event: any, params: any) { const element = contextMenuElement(editor, params.x, params.y); @@ -82,6 +83,8 @@ export default function(editor: any, plugins: PluginStates, dispatch: Function) fireEditorEvent: (event: TinyMceEditorEvents) => { editor.fire(event); }, + htmlToMd, + mdToHtml, }; let template = []; @@ -118,5 +121,5 @@ export default function(editor: any, plugins: PluginStates, dispatch: Function) bridge().window().webContents.off('context-menu', onContextMenu); } }; - }, [editor, plugins, dispatch]); + }, [editor, plugins, dispatch, htmlToMd, mdToHtml]); } diff --git a/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx b/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx index 2a9795b99..de9185b38 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx @@ -341,7 +341,7 @@ function NoteEditor(props: NoteEditorProps) { // eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied }, [formNote, handleProvisionalFlag]); - const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote); + const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml); const externalEditWatcher_noteChange = useCallback((event: any) => { if (event.id === formNote.id) { diff --git a/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts b/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts index 0ef3a6b63..d9cf11afd 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts @@ -14,6 +14,7 @@ import { TinyMceEditorEvents } from '../NoteBody/TinyMCE/utils/types'; import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly'; import Setting from '@joplin/lib/models/Setting'; import ItemChange from '@joplin/lib/models/ItemChange'; +import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types'; const fs = require('fs-extra'); const { writeFile } = require('fs-extra'); const { clipboard } = require('electron'); @@ -77,7 +78,7 @@ export async function openItemById(itemId: string, dispatch: Function, hash = '' } // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied -export function menuItems(dispatch: Function): ContextMenuItems { +export function menuItems(dispatch: Function, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler): ContextMenuItems { return { open: { label: _('Open...'), @@ -179,7 +180,7 @@ export function menuItems(dispatch: Function): ContextMenuItems { let content = pastedHtml ? pastedHtml : clipboard.readText(); if (pastedHtml) { - content = await processPastedHtml(pastedHtml); + content = await processPastedHtml(pastedHtml, htmlToMd, mdToHtml); } options.insertContent(content); @@ -207,7 +208,7 @@ export function menuItems(dispatch: Function): ContextMenuItems { export default async function contextMenu(options: ContextMenuOptions, dispatch: Function) { const menu = new Menu(); - const items = menuItems(dispatch); + const items = menuItems(dispatch, options.htmlToMd, options.mdToHtml); if (!('readyOnly' in options)) options.isReadOnly = true; for (const itemKey in items) { diff --git a/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts b/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts index 613d5a9cd..94f3c0b38 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts @@ -1,6 +1,9 @@ import Resource from '@joplin/lib/models/Resource'; import Logger from '@joplin/utils/Logger'; +import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types'; + const logger = Logger.create('contextMenuUtils'); + export enum ContextMenuItemType { None = '', Image = 'image', @@ -22,6 +25,8 @@ export interface ContextMenuOptions { isReadOnly?: boolean; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied fireEditorEvent: Function; + htmlToMd: HtmlToMarkdownHandler; + mdToHtml: MarkupToHtmlHandler; } export interface ContextMenuItem { diff --git a/packages/app-desktop/gui/NoteEditor/utils/markupRenderOptions.ts b/packages/app-desktop/gui/NoteEditor/utils/markupRenderOptions.ts new file mode 100644 index 000000000..f3e39db64 --- /dev/null +++ b/packages/app-desktop/gui/NoteEditor/utils/markupRenderOptions.ts @@ -0,0 +1,16 @@ +import { MarkupToHtmlOptions } from './types'; + +export default (override: MarkupToHtmlOptions = null): MarkupToHtmlOptions => { + return { + plugins: { + checkbox: { + checkboxRenderingType: 2, + }, + link_open: { + linkRenderingType: 2, + }, + }, + replaceResourceInternalToExternalLinks: true, + ...override, + }; +}; diff --git a/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.ts b/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.ts index 7df618d26..b758c3a3a 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.ts @@ -1,5 +1,8 @@ import Setting from '@joplin/lib/models/Setting'; import { processPastedHtml } from './resourceHandling'; +import markupLanguageUtils from '@joplin/lib/markupLanguageUtils'; +import HtmlToMd from '@joplin/lib/HtmlToMd'; +import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types'; describe('resourceHandling', () => { it('should sanitize pasted HTML', async () => { @@ -19,7 +22,32 @@ describe('resourceHandling', () => { ]; for (const [html, expected] of testCases) { - expect(await processPastedHtml(html)).toBe(expected); + expect(await processPastedHtml(html, null, null)).toBe(expected); } }); + + it('should clean up pasted HTML', async () => { + const markupToHtml: MarkupToHtmlHandler = async (markupLanguage, markup, options) => { + const conv = markupLanguageUtils.newMarkupToHtml({}, { + resourceBaseUrl: `file://${Setting.value('resourceDir')}/`, + customCss: '', + }); + return conv.render(markupLanguage, markup, {}, options); + }; + + const htmlToMd: HtmlToMarkdownHandler = async (_markupLanguage, html, _originalCss) => { + const conv = new HtmlToMd(); + return conv.parse(html); + }; + + const testCases = [ + ['

Hello

World

', '

Hello

\n

World

\n'], + ['', ''], + ]; + + for (const [html, expected] of testCases) { + expect(await processPastedHtml(html, htmlToMd, markupToHtml)).toBe(expected); + } + }); + }); diff --git a/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts b/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts index efdefced0..836bddf13 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts @@ -10,6 +10,8 @@ import rendererHtmlUtils, { extractHtmlBody } from '@joplin/renderer/htmlUtils'; import Logger from '@joplin/utils/Logger'; import { fileUriToPath } from '@joplin/utils/url'; import { MarkupLanguage } from '@joplin/renderer'; +import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types'; +import markupRenderOptions from './markupRenderOptions'; const joplinRendererUtils = require('@joplin/renderer').utils; const { clipboard } = require('electron'); const mimeUtils = require('@joplin/lib/mime-utils.js').mime; @@ -135,7 +137,7 @@ export async function getResourcesFromPasteEvent(event: any) { return output; } -export async function processPastedHtml(html: string) { +export async function processPastedHtml(html: string, htmlToMd: HtmlToMarkdownHandler | null, mdToHtml: MarkupToHtmlHandler | null) { const allImageUrls: string[] = []; const mappedResources: Record = {}; @@ -179,6 +181,15 @@ export async function processPastedHtml(html: string) { } } + // TinyMCE can accept any type of HTML, including HTML that may not be preserved once saved as + // Markdown. For example the content may have a dark background which would be supported by + // TinyMCE, but lost once the note is saved. So here we convert the HTML to Markdown then back + // to HTML to ensure that the content we paste will be handled correctly by the app. + if (htmlToMd && mdToHtml) { + const md = await htmlToMd(MarkupLanguage.Markdown, html, ''); + html = (await mdToHtml(MarkupLanguage.Markdown, md, markupRenderOptions({ bodyOnly: true }))).html; + } + return extractHtmlBody(rendererHtmlUtils.sanitizeHtml( htmlUtils.replaceImageUrls(html, (src: string) => { return mappedResources[src]; diff --git a/packages/app-desktop/gui/NoteEditor/utils/types.ts b/packages/app-desktop/gui/NoteEditor/utils/types.ts index 5a4029610..3ac9422a8 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/types.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/types.ts @@ -3,7 +3,6 @@ import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUt import { PluginStates } from '@joplin/lib/services/plugins/reducer'; import { MarkupLanguage } from '@joplin/renderer'; import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/types'; -import { MarkupToHtmlOptions } from './useMarkupToHtml'; import { Dispatch } from 'redux'; import { ProcessResultsRow } from '@joplin/lib/services/search/SearchEngine'; @@ -61,6 +60,24 @@ export interface NoteBodyEditorRef { execCommand(command: CommandValue): Promise; } +export interface MarkupToHtmlOptions { + replaceResourceInternalToExternalLinks?: boolean; + resourceInfos?: ResourceInfos; + contentMaxWidth?: number; + plugins?: Record; + bodyOnly?: boolean; + mapsToLine?: boolean; + useCustomPdfViewer?: boolean; + noteId?: string; + vendorDir?: string; + platformName?: string; + allowedFilePrefixes?: string[]; + whiteBackgroundNoteRendering?: boolean; +} + +export type MarkupToHtmlHandler = (markupLanguage: MarkupLanguage, markup: string, options: MarkupToHtmlOptions)=> Promise; +export type HtmlToMarkdownHandler = (markupLanguage: number, html: string, originalCss: string)=> Promise; + export interface NoteBodyEditorProps { style: any; ref: any; @@ -81,9 +98,8 @@ export interface NoteBodyEditorProps { onWillChange(event: any): void; onMessage(event: any): void; onScroll(event: { percent: number }): void; - markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: MarkupToHtmlOptions)=> Promise; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - htmlToMarkdown: Function; + markupToHtml: MarkupToHtmlHandler; + htmlToMarkdown: HtmlToMarkdownHandler; allAssets: (markupLanguage: MarkupLanguage, options: AllAssetsOptions)=> Promise; disabled: boolean; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied diff --git a/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts b/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts index 8c1ee70ad..e591c1b53 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts @@ -1,12 +1,12 @@ import { PluginStates } from '@joplin/lib/services/plugins/reducer'; import { useCallback, useMemo } from 'react'; -import { ResourceInfos } from './types'; import markupLanguageUtils from '../../../utils/markupLanguageUtils'; import Setting from '@joplin/lib/models/Setting'; import shim from '@joplin/lib/shim'; const { themeStyle } = require('@joplin/lib/theme'); import Note from '@joplin/lib/models/Note'; +import { MarkupToHtmlOptions } from './types'; interface HookDependencies { themeId: number; @@ -16,21 +16,6 @@ interface HookDependencies { whiteBackgroundNoteRendering: boolean; } -export interface MarkupToHtmlOptions { - replaceResourceInternalToExternalLinks?: boolean; - resourceInfos?: ResourceInfos; - contentMaxWidth?: number; - plugins?: Record; - bodyOnly?: boolean; - mapsToLine?: boolean; - useCustomPdfViewer?: boolean; - noteId?: string; - vendorDir?: string; - platformName?: string; - allowedFilePrefixes?: string[]; - whiteBackgroundNoteRendering?: boolean; -} - export default function useMarkupToHtml(deps: HookDependencies) { const { themeId, customCss, plugins, whiteBackgroundNoteRendering } = deps; diff --git a/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts b/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts index 57c9a8c71..341619ab4 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { FormNote } from './types'; +import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types'; import contextMenu from './contextMenu'; import CommandService from '@joplin/lib/services/CommandService'; import PostMessageService from '@joplin/lib/services/PostMessageService'; @@ -8,7 +8,7 @@ import { reg } from '@joplin/lib/registry'; const bridge = require('@electron/remote').require('./bridge').default; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied -export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote) { +export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) { return useCallback(async (event: any) => { const msg = event.channel ? event.channel : ''; const args = event.args; @@ -44,6 +44,8 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea htmlToCopy: '', insertContent: () => { console.warn('insertContent() not implemented'); }, fireEditorEvent: () => { console.warn('fireEditorEvent() not implemented'); }, + htmlToMd, + mdToHtml, }, dispatch); menu.popup({ window: bridge().window() }); diff --git a/packages/app-desktop/gui/PdfViewer.tsx b/packages/app-desktop/gui/PdfViewer.tsx index b8acd71b0..792eae65c 100644 --- a/packages/app-desktop/gui/PdfViewer.tsx +++ b/packages/app-desktop/gui/PdfViewer.tsx @@ -61,6 +61,8 @@ export default function PdfViewer(props: Props) { htmlToCopy: '', insertContent: () => { console.warn('insertContent() not implemented'); }, fireEditorEvent: () => { console.warn('fireEditorEvent() not implemented'); }, + htmlToMd: async (_a, b, _c) => b, + mdToHtml: async (_a, b, _c) => { return { html: b, pluginAssets: [], cssStrings: [] }; }, } as ContextMenuOptions, props.dispatch); menu.popup({ window: bridge().window() });