You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-07-16 00:14:34 +02:00
Desktop: WYSIWYG: Allow pasting images in editor
This commit is contained in:
@ -3,7 +3,7 @@ import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHand
|
|||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
||||||
import { commandAttachFileToBody } from '../../utils/resourceHandling';
|
import { commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
|
||||||
import { ScrollOptions, ScrollOptionTypes } from '../../utils/types';
|
import { ScrollOptions, ScrollOptionTypes } from '../../utils/types';
|
||||||
import { textOffsetToCursorPosition, useScrollHandler, useRootWidth, usePrevious, lineLeftSpaces, selectionRangeCurrentLine, selectionRangePreviousLine, currentTextOffset, textOffsetSelection, selectedText, useSelectionRange } from './utils';
|
import { textOffsetToCursorPosition, useScrollHandler, useRootWidth, usePrevious, lineLeftSpaces, selectionRangeCurrentLine, selectionRangePreviousLine, currentTextOffset, textOffsetSelection, selectedText, useSelectionRange } from './utils';
|
||||||
import useListIdent from './utils/useListIdent';
|
import useListIdent from './utils/useListIdent';
|
||||||
@ -15,12 +15,9 @@ const AceEditorReact = require('react-ace').default;
|
|||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const Note = require('lib/models/Note.js');
|
const Note = require('lib/models/Note.js');
|
||||||
const { clipboard } = require('electron');
|
const { clipboard } = require('electron');
|
||||||
const mimeUtils = require('lib/mime-utils.js').mime;
|
|
||||||
const Setting = require('lib/models/Setting.js');
|
const Setting = require('lib/models/Setting.js');
|
||||||
const NoteTextViewer = require('../../../NoteTextViewer.min');
|
const NoteTextViewer = require('../../../NoteTextViewer.min');
|
||||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||||
const md5 = require('md5');
|
|
||||||
const { shim } = require('lib/shim.js');
|
|
||||||
const Menu = bridge().Menu;
|
const Menu = bridge().Menu;
|
||||||
const MenuItem = bridge().MenuItem;
|
const MenuItem = bridge().MenuItem;
|
||||||
const markdownUtils = require('lib/markdownUtils');
|
const markdownUtils = require('lib/markdownUtils');
|
||||||
@ -332,29 +329,10 @@ function AceEditor(props: NoteBodyEditorProps, ref: any) {
|
|||||||
}, [editor, props.content, addListItem, wrapSelectionWithStrings, selectionRangeCurrentLine, aceEditor_change, setEditorPercentScroll, setViewerPercentScroll, resetScroll, renderedBody]);
|
}, [editor, props.content, addListItem, wrapSelectionWithStrings, selectionRangeCurrentLine, aceEditor_change, setEditorPercentScroll, setViewerPercentScroll, resetScroll, renderedBody]);
|
||||||
|
|
||||||
const onEditorPaste = useCallback(async (event: any = null) => {
|
const onEditorPaste = useCallback(async (event: any = null) => {
|
||||||
const formats = clipboard.availableFormats();
|
const resourceMds = await handlePasteEvent(event);
|
||||||
for (let i = 0; i < formats.length; i++) {
|
if (!resourceMds.length) return;
|
||||||
const format = formats[i].toLowerCase();
|
wrapSelectionWithStrings('', '', resourceMds.join('\n'));
|
||||||
const formatType = format.split('/')[0];
|
}, [wrapSelectionWithStrings]);
|
||||||
|
|
||||||
const position = currentTextOffset(editor, props.content);
|
|
||||||
|
|
||||||
if (formatType === 'image') {
|
|
||||||
if (event) event.preventDefault();
|
|
||||||
|
|
||||||
const image = clipboard.readImage();
|
|
||||||
|
|
||||||
const fileExt = mimeUtils.toFileExtension(format);
|
|
||||||
const filePath = `${Setting.value('tempDir')}/${md5(Date.now())}.${fileExt}`;
|
|
||||||
|
|
||||||
await shim.writeImageToFile(image, format, filePath);
|
|
||||||
const newBody = await commandAttachFileToBody(props.content, [filePath], { position });
|
|
||||||
await shim.fsDriver().remove(filePath);
|
|
||||||
|
|
||||||
aceEditor_change(newBody);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [editor, props.content, aceEditor_change]);
|
|
||||||
|
|
||||||
const onEditorKeyDown = useCallback((event: any) => {
|
const onEditorKeyDown = useCallback((event: any) => {
|
||||||
setLastKeys(prevLastKeys => {
|
setLastKeys(prevLastKeys => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
|
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||||
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
||||||
import { resourcesStatus, commandAttachFileToBody } from '../../utils/resourceHandling';
|
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
|
||||||
import useScroll from './utils/useScroll';
|
import useScroll from './utils/useScroll';
|
||||||
import { menuItems, ContextMenuOptions, ContextMenuItemType } from '../../utils/contextMenu';
|
import { menuItems, ContextMenuOptions, ContextMenuItemType } from '../../utils/contextMenu';
|
||||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||||
@ -879,6 +879,11 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onPaste(event:any) {
|
async function onPaste(event:any) {
|
||||||
|
const resourceMds = await handlePasteEvent(event);
|
||||||
|
if (resourceMds.length) {
|
||||||
|
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||||
|
editor.insertContent(result.html);
|
||||||
|
} else {
|
||||||
const pastedText = event.clipboardData.getData('text');
|
const pastedText = event.clipboardData.getData('text');
|
||||||
|
|
||||||
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
|
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
|
||||||
@ -893,6 +898,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||||||
onChangeHandler();
|
onChangeHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onKeyDown(event:any) {
|
function onKeyDown(event:any) {
|
||||||
// Handle "paste as text". Note that when pressing CtrlOrCmd+Shift+V it's going
|
// Handle "paste as text". Note that when pressing CtrlOrCmd+Shift+V it's going
|
||||||
|
@ -7,6 +7,9 @@ const { bridge } = require('electron').remote.require('./bridge');
|
|||||||
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
||||||
const { reg } = require('lib/registry.js');
|
const { reg } = require('lib/registry.js');
|
||||||
const joplinRendererUtils = require('lib/joplin-renderer').utils;
|
const joplinRendererUtils = require('lib/joplin-renderer').utils;
|
||||||
|
const { clipboard } = require('electron');
|
||||||
|
const mimeUtils = require('lib/mime-utils.js').mime;
|
||||||
|
const md5 = require('md5');
|
||||||
|
|
||||||
export async function handleResourceDownloadMode(noteBody: string) {
|
export async function handleResourceDownloadMode(noteBody: string) {
|
||||||
if (noteBody && Setting.value('sync.resourceDownloadMode') === 'auto') {
|
if (noteBody && Setting.value('sync.resourceDownloadMode') === 'auto') {
|
||||||
@ -97,3 +100,28 @@ export function resourcesStatus(resourceInfos: any) {
|
|||||||
}
|
}
|
||||||
return joplinRendererUtils.resourceStatusName(lowestIndex);
|
return joplinRendererUtils.resourceStatusName(lowestIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function handlePasteEvent(event:any) {
|
||||||
|
const output = [];
|
||||||
|
const formats = clipboard.availableFormats();
|
||||||
|
for (let i = 0; i < formats.length; i++) {
|
||||||
|
const format = formats[i].toLowerCase();
|
||||||
|
const formatType = format.split('/')[0];
|
||||||
|
|
||||||
|
if (formatType === 'image') {
|
||||||
|
if (event) event.preventDefault();
|
||||||
|
|
||||||
|
const image = clipboard.readImage();
|
||||||
|
|
||||||
|
const fileExt = mimeUtils.toFileExtension(format);
|
||||||
|
const filePath = `${Setting.value('tempDir')}/${md5(Date.now())}.${fileExt}`;
|
||||||
|
|
||||||
|
await shim.writeImageToFile(image, format, filePath);
|
||||||
|
const md = await commandAttachFileToBody('', [filePath]);
|
||||||
|
await shim.fsDriver().remove(filePath);
|
||||||
|
|
||||||
|
output.push(md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user