From ca653d3e88438e5868984ad5970380b330128ac5 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Fri, 6 Jun 2025 02:32:35 -0700 Subject: [PATCH] Desktop: Rich text editor: Add a right-click "Open" menu item for external links (#12391) --- .../NoteBody/TinyMCE/utils/useContextMenu.ts | 7 ++++--- .../gui/NoteEditor/utils/contextMenu.ts | 14 ++++++++++++-- .../gui/NoteEditor/utils/contextMenuUtils.ts | 1 + .../gui/NoteEditor/utils/useMessageHandler.ts | 1 + packages/app-desktop/gui/PdfViewer.tsx | 1 + 5 files changed, 19 insertions(+), 5 deletions(-) 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 fda86121b0..e905a0f919 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.ts @@ -39,7 +39,7 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Dispatc const makeMainMenuItems = (element: Element) => { let itemType: ContextMenuItemType = ContextMenuItemType.None; let resourceId = ''; - let linkToCopy = null; + let linkUrl = null; const pathToId = (path: string) => { const id = Resource.pathToId(path); @@ -52,7 +52,7 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Dispatc } else if (element.nodeName === 'A') { resourceId = pathToId((element as HTMLAnchorElement).href); itemType = resourceId ? ContextMenuItemType.Resource : ContextMenuItemType.Link; - linkToCopy = element.getAttribute('href') || ''; + linkUrl = element.getAttribute('href') || ''; } else { itemType = ContextMenuItemType.Text; } @@ -62,7 +62,8 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Dispatc resourceId, filename: null, mime: null, - linkToCopy, + linkToCopy: linkUrl, + linkToOpen: linkUrl, textToCopy: null, htmlToCopy: editor.selection ? editor.selection.getContent() : '', insertContent: (content: string) => { diff --git a/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts b/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts index 195f5bddfe..f1fe8559ff 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts @@ -15,6 +15,7 @@ import Setting from '@joplin/lib/models/Setting'; import ItemChange from '@joplin/lib/models/ItemChange'; import shim from '@joplin/lib/shim'; import { openFileWithExternalEditor } from '@joplin/lib/services/ExternalEditWatcher/utils'; +import CommandService from '@joplin/lib/services/CommandService'; const fs = require('fs-extra'); const { writeFile } = require('fs-extra'); const { clipboard } = require('electron'); @@ -84,9 +85,18 @@ export function menuItems(dispatch: Function): ContextMenuItems { open: { label: _('Open...'), onAction: async (options: ContextMenuOptions) => { - await openItemById(options.resourceId, dispatch); + if (options.resourceId) { + await openItemById(options.resourceId, dispatch); + } else if (options.linkToOpen) { + await CommandService.instance().execute('openItem', options.linkToOpen); + } else { + await shim.showErrorDialog('No link found'); + } }, - isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => !options.textToCopy && (itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource), + isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => ( + (!options.textToCopy && (itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource)) + || (!!options.linkToOpen && itemType === ContextMenuItemType.Link) + ), }, saveAs: { label: _('Save as...'), diff --git a/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts b/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts index 94f3c0b385..b3af42a9a9 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts @@ -17,6 +17,7 @@ export interface ContextMenuOptions { resourceId: string; mime: string; filename: string; + linkToOpen: string; linkToCopy: string; textToCopy: string; htmlToCopy: string; diff --git a/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts b/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts index 5c15325bba..f4239cc492 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts @@ -52,6 +52,7 @@ export default function useMessageHandler( resourceId: arg0.resourceId, filename: arg0.filename, mime: arg0.mime, + linkToOpen: null, textToCopy: arg0.textToCopy, linkToCopy: arg0.linkToCopy || null, htmlToCopy: '', diff --git a/packages/app-desktop/gui/PdfViewer.tsx b/packages/app-desktop/gui/PdfViewer.tsx index 806615ea8b..79c08b9d0b 100644 --- a/packages/app-desktop/gui/PdfViewer.tsx +++ b/packages/app-desktop/gui/PdfViewer.tsx @@ -62,6 +62,7 @@ export default function PdfViewer(props: Props) { mime: 'text/plain', textToCopy: text, linkToCopy: null, + linkToOpen: null, htmlToCopy: '', insertContent: () => { console.warn('insertContent() not implemented'); }, fireEditorEvent: () => { console.warn('fireEditorEvent() not implemented'); },