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,
})
)