mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-30 10:36:35 +02:00
Desktop: Speed up pasting text and images in Rich Text Editor
This commit is contained in:
parent
4b7f0bfbb9
commit
b1877fcd0d
@ -15,7 +15,6 @@ import useContextMenu from './utils/useContextMenu';
|
||||
import { copyHtmlToClipboard } from '../../utils/clipboardUtils';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import { MarkupLanguage, MarkupToHtml } from '@joplin/renderer';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import setupToolbarButtons from './utils/setupToolbarButtons';
|
||||
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
|
||||
@ -31,10 +30,13 @@ import lightTheme from '@joplin/lib/themes/light';
|
||||
import { Options as NoteStyleOptions } from '@joplin/renderer/noteStyle';
|
||||
import markupRenderOptions from '../../utils/markupRenderOptions';
|
||||
import { DropHandler } from '../../utils/useDropHandler';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
const md5 = require('md5');
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
|
||||
const logger = Logger.create('TinyMCE');
|
||||
|
||||
// In TinyMCE 5.2, when setting the body to '<div id="rendered-md"></div>',
|
||||
// it would end up as '<div id="rendered-md"><br/></div>' once rendered
|
||||
// (an additional <br/> was inserted).
|
||||
@ -153,7 +155,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
if (anchor) {
|
||||
anchor.scrollIntoView();
|
||||
} else {
|
||||
reg.logger().warn('TinyMce: could not find anchor with ID ', anchorName);
|
||||
logger.warn('could not find anchor with ID ', anchorName);
|
||||
}
|
||||
} else {
|
||||
props.onMessage({ channel: href });
|
||||
@ -193,7 +195,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
execCommand: async (cmd: EditorCommand) => {
|
||||
if (!editor) return false;
|
||||
|
||||
reg.logger().debug('TinyMce: execCommand', cmd);
|
||||
logger.debug('execCommand', cmd);
|
||||
|
||||
let commandProcessed = true;
|
||||
|
||||
@ -215,7 +217,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
} else if (cmd.value.type === 'files') {
|
||||
insertResourcesIntoContentRef.current(cmd.value.paths, { createFileURL: !!cmd.value.createFileURL });
|
||||
} else {
|
||||
reg.logger().warn('TinyMCE: unsupported drop item: ', cmd);
|
||||
logger.warn('unsupported drop item: ', cmd);
|
||||
}
|
||||
} else {
|
||||
commandProcessed = false;
|
||||
@ -249,7 +251,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
}
|
||||
|
||||
if (!joplinCommandToTinyMceCommands[cmd.name]) {
|
||||
reg.logger().warn('TinyMCE: unsupported Joplin command: ', cmd);
|
||||
logger.warn('unsupported Joplin command: ', cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1132,16 +1134,20 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
const resourceMds = await getResourcesFromPasteEvent(event);
|
||||
|
||||
if (shouldPasteResources(pastedText, pastedHtml, resourceMds)) {
|
||||
logger.info(`onPaste: pasting ${resourceMds.length} resources`);
|
||||
if (resourceMds.length) {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
}
|
||||
} else {
|
||||
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
|
||||
logger.info('onPaste: pasting as a Markdown tag');
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, pastedText, markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
} else { // Paste regular text
|
||||
if (pastedHtml) { // Handles HTML
|
||||
logger.info('onPaste: pasting as HTML');
|
||||
|
||||
const modifiedHtml = await processPastedHtml(
|
||||
pastedHtml,
|
||||
prop_htmlToMarkdownRef.current,
|
||||
@ -1149,6 +1155,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
);
|
||||
editor.insertContent(modifiedHtml);
|
||||
} else { // Handles plain text
|
||||
logger.info('onPaste: pasting as text');
|
||||
pasteAsPlainText(pastedText);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export default (pastedText: string, pastedHtml: string, resourceMds: string[]) =
|
||||
logger.info('Resources:', resourceMds);
|
||||
|
||||
if (pastedText) {
|
||||
logger.info('Not pasting resources because the clipboard contains plain text');
|
||||
logger.info('Not pasting resources only because the clipboard contains plain text');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -152,8 +152,24 @@ export async function processPastedHtml(html: string, htmlToMd: HtmlToMarkdownHa
|
||||
allImageUrls.push(src);
|
||||
});
|
||||
|
||||
const downloadImage = async (imageSrc: string) => {
|
||||
try {
|
||||
const filePath = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}`;
|
||||
await shim.fetchBlob(imageSrc, { path: filePath });
|
||||
const createdResource = await shim.createResourceFromPath(filePath);
|
||||
await shim.fsDriver().remove(filePath);
|
||||
mappedResources[imageSrc] = `file://${encodeURI(Resource.fullPath(createdResource))}`;
|
||||
} catch (error) {
|
||||
logger.warn(`Error creating a resource for ${imageSrc}.`, error);
|
||||
mappedResources[imageSrc] = imageSrc;
|
||||
}
|
||||
};
|
||||
|
||||
const downloadImages: Promise<void>[] = [];
|
||||
|
||||
for (const imageSrc of allImageUrls) {
|
||||
if (!mappedResources[imageSrc]) {
|
||||
logger.info(`processPastedHtml: Processing image ${imageSrc}`);
|
||||
try {
|
||||
if (imageSrc.startsWith('file')) {
|
||||
const imageFilePath = path.normalize(fileUriToPath(imageSrc));
|
||||
@ -165,22 +181,20 @@ export async function processPastedHtml(html: string, htmlToMd: HtmlToMarkdownHa
|
||||
const createdResource = await shim.createResourceFromPath(imageFilePath);
|
||||
mappedResources[imageSrc] = `file://${encodeURI(Resource.fullPath(createdResource))}`;
|
||||
}
|
||||
} else if (imageSrc.startsWith('data:')) { // Data URIs
|
||||
} else if (imageSrc.startsWith('data:')) {
|
||||
mappedResources[imageSrc] = imageSrc;
|
||||
} else {
|
||||
const filePath = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}`;
|
||||
await shim.fetchBlob(imageSrc, { path: filePath });
|
||||
const createdResource = await shim.createResourceFromPath(filePath);
|
||||
await shim.fsDriver().remove(filePath);
|
||||
mappedResources[imageSrc] = `file://${encodeURI(Resource.fullPath(createdResource))}`;
|
||||
downloadImages.push(downloadImage(imageSrc));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`Error creating a resource for ${imageSrc}.`, error);
|
||||
logger.warn(`processPastedHtml: Error creating a resource for ${imageSrc}.`, error);
|
||||
mappedResources[imageSrc] = imageSrc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(downloadImages);
|
||||
|
||||
// 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
|
||||
|
Loading…
Reference in New Issue
Block a user