mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 21:11:30 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/develop' into feature/codemirror6
This commit is contained in:
		
						commit
						00a9908907
					
				| @ -60,7 +60,7 @@ | |||||||
|     "@types/react": "19.1.3", |     "@types/react": "19.1.3", | ||||||
|     "@types/react-dom": "19.1.3", |     "@types/react-dom": "19.1.3", | ||||||
|     "copy-webpack-plugin": "13.0.0", |     "copy-webpack-plugin": "13.0.0", | ||||||
|     "happy-dom": "17.4.6", |     "happy-dom": "17.4.7", | ||||||
|     "script-loader": "0.7.2" |     "script-loader": "0.7.2" | ||||||
|   }, |   }, | ||||||
|   "nx": { |   "nx": { | ||||||
|  | |||||||
| @ -277,10 +277,18 @@ export default class TabManager extends Component { | |||||||
|         return noteContext; |         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); |         const noteContext = await this.openEmptyTab(null, hoistedNoteId || this.getActiveContext()?.hoistedNoteId); | ||||||
| 
 | 
 | ||||||
|         await noteContext.setNote(targetNoteId); |         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) { |     async openInSameTab(targetNoteId: string, hoistedNoteId: string | null = null) { | ||||||
|  | |||||||
| @ -126,7 +126,8 @@ | |||||||
|     "collapseWholeTree": "collapse whole note tree", |     "collapseWholeTree": "collapse whole note tree", | ||||||
|     "collapseSubTree": "collapse sub-tree", |     "collapseSubTree": "collapse sub-tree", | ||||||
|     "tabShortcuts": "Tab shortcuts", |     "tabShortcuts": "Tab shortcuts", | ||||||
|     "newTabNoteLink": "<kbd>CTRL+click</kbd> - (or middle mouse click) on note link opens note in a new tab", |     "newTabNoteLink": "<kbd>Ctrl+click</kbd> - (or <kbd>middle mouse click</kbd>) on note link opens note in a new tab", | ||||||
|  |     "newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+click</kbd> - (or <kbd>Shift+middle mouse click</kbd>) on note link opens and activates the note in a new tab", | ||||||
|     "onlyInDesktop": "Only in desktop (Electron build)", |     "onlyInDesktop": "Only in desktop (Electron build)", | ||||||
|     "openEmptyTab": "open empty tab", |     "openEmptyTab": "open empty tab", | ||||||
|     "closeActiveTab": "close active tab", |     "closeActiveTab": "close active tab", | ||||||
|  | |||||||
| @ -53,11 +53,12 @@ export default class NoteLauncher extends AbstractLauncher { | |||||||
|             await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteId); |             await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteId); | ||||||
|         } else { |         } else { | ||||||
|             const ctrlKey = utils.isCtrlKey(evt); |             const ctrlKey = utils.isCtrlKey(evt); | ||||||
|  |             const activate = evt.shiftKey ? true : false; | ||||||
| 
 | 
 | ||||||
|             if ((evt.which === 1 && ctrlKey) || evt.which === 2) { |             if ((evt.which === 1 && ctrlKey) || evt.which === 2) { | ||||||
|                 // TODO: Fix once tabManager is ported.
 |                 // TODO: Fix once tabManager is ported.
 | ||||||
|                 //@ts-ignore
 |                 //@ts-ignore
 | ||||||
|                 await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteId); |                 await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteId, activate); | ||||||
|             } else { |             } else { | ||||||
|                 // TODO: Fix once tabManager is ported.
 |                 // TODO: Fix once tabManager is ported.
 | ||||||
|                 //@ts-ignore
 |                 //@ts-ignore
 | ||||||
|  | |||||||
| @ -28,15 +28,21 @@ export default class OpenNoteButtonWidget extends OnClickButtonWidget { | |||||||
|         if (evt.which === 3) { |         if (evt.which === 3) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |         const hoistedNoteId = this.getHoistedNoteId(); | ||||||
|         const ctrlKey = utils.isCtrlKey(evt); |         const ctrlKey = utils.isCtrlKey(evt); | ||||||
| 
 | 
 | ||||||
|         if ((evt.which === 1 && ctrlKey) || evt.which === 2) { |         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 { |         } else { | ||||||
|             await appContext.tabManager.openInSameTab(this.noteToOpen.noteId); |             await appContext.tabManager.openInSameTab(this.noteToOpen.noteId); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     getHoistedNoteId() { | ||||||
|  |         return this.noteToOpen.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     initialRenderCompleteEvent() { |     initialRenderCompleteEvent() { | ||||||
|         // we trigger refresh above
 |         // we trigger refresh above
 | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -38,6 +38,7 @@ const TPL = /*html*/` | |||||||
|                             <p class="card-text"> |                             <p class="card-text"> | ||||||
|                             <ul> |                             <ul> | ||||||
|                                 <li>${t("help.newTabNoteLink")}</li> |                                 <li>${t("help.newTabNoteLink")}</li> | ||||||
|  |                                 <li>${t("help.newTabWithActivationNoteLink")}</li> | ||||||
|                             </ul> |                             </ul> | ||||||
|                             <h6>${t("help.onlyInDesktop")}:</h6> |                             <h6>${t("help.onlyInDesktop")}:</h6> | ||||||
|                             <ul> |                             <ul> | ||||||
|  | |||||||
| @ -230,7 +230,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                 const notePath = treeService.getNotePath(node); |                 const notePath = treeService.getNotePath(node); | ||||||
| 
 | 
 | ||||||
|                 if (notePath) { |                 if (notePath) { | ||||||
|                     appContext.tabManager.openTabWithNoteWithHoisting(notePath); |                     appContext.tabManager.openTabWithNoteWithHoisting(notePath, { | ||||||
|  |                         activate: e.shiftKey ? true : false | ||||||
|  |                     }); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 e.stopPropagation(); |                 e.stopPropagation(); | ||||||
| @ -343,11 +345,12 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|             }, |             }, | ||||||
|             scrollParent: this.$tree, |             scrollParent: this.$tree, | ||||||
|             minExpandLevel: 2, // root can't be collapsed
 |             minExpandLevel: 2, // root can't be collapsed
 | ||||||
|             click: (event, data): boolean => { |             click: (event: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent<HTMLCanvasElement>, data): boolean => { | ||||||
|                 this.activityDetected(); |                 this.activityDetected(); | ||||||
| 
 | 
 | ||||||
|                 const targetType = data.targetType; |                 const targetType = data.targetType; | ||||||
|                 const node = data.node; |                 const node = data.node; | ||||||
|  |                 const ctrlKey = utils.isCtrlKey(event); | ||||||
| 
 | 
 | ||||||
|                 if (node.isSelected() && targetType === "icon") { |                 if (node.isSelected() && targetType === "icon") { | ||||||
|                     this.triggerCommand("openBulkActionsDialog", { |                     this.triggerCommand("openBulkActionsDialog", { | ||||||
| @ -356,7 +359,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
| 
 | 
 | ||||||
|                     return false; |                     return false; | ||||||
|                 } else if (targetType === "title" || targetType === "icon") { |                 } else if (targetType === "title" || targetType === "icon") { | ||||||
|                     if (event.shiftKey) { |                     if (event.shiftKey && !ctrlKey) { | ||||||
|                         const activeNode = this.getActiveNode(); |                         const activeNode = this.getActiveNode(); | ||||||
| 
 | 
 | ||||||
|                         if (activeNode.getParent() !== node.getParent()) { |                         if (activeNode.getParent() !== node.getParent()) { | ||||||
| @ -381,9 +384,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         node.setFocus(true); |                         node.setFocus(true); | ||||||
|                     } else if ((!utils.isMac() && event.ctrlKey) || (utils.isMac() && event.metaKey)) { |                     } else if (ctrlKey) { | ||||||
|                         const notePath = treeService.getNotePath(node); |                         const notePath = treeService.getNotePath(node); | ||||||
|                         appContext.tabManager.openTabWithNoteWithHoisting(notePath); |                         appContext.tabManager.openTabWithNoteWithHoisting(notePath, { | ||||||
|  |                             activate: event.shiftKey ? true : false | ||||||
|  |                         }); | ||||||
|                     } else if (event.altKey) { |                     } else if (event.altKey) { | ||||||
|                         node.setSelected(!node.isSelected()); |                         node.setSelected(!node.isSelected()); | ||||||
|                         node.setFocus(true); |                         node.setFocus(true); | ||||||
|  | |||||||
| @ -20,9 +20,10 @@ export default class AbstractTextTypeWidget extends TypeWidget { | |||||||
|             const isLeftClick = e.which === 1; |             const isLeftClick = e.which === 1; | ||||||
|             const isMiddleClick = e.which === 2; |             const isMiddleClick = e.which === 2; | ||||||
|             const ctrlKey = utils.isCtrlKey(e); |             const ctrlKey = utils.isCtrlKey(e); | ||||||
|  |             const activate = (isLeftClick && ctrlKey && e.shiftKey) || (isMiddleClick && e.shiftKey); | ||||||
| 
 | 
 | ||||||
|             if ((isLeftClick && ctrlKey) || isMiddleClick) { |             if ((isLeftClick && ctrlKey) || isMiddleClick) { | ||||||
|                 this.openImageInNewTab($(e.target)); |                 this.openImageInNewTab($(e.target), activate); | ||||||
|             } else if (isLeftClick && singleClickOpens) { |             } else if (isLeftClick && singleClickOpens) { | ||||||
|                 this.openImageInCurrentTab($(e.target)); |                 this.openImageInCurrentTab($(e.target)); | ||||||
|             } |             } | ||||||
| @ -39,11 +40,11 @@ export default class AbstractTextTypeWidget extends TypeWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async openImageInNewTab($img: JQuery<HTMLElement>) { |     async openImageInNewTab($img: JQuery<HTMLElement>, activate: boolean = false) { | ||||||
|         const parsedImage = await this.parseFromImage($img); |         const parsedImage = await this.parseFromImage($img); | ||||||
| 
 | 
 | ||||||
|         if (parsedImage) { |         if (parsedImage) { | ||||||
|             appContext.tabManager.openTabWithNoteWithHoisting(parsedImage.noteId, { viewScope: parsedImage.viewScope }); |             appContext.tabManager.openTabWithNoteWithHoisting(parsedImage.noteId, { activate, viewScope: parsedImage.viewScope }); | ||||||
|         } else { |         } else { | ||||||
|             window.open($img.prop("src"), "_blank"); |             window.open($img.prop("src"), "_blank"); | ||||||
|         } |         } | ||||||
|  | |||||||
							
								
								
									
										47
									
								
								apps/server-e2e/src/layout/open_note_and_activate.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								apps/server-e2e/src/layout/open_note_and_activate.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | 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(); | ||||||
|  | 
 | ||||||
|  |     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 mapButton.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 page.keyboard.down('Control');   | ||||||
|  |     await page.keyboard.down('Shift');  | ||||||
|  | 
 | ||||||
|  |     await app.clickNoteOnNoteTreeByTitle(NOTE_TITLE); | ||||||
|  | 
 | ||||||
|  |     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', '');  | ||||||
|  | }); | ||||||
| @ -188,6 +188,12 @@ second line 2</code></pre><ul><li>Hello</li><li>world</li></ul><ol><li>Hello</li | |||||||
|         expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected); |         expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it("converts multiple inline math expressions into Mathtex format", () => { | ||||||
|  |         const input = `Energy: $e=mc^{2}$, Force: $F=ma$.`; | ||||||
|  |         const expected = /*html*/`<p>Energy: <span class="math-tex">\\(e=mc^{2}\\)</span>, Force: <span class="math-tex">\\(F=ma\\)</span>.</p>`; | ||||||
|  |         expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     it("converts display math expressions into Mathtex format", () => { |     it("converts display math expressions into Mathtex format", () => { | ||||||
|         const input = `$$\sqrt{x^{2}+1}$$`; |         const input = `$$\sqrt{x^{2}+1}$$`; | ||||||
|         const expected = /*html*/`<p><span class="math-tex">\\[\sqrt{x^{2}+1}\\]</span></p>`; |         const expected = /*html*/`<p><span class="math-tex">\\[\sqrt{x^{2}+1}\\]</span></p>`; | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ class CustomMarkdownRenderer extends Renderer { | |||||||
|                 `<span class="math-tex">\\\[$1\\\]</span>`); |                 `<span class="math-tex">\\\[$1\\\]</span>`); | ||||||
| 
 | 
 | ||||||
|             // Inline math
 |             // Inline math
 | ||||||
|             text = text.replaceAll(/(?<!\\)\$(.+)\$/g, |             text = text.replaceAll(/(?<!\\)\$(.+?)\$/g, | ||||||
|                 `<span class="math-tex">\\\($1\\\)</span>`); |                 `<span class="math-tex">\\\($1\\\)</span>`); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ | |||||||
| ## 🐞 Bugfixes | ## 🐞 Bugfixes | ||||||
| 
 | 
 | ||||||
| *   [Inconsistent Find and Replace Behavior in Large Code Notes](https://github.com/TriliumNext/Notes/issues/1826) by @SiriusXT | *   [Inconsistent Find and Replace Behavior in Large Code Notes](https://github.com/TriliumNext/Notes/issues/1826) by @SiriusXT | ||||||
|  | *   [Incorrect import of multiple inline math](https://github.com/TriliumNext/Notes/pull/1906) by @SiriusXT | ||||||
| 
 | 
 | ||||||
| ## ✨ Improvements | ## ✨ Improvements | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ | |||||||
|     "eslint": "^9.0.0", |     "eslint": "^9.0.0", | ||||||
|     "eslint-config-ckeditor5": ">=9.1.0", |     "eslint-config-ckeditor5": ">=9.1.0", | ||||||
|     "http-server": "^14.1.0", |     "http-server": "^14.1.0", | ||||||
|     "lint-staged": "^15.0.0", |     "lint-staged": "^16.0.0", | ||||||
|     "stylelint": "^16.0.0", |     "stylelint": "^16.0.0", | ||||||
|     "stylelint-config-ckeditor5": ">=9.1.0", |     "stylelint-config-ckeditor5": ">=9.1.0", | ||||||
|     "ts-node": "^10.9.1", |     "ts-node": "^10.9.1", | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ | |||||||
|     "eslint": "^9.0.0", |     "eslint": "^9.0.0", | ||||||
|     "eslint-config-ckeditor5": ">=9.1.0", |     "eslint-config-ckeditor5": ">=9.1.0", | ||||||
|     "http-server": "^14.1.0", |     "http-server": "^14.1.0", | ||||||
|     "lint-staged": "^15.0.0", |     "lint-staged": "^16.0.0", | ||||||
|     "stylelint": "^16.0.0", |     "stylelint": "^16.0.0", | ||||||
|     "stylelint-config-ckeditor5": ">=9.1.0", |     "stylelint-config-ckeditor5": ">=9.1.0", | ||||||
|     "ts-node": "^10.9.1", |     "ts-node": "^10.9.1", | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ | |||||||
|     "eslint": "^9.0.0", |     "eslint": "^9.0.0", | ||||||
|     "eslint-config-ckeditor5": ">=9.1.0", |     "eslint-config-ckeditor5": ">=9.1.0", | ||||||
|     "http-server": "^14.1.0", |     "http-server": "^14.1.0", | ||||||
|     "lint-staged": "^15.0.0", |     "lint-staged": "^16.0.0", | ||||||
|     "stylelint": "^16.0.0", |     "stylelint": "^16.0.0", | ||||||
|     "stylelint-config-ckeditor5": ">=9.1.0", |     "stylelint-config-ckeditor5": ">=9.1.0", | ||||||
|     "ts-node": "^10.9.1", |     "ts-node": "^10.9.1", | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ | |||||||
|     "eslint": "^9.0.0", |     "eslint": "^9.0.0", | ||||||
|     "eslint-config-ckeditor5": ">=9.1.0", |     "eslint-config-ckeditor5": ">=9.1.0", | ||||||
|     "http-server": "^14.1.0", |     "http-server": "^14.1.0", | ||||||
|     "lint-staged": "^15.0.0", |     "lint-staged": "^16.0.0", | ||||||
|     "stylelint": "^16.0.0", |     "stylelint": "^16.0.0", | ||||||
|     "stylelint-config-ckeditor5": ">=9.1.0", |     "stylelint-config-ckeditor5": ">=9.1.0", | ||||||
|     "ts-node": "^10.9.1", |     "ts-node": "^10.9.1", | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ | |||||||
|     "eslint": "^9.0.0", |     "eslint": "^9.0.0", | ||||||
|     "eslint-config-ckeditor5": ">=9.1.0", |     "eslint-config-ckeditor5": ">=9.1.0", | ||||||
|     "http-server": "^14.1.0", |     "http-server": "^14.1.0", | ||||||
|     "lint-staged": "^15.0.0", |     "lint-staged": "^16.0.0", | ||||||
|     "stylelint": "^16.0.0", |     "stylelint": "^16.0.0", | ||||||
|     "stylelint-config-ckeditor5": ">=9.1.0", |     "stylelint-config-ckeditor5": ">=9.1.0", | ||||||
|     "ts-node": "^10.9.1", |     "ts-node": "^10.9.1", | ||||||
|  | |||||||
							
								
								
									
										695
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										695
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran