From d3a6079d9b0547456b38b301034e559d5eb8cf90 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Sun, 4 May 2025 17:02:57 +0800 Subject: [PATCH 1/6] Open in a new tab and activate notes when ctrl+click in tree --- apps/client/src/widgets/note_tree.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index cbc3ec055..6f5f09585 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -230,7 +230,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const notePath = treeService.getNotePath(node); if (notePath) { - appContext.tabManager.openTabWithNoteWithHoisting(notePath); + appContext.tabManager.openTabWithNoteWithHoisting(notePath, { + activate: e.shiftKey ? true : false + }); } e.stopPropagation(); @@ -343,11 +345,12 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { }, scrollParent: this.$tree, minExpandLevel: 2, // root can't be collapsed - click: (event, data): boolean => { + click: (event: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent, data): boolean => { this.activityDetected(); const targetType = data.targetType; const node = data.node; + const ctrlKey = utils.isCtrlKey(event); if (node.isSelected() && targetType === "icon") { this.triggerCommand("openBulkActionsDialog", { @@ -356,7 +359,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { return false; } else if (targetType === "title" || targetType === "icon") { - if (event.shiftKey) { + if (event.shiftKey && !ctrlKey) { const activeNode = this.getActiveNode(); if (activeNode.getParent() !== node.getParent()) { @@ -381,9 +384,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } node.setFocus(true); - } else if ((!utils.isMac() && event.ctrlKey) || (utils.isMac() && event.metaKey)) { + } else if (ctrlKey) { const notePath = treeService.getNotePath(node); - appContext.tabManager.openTabWithNoteWithHoisting(notePath); + appContext.tabManager.openTabWithNoteWithHoisting(notePath, { + activate: event.shiftKey ? true : false + }); } else if (event.altKey) { node.setSelected(!node.isSelected()); node.setFocus(true); From 9765f2f6d7ebb7a31287d2de992489b9696c320d Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Sun, 4 May 2025 17:18:02 +0800 Subject: [PATCH 2/6] Use Ctrl + Shift + Click on the launcher pane to open and activate in a new tab. --- apps/client/src/components/tab_manager.ts | 10 +++++++++- .../src/widgets/buttons/launcher/note_launcher.ts | 3 ++- .../src/widgets/buttons/open_note_button_widget.ts | 8 +++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/client/src/components/tab_manager.ts b/apps/client/src/components/tab_manager.ts index 937bd8982..cf2876a5a 100644 --- a/apps/client/src/components/tab_manager.ts +++ b/apps/client/src/components/tab_manager.ts @@ -277,10 +277,18 @@ export default class TabManager extends Component { return noteContext; } - async openInNewTab(targetNoteId: string, hoistedNoteId: string | null = null) { + async openInNewTab(targetNoteId: string, hoistedNoteId: string | null = null, activate: boolean = false) { const noteContext = await this.openEmptyTab(null, hoistedNoteId || this.getActiveContext()?.hoistedNoteId); await noteContext.setNote(targetNoteId); + + if (activate && noteContext.notePath) { + this.activateNoteContext(noteContext.ntxId, false); + await this.triggerEvent("noteSwitchedAndActivated", { + noteContext, + notePath: noteContext.notePath + }); + } } async openInSameTab(targetNoteId: string, hoistedNoteId: string | null = null) { diff --git a/apps/client/src/widgets/buttons/launcher/note_launcher.ts b/apps/client/src/widgets/buttons/launcher/note_launcher.ts index 7622df401..00ba956a1 100644 --- a/apps/client/src/widgets/buttons/launcher/note_launcher.ts +++ b/apps/client/src/widgets/buttons/launcher/note_launcher.ts @@ -53,11 +53,12 @@ export default class NoteLauncher extends AbstractLauncher { await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteId); } else { const ctrlKey = utils.isCtrlKey(evt); + const activate = evt.shiftKey ? true : false; if ((evt.which === 1 && ctrlKey) || evt.which === 2) { // TODO: Fix once tabManager is ported. //@ts-ignore - await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteId); + await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteId, activate); } else { // TODO: Fix once tabManager is ported. //@ts-ignore diff --git a/apps/client/src/widgets/buttons/open_note_button_widget.ts b/apps/client/src/widgets/buttons/open_note_button_widget.ts index 950ded0a6..c0a4c6334 100644 --- a/apps/client/src/widgets/buttons/open_note_button_widget.ts +++ b/apps/client/src/widgets/buttons/open_note_button_widget.ts @@ -28,15 +28,21 @@ export default class OpenNoteButtonWidget extends OnClickButtonWidget { if (evt.which === 3) { return; } + const hoistedNoteId = this.getHoistedNoteId(); const ctrlKey = utils.isCtrlKey(evt); if ((evt.which === 1 && ctrlKey) || evt.which === 2) { - await appContext.tabManager.openInNewTab(this.noteToOpen.noteId); + const activate = evt.shiftKey ? true : false; + await appContext.tabManager.openInNewTab(this.noteToOpen.noteId, hoistedNoteId, activate); } else { await appContext.tabManager.openInSameTab(this.noteToOpen.noteId); } } + getHoistedNoteId() { + return this.noteToOpen.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId; + } + initialRenderCompleteEvent() { // we trigger refresh above } From 5944d0acf4f4d501eb27bacc670e5d0fa032b8ad Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Sun, 4 May 2025 17:24:15 +0800 Subject: [PATCH 3/6] Open an image in a new tab using ctrl+shift+click --- .../src/widgets/type_widgets/abstract_text_type_widget.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/abstract_text_type_widget.ts b/apps/client/src/widgets/type_widgets/abstract_text_type_widget.ts index 2ff3e02bb..71bd2a57c 100644 --- a/apps/client/src/widgets/type_widgets/abstract_text_type_widget.ts +++ b/apps/client/src/widgets/type_widgets/abstract_text_type_widget.ts @@ -20,9 +20,10 @@ export default class AbstractTextTypeWidget extends TypeWidget { const isLeftClick = e.which === 1; const isMiddleClick = e.which === 2; const ctrlKey = utils.isCtrlKey(e); + const activate = (isLeftClick && ctrlKey && e.shiftKey) || (isMiddleClick && e.shiftKey); if ((isLeftClick && ctrlKey) || isMiddleClick) { - this.openImageInNewTab($(e.target)); + this.openImageInNewTab($(e.target), activate); } else if (isLeftClick && singleClickOpens) { this.openImageInCurrentTab($(e.target)); } @@ -39,11 +40,11 @@ export default class AbstractTextTypeWidget extends TypeWidget { } } - async openImageInNewTab($img: JQuery) { + async openImageInNewTab($img: JQuery, activate: boolean = false) { const parsedImage = await this.parseFromImage($img); if (parsedImage) { - appContext.tabManager.openTabWithNoteWithHoisting(parsedImage.noteId, { viewScope: parsedImage.viewScope }); + appContext.tabManager.openTabWithNoteWithHoisting(parsedImage.noteId, { activate, viewScope: parsedImage.viewScope }); } else { window.open($img.prop("src"), "_blank"); } From 9b1ddd970ca34884079f1a3b4258eee35bb168f2 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Sun, 4 May 2025 17:30:13 +0800 Subject: [PATCH 4/6] Add help for CTRL+SHIFT+CLICK --- apps/client/src/translations/en/translation.json | 3 ++- apps/client/src/widgets/dialogs/help.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 6ba46d7e2..a5526659b 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -126,7 +126,8 @@ "collapseWholeTree": "collapse whole note tree", "collapseSubTree": "collapse sub-tree", "tabShortcuts": "Tab shortcuts", - "newTabNoteLink": "CTRL+click - (or middle mouse click) on note link opens note in a new tab", + "newTabNoteLink": "Ctrl+click - (or middle mouse click) on note link opens note in a new tab", + "newTabWithActivationNoteLink": "Ctrl+Shift+click - (or Shift+middle mouse click) on note link opens and activates the note in a new tab", "onlyInDesktop": "Only in desktop (Electron build)", "openEmptyTab": "open empty tab", "closeActiveTab": "close active tab", diff --git a/apps/client/src/widgets/dialogs/help.ts b/apps/client/src/widgets/dialogs/help.ts index 0d2e77d7b..747d01b02 100644 --- a/apps/client/src/widgets/dialogs/help.ts +++ b/apps/client/src/widgets/dialogs/help.ts @@ -38,6 +38,7 @@ const TPL = /*html*/`

  • ${t("help.newTabNoteLink")}
  • +
  • ${t("help.newTabWithActivationNoteLink")}
${t("help.onlyInDesktop")}:
    From 52d6d05159dbabe1966d6b9486ce8836f744e410 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Tue, 6 May 2025 21:27:58 +0800 Subject: [PATCH 5/6] Add e2e: open and activate notes from the launch pane and tree --- .../src/layout/open_note_and_activate.spec.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 apps/server-e2e/src/layout/open_note_and_activate.spec.ts diff --git a/apps/server-e2e/src/layout/open_note_and_activate.spec.ts b/apps/server-e2e/src/layout/open_note_and_activate.spec.ts new file mode 100644 index 000000000..b6e4d47bf --- /dev/null +++ b/apps/server-e2e/src/layout/open_note_and_activate.spec.ts @@ -0,0 +1,49 @@ +import { test, expect } from "@playwright/test"; +import App from "../support/app"; + +const NOTE_TITLE = "Trilium Integration Test DB"; + +test("Opens and activate a note from launcher Bar", async ({ page, context }) => { + const app = new App(page, context); + await app.goto(); + await app.closeAllTabs(); + await app.goToNoteInNewTab(NOTE_TITLE); + + const calendarButton = app.launcherBar.locator(".launcher-button.bx-calendar-star.visible"); + await expect(calendarButton).toBeVisible(); + + await page.keyboard.down('Control'); + await page.keyboard.down('Shift'); + + await calendarButton.click(); + + await page.keyboard.up('Control'); + await page.keyboard.up('Shift'); + + const tabs = app.tabBar.locator(".note-tab"); + await expect(tabs).toHaveCount(2); + + const secondTab = tabs.nth(1); + await expect(secondTab).toHaveAttribute('active', ''); +}); + +test("Opens and activate a note from note tree", async ({ page, context }) => { + const app = new App(page, context); + await app.goto(); + await app.closeAllTabs(); + await app.goToNoteInNewTab(NOTE_TITLE); + + await page.keyboard.down('Control'); + await page.keyboard.down('Shift'); + + await app.noteTreeActiveNote.click(); + + await page.keyboard.up('Control'); + await page.keyboard.up('Shift'); + + const tabs = app.tabBar.locator(".note-tab"); + await expect(tabs).toHaveCount(2); + + const secondTab = tabs.nth(1); + await expect(secondTab).toHaveAttribute('active', ''); +}); \ No newline at end of file From f26a7a90a27309ef4ec98af070e7941d8a9d27b0 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Sun, 11 May 2025 10:53:45 +0800 Subject: [PATCH 6/6] test(e2e): open and activate notes from the launch pane and tree --- .../src/layout/open_note_and_activate.spec.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/server-e2e/src/layout/open_note_and_activate.spec.ts b/apps/server-e2e/src/layout/open_note_and_activate.spec.ts index b6e4d47bf..bb3770245 100644 --- a/apps/server-e2e/src/layout/open_note_and_activate.spec.ts +++ b/apps/server-e2e/src/layout/open_note_and_activate.spec.ts @@ -7,15 +7,14 @@ test("Opens and activate a note from launcher Bar", async ({ page, context }) => const app = new App(page, context); await app.goto(); await app.closeAllTabs(); - await app.goToNoteInNewTab(NOTE_TITLE); - const calendarButton = app.launcherBar.locator(".launcher-button.bx-calendar-star.visible"); - await expect(calendarButton).toBeVisible(); + const mapButton = app.launcherBar.locator(".launcher-button.bx-search.visible"); + await expect(mapButton).toBeVisible(); await page.keyboard.down('Control'); await page.keyboard.down('Shift'); - await calendarButton.click(); + await mapButton.click(); await page.keyboard.up('Control'); await page.keyboard.up('Shift'); @@ -31,12 +30,11 @@ test("Opens and activate a note from note tree", async ({ page, context }) => { const app = new App(page, context); await app.goto(); await app.closeAllTabs(); - await app.goToNoteInNewTab(NOTE_TITLE); await page.keyboard.down('Control'); await page.keyboard.down('Shift'); - await app.noteTreeActiveNote.click(); + await app.clickNoteOnNoteTreeByTitle(NOTE_TITLE); await page.keyboard.up('Control'); await page.keyboard.up('Shift');