diff --git a/src/public/app/menus/tree_context_menu.js b/src/public/app/menus/tree_context_menu.js index c0daf7f4f..38c245251 100644 --- a/src/public/app/menus/tree_context_menu.js +++ b/src/public/app/menus/tree_context_menu.js @@ -69,7 +69,8 @@ export default class TreeContextMenu { { title: 'Collapse subtree ', command: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes }, { title: 'Sort by ... ', command: "sortChildNotes", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch }, { title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes }, - { title: 'Convert to attachment', command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted } + { title: 'Convert to attachment', command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted }, + { title: 'Copy note path to clipboard', command: "copyNotePathToClipboard", uiIcon: "bx bx-empty", enabled: true } ] }, { title: "----" }, { title: "Protect subtree", command: "protectSubtree", uiIcon: "bx bx-check-shield", enabled: noSelectedNotes }, @@ -153,6 +154,9 @@ export default class TreeContextMenu { toastService.showMessage(`${converted} notes have been converted to attachments.`); } + else if (command === 'copyNotePathToClipboard') { + navigator.clipboard.writeText('#' + notePath); + } else { this.treeWidget.triggerCommand(command, { node: this.node, diff --git a/src/public/app/services/link.js b/src/public/app/services/link.js index c859b84cf..ee991b202 100644 --- a/src/public/app/services/link.js +++ b/src/public/app/services/link.js @@ -194,6 +194,10 @@ function goToLink(evt) { const $link = $(evt.target).closest("a,.block-link"); const hrefLink = $link.attr('href') || $link.attr('data-href'); + return goToLinkExt(evt, hrefLink, $link); +} + +function goToLinkExt(evt, hrefLink, $link) { if (hrefLink?.startsWith("data:")) { return true; } @@ -201,7 +205,7 @@ function goToLink(evt) { evt.preventDefault(); evt.stopPropagation(); - const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink); + const {notePath, viewScope} = parseNavigationStateFromUrl(hrefLink); const ctrlKey = utils.isCtrlKey(evt); const isLeftClick = evt.which === 1; @@ -213,25 +217,23 @@ function goToLink(evt) { if (notePath) { if (openInNewTab) { - appContext.tabManager.openTabWithNoteWithHoisting(notePath, { viewScope }); - } - else if (isLeftClick) { + appContext.tabManager.openTabWithNoteWithHoisting(notePath, {viewScope}); + } else if (isLeftClick) { const ntxId = $(evt.target).closest("[data-ntx-id]").attr("data-ntx-id"); const noteContext = ntxId ? appContext.tabManager.getNoteContextById(ntxId) : appContext.tabManager.getActiveContext(); - noteContext.setNote(notePath, { viewScope }).then(() => { + noteContext.setNote(notePath, {viewScope}).then(() => { if (noteContext !== appContext.tabManager.getActiveContext()) { appContext.tabManager.activateNoteContext(noteContext.ntxId); } }); } - } - else if (hrefLink) { - const withinEditLink = $link.hasClass("ck-link-actions__preview"); - const outsideOfCKEditor = $link.closest("[contenteditable]").length === 0; + } else if (hrefLink) { + const withinEditLink = $link?.hasClass("ck-link-actions__preview"); + const outsideOfCKEditor = !$link || $link.closest("[contenteditable]").length === 0; if (openInNewTab || (withinEditLink && (leftClick || middleClick)) @@ -239,8 +241,7 @@ function goToLink(evt) { ) { if (hrefLink.toLowerCase().startsWith('http') || hrefLink.startsWith("api/")) { window.open(hrefLink, '_blank'); - } - else if (hrefLink.toLowerCase().startsWith('file:') && utils.isElectron()) { + } else if (hrefLink.toLowerCase().startsWith('file:') && utils.isElectron()) { const electron = utils.dynamicRequire('electron'); electron.shell.openPath(hrefLink); @@ -364,6 +365,7 @@ export default { getNotePathFromUrl, createLink, goToLink, + goToLinkExt, loadReferenceLinkTitle, getReferenceLinkTitle, getReferenceLinkTitleSync, diff --git a/src/public/app/widgets/type_widgets/canvas.js b/src/public/app/widgets/type_widgets/canvas.js index 5529c4713..c01159e50 100644 --- a/src/public/app/widgets/type_widgets/canvas.js +++ b/src/public/app/widgets/type_widgets/canvas.js @@ -1,6 +1,7 @@ import libraryLoader from "../../services/library_loader.js"; import TypeWidget from "./type_widget.js"; import utils from '../../services/utils.js'; +import linkService from '../../services/link.js'; import debounce from "../../services/debounce.js"; const {sleep} = utils; @@ -404,20 +405,17 @@ export default class ExcalidrawTypeWidget extends TypeWidget { }, [excalidrawWrapperRef]); const onLinkOpen = React.useCallback((element, event) => { - const link = element.link; - const { nativeEvent } = event.detail; - const isNewTab = nativeEvent.ctrlKey || nativeEvent.metaKey; - const isNewWindow = nativeEvent.shiftKey; - const isInternalLink = link.startsWith("/") || link.includes(window.location.origin); + let link = element.link; - if (isInternalLink && !isNewTab && !isNewWindow) { - // signal that we're handling the redirect ourselves - event.preventDefault(); - // do a custom redirect, such as passing to react-router - // ... - } else { - // open in the same tab + if (link.startsWith("root/")) { + link = "#" + link; } + + const { nativeEvent } = event.detail; + + event.preventDefault(); + + return linkService.goToLinkExt(nativeEvent, link, null); }, []); return React.createElement( @@ -450,7 +448,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { isCollaborating: false, detectScroll: false, handleKeyboardGlobally: false, - autoFocus: true, + autoFocus: false, onLinkOpen, }) )