1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-20 18:48:28 +02:00

Desktop: Resolves #8931: Improve support for plugins in the Rich Text Editor (implement webviewApi.postMesage) (#10158)

This commit is contained in:
Henry Heino 2024-03-20 03:52:29 -07:00 committed by GitHub
parent eb06ac673b
commit eecad1aefc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 96 additions and 4 deletions

View File

@ -290,6 +290,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
packages/app-desktop/gui/NoteEditor/NoteEditor.js
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js

1
.gitignore vendored
View File

@ -270,6 +270,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
packages/app-desktop/gui/NoteEditor/NoteEditor.js
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js

View File

@ -3,7 +3,7 @@ const leftPad = require('left-pad');
export default function(context) {
return {
plugin: function(markdownIt, _options) {
const pluginId = context.pluginId;
const contentScriptId = context.contentScriptId;
const defaultRender = markdownIt.renderer.rules.fence || function(tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options, env, self);
@ -14,15 +14,30 @@ export default function(context) {
if (token.info !== 'justtesting') return defaultRender(tokens, idx, options, env, self);
const postMessageWithResponseTest = `
webviewApi.postMessage('${pluginId}', 'justtesting').then(function(response) {
webviewApi.postMessage('${contentScriptId}', 'justtesting').then(function(response) {
console.info('Got response in content script: ' + response);
});
return false;
`;
// Rich text editor support:
// The joplin-editable and joplin-source CSS classes mark the generated div
// as a region that needs special processing when converting back to markdown.
// This element helps Joplin reconstruct the original markdown.
const richTextEditorMetadata = `
<pre
class="joplin-source"
data-joplin-language="justtesting"
data-joplin-source-open="\`\`\`justtesting\n"
data-joplin-source-close="\`\`\`"
>${markdownIt.utils.escapeHtml(token.content)}</pre>
`;
return `
<div class="just-testing">
<p>JUST TESTING: <pre>${leftPad(token.content.trim(), 10, 'x')}</pre></p>
<div class="just-testing joplin-editable">
${richTextEditorMetadata}
<p>JUST TESTING: <pre>${markdownIt.utils.escapeHtml(leftPad(token.content.trim(), 10, 'x'))}</pre></p>
<p><a href="#" onclick="${postMessageWithResponseTest.replace(/\n/g, ' ')}">Click to post a message "justtesting" to plugin and check the response in the console</a></p>
</div>
`;

View File

@ -31,6 +31,7 @@ import { Options as NoteStyleOptions } from '@joplin/renderer/noteStyle';
import markupRenderOptions from '../../utils/markupRenderOptions';
import { DropHandler } from '../../utils/useDropHandler';
import Logger from '@joplin/utils/Logger';
import useWebViewApi from './utils/useWebViewApi';
const md5 = require('md5');
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@ -348,6 +349,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
};
}, []);
useWebViewApi(editor);
useEffect(() => {
const theme = themeStyle(props.themeId);
const backgroundColor = props.whiteBackgroundNoteRendering ? lightTheme.backgroundColor : theme.backgroundColor;

View File

@ -0,0 +1,72 @@
import PluginService from '@joplin/lib/services/plugins/PluginService';
import { useEffect } from 'react';
import { Editor } from 'tinymce';
const useWebViewApi = (editor: Editor) => {
useEffect(() => {
if (!editor) return ()=>{};
const scriptElement = document.createElement('script');
const channelId = `plugin-post-message-${Math.random()}`;
scriptElement.appendChild(document.createTextNode(`
window.webviewApi = {
postMessage: (contentScriptId, message) => {
const channelId = ${JSON.stringify(channelId)};
const messageId = Math.random();
window.parent.postMessage({
channelId,
messageId,
contentScriptId,
message,
}, '*');
const waitForResponse = async () => {
while (true) {
const messageEvent = await new Promise(resolve => {
window.addEventListener('message', event => {
resolve(event);
}, {once: true});
});
if (messageEvent.source !== window.parent || messageEvent.data.messageId !== messageId) {
continue;
}
const data = messageEvent.data;
return data.response;
}
};
return waitForResponse();
},
};
`));
const editorWindow = editor.getWin();
editorWindow.document.head.appendChild(scriptElement);
const onMessageHandler = async (event: MessageEvent) => {
if (event.source !== editorWindow || event.data.channelId !== channelId) {
return;
}
const contentScriptId = event.data.contentScriptId;
const pluginService = PluginService.instance();
const plugin = pluginService.pluginById(
pluginService.pluginIdByContentScriptId(contentScriptId),
);
const result = await plugin.emitContentScriptMessage(contentScriptId, event.data.message);
editorWindow.postMessage({
messageId: event.data.messageId,
response: result,
}, '*');
};
window.addEventListener('message', onMessageHandler);
return () => {
window.removeEventListener('message', onMessageHandler);
scriptElement.remove();
};
}, [editor]);
};
export default useWebViewApi;