mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 03:29:02 +01:00 
			
		
		
		
	Merge branch 'master' into next61
# Conflicts: # package-lock.json # package.json # src/public/app/components/note_context.js # src/public/app/services/open.js # src/public/app/widgets/buttons/note_actions.js # src/public/app/widgets/find_in_code.js # src/public/app/widgets/type_widgets/canvas.js # src/services/options.js
This commit is contained in:
		
						commit
						9e9fb2979f
					
				
										
											Binary file not shown.
										
									
								
							| @ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.59.4", | ||||
|   "version": "0.60.0-beta", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
| @ -28,7 +28,7 @@ | ||||
|     "test": "npm run test-jasmine && npm run test-es6", | ||||
|     "postinstall": "rimraf ./node_modules/canvas", | ||||
|     "lint": "eslint .  --cache", | ||||
|     "prepare": "husky install" | ||||
|     "prepare": "husky install || echo 'Husky install failed, expected on flatpak build'" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@braintree/sanitize-url": "6.0.2", | ||||
| @ -45,8 +45,8 @@ | ||||
|     "cookie-parser": "1.4.6", | ||||
|     "csurf": "1.11.0", | ||||
|     "dayjs": "1.11.7", | ||||
|     "dayjs-plugin-utc": "^0.1.2", | ||||
|     "debounce": "^1.2.1", | ||||
|     "dayjs-plugin-utc": "0.1.2", | ||||
|     "debounce": "1.2.1", | ||||
|     "ejs": "3.1.9", | ||||
|     "electron-debug": "3.2.0", | ||||
|     "electron-dl": "3.5.0", | ||||
|  | ||||
| @ -57,10 +57,8 @@ export default class RootCommandExecutor extends Component { | ||||
| 
 | ||||
|     openNoteCustomCommand() { | ||||
|         const noteId = appContext.tabManager.getActiveContextNoteId(); | ||||
|         const mime = appContext.tabManager.getActiveContextNoteMime() | ||||
| 
 | ||||
|         if (noteId) { | ||||
|             openService.openNoteCustom(noteId, mime); | ||||
|             openService.openNoteCustom(noteId); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -41,14 +41,19 @@ function downloadAttachment(attachmentId) { | ||||
|     download(url); | ||||
| } | ||||
| 
 | ||||
| async function openNoteCustom(noteId, mime) { | ||||
|     if (utils.isElectron()) { | ||||
| async function openNoteCustom(noteId) { | ||||
|     if (!utils.isElectron() || utils.isMac()) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const resp = await server.post(`notes/${noteId}/save-to-tmp-dir`); | ||||
|       const filePath = resp.tmpFilePath; | ||||
|     let filePath = resp.tmpFilePath; | ||||
|     const {exec} = utils.dynamicRequire('child_process'); | ||||
|     const platform = process.platform; | ||||
| 
 | ||||
|     if (platform === 'linux') { | ||||
|         const terminals = ['gnome-terminal', 'konsole', 'xterm', 'xfce4-terminal', 'mate-terminal', 'rxvt', 'terminator', 'terminology']; | ||||
|         // we don't know which terminal is available, try in succession
 | ||||
|         const terminals = ['x-terminal-emulator', 'gnome-terminal', 'konsole', 'xterm', 'xfce4-terminal', 'mate-terminal', 'rxvt', 'terminator', 'terminology']; | ||||
|         const openFileWithTerminal = (terminal) => { | ||||
|             const command = `${terminal} -e 'mimeopen -d "${filePath}"'`; | ||||
|             console.log(`Open Note custom: ${command} `); | ||||
| @ -57,10 +62,11 @@ async function openNoteCustom(noteId, mime) { | ||||
|                     console.error(`Open Note custom: Failed to open file with ${terminal}: ${error}`); | ||||
|                     searchTerminal(terminals.indexOf(terminal) + 1); | ||||
|                 } else { | ||||
|               console.log(`Open Note custom: File opened with ${terminal}. ${stdout}`); | ||||
|                     console.log(`Open Note custom: File opened with ${terminal}: ${stdout}`); | ||||
|                 } | ||||
|             }); | ||||
|         }; | ||||
| 
 | ||||
|         const searchTerminal = (index) => { | ||||
|             const terminal = terminals[index]; | ||||
|             if (!terminal) { | ||||
| @ -95,7 +101,6 @@ async function openNoteCustom(noteId, mime) { | ||||
|         open(getFileUrl(noteId), {url: true}); | ||||
|     } | ||||
| } | ||||
|   } | ||||
| 
 | ||||
| function downloadNoteRevision(noteId, noteRevisionId) { | ||||
|     const url = getUrlForDownload(`api/revisions/${noteRevisionId}/download`); | ||||
|  | ||||
| @ -40,7 +40,7 @@ const TPL = ` | ||||
|             <kbd data-command="openNoteExternally"></kbd>  | ||||
|             Open note externally | ||||
|         </a> | ||||
|         <a data-trigger-command="openNoteCustom" class="dropdown-item open-note-custom-button"><kbd data-command="openNoteCustom"></kbd> Open note custom (beta)</a> | ||||
|         <a data-trigger-command="openNoteCustom" class="dropdown-item open-note-custom-button"><kbd data-command="openNoteCustom"></kbd> Open note custom</a> | ||||
|         <a class="dropdown-item import-files-button">Import files</a> | ||||
|         <a class="dropdown-item export-note-button">Export note</a> | ||||
|         <a class="dropdown-item delete-note-button">Delete note</a> | ||||
| @ -104,7 +104,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget { | ||||
|         this.$renderNoteButton.toggle(note.type === 'render'); | ||||
| 
 | ||||
|         this.$openNoteExternallyButton.toggle(utils.isElectron()); | ||||
|         this.$openNoteCustomButton.toggle(utils.isElectron()); | ||||
|         this.$openNoteCustomButton.toggle(utils.isElectron() && !utils.isMac()); // no implementation for Mac yet
 | ||||
|     } | ||||
| 
 | ||||
|     async convertNoteIntoAttachmentCommand() { | ||||
|  | ||||
| @ -6,6 +6,7 @@ export default class ScrollingContainer extends Container { | ||||
| 
 | ||||
|         this.class("scrolling-container"); | ||||
|         this.css('overflow', 'auto'); | ||||
|         this.css('scroll-behavior', 'smooth'); | ||||
|         this.css('position', 'relative'); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -146,20 +146,31 @@ export default class FindWidget extends NoteContextAwareWidget { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.handler = await this.getHandler(); | ||||
| 
 | ||||
|         const selectedText = window.getSelection().toString() || ""; | ||||
| 
 | ||||
|         this.$findBox.show(); | ||||
|         this.$input.focus(); | ||||
|         this.handler = await this.getHandler(); | ||||
| 
 | ||||
|         const isAlreadyVisible = this.$findBox.is(":visible"); | ||||
| 
 | ||||
|         if (isAlreadyVisible) { | ||||
|             if (selectedText) { | ||||
|                 this.$input.val(selectedText); | ||||
|             } | ||||
| 
 | ||||
|             if (this.$input.val()) { | ||||
|                 await this.performFind(); | ||||
|             } | ||||
| 
 | ||||
|             this.$input.select(); | ||||
|         } else { | ||||
|             this.$totalFound.text(0); | ||||
|             this.$currentFound.text(0); | ||||
|             const searchTerm = await this.handler.getInitialSearchTerm(); | ||||
|             this.$input.val(searchTerm || ""); | ||||
|             if (searchTerm !== "") { | ||||
|             this.$input.val(selectedText); | ||||
| 
 | ||||
|             if (selectedText) { | ||||
|                 this.$input.select(); | ||||
|                 await this.performFind(); | ||||
|             } | ||||
|  | ||||
| @ -16,23 +16,6 @@ export default class FindInCode { | ||||
|         return this.parent.noteContext.getCodeEditor(); | ||||
|     } | ||||
| 
 | ||||
|     async getInitialSearchTerm() { | ||||
|         const codeEditor = await this.getCodeEditor(); | ||||
| 
 | ||||
|         // highlightSelectionMatches is the overlay that highlights
 | ||||
|         // the words under the cursor. This occludes the search
 | ||||
|         // markers style, save it, disable it. It will be restored when
 | ||||
|         // the focus is back into the note
 | ||||
|         this.oldHighlightSelectionMatches = codeEditor.getOption("highlightSelectionMatches"); | ||||
|         codeEditor.setOption("highlightSelectionMatches", false); | ||||
| 
 | ||||
|         // Fill in the findbox with the current selection if any
 | ||||
|         const selectedText = codeEditor.getSelection() | ||||
|         if (selectedText !== "") { | ||||
|             return selectedText; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async performFind(searchTerm, matchCase, wholeWord) { | ||||
|         let findResult = null; | ||||
|         let totalFound = 0; | ||||
|  | ||||
| @ -16,10 +16,6 @@ export default class FindInHtml { | ||||
|         this.$results = null; | ||||
|     } | ||||
| 
 | ||||
|     async getInitialSearchTerm() { | ||||
|         return ""; // FIXME
 | ||||
|     } | ||||
| 
 | ||||
|     async performFind(searchTerm, matchCase, wholeWord) { | ||||
|         await libraryLoader.requireLibrary(libraryLoader.MARKJS); | ||||
| 
 | ||||
|  | ||||
| @ -8,19 +8,6 @@ export default class FindInText { | ||||
|         return this.parent.noteContext.getTextEditor(); | ||||
|     } | ||||
| 
 | ||||
|     async getInitialSearchTerm() { | ||||
|         const textEditor = await this.getTextEditor(); | ||||
| 
 | ||||
|         const selection = textEditor.model.document.selection; | ||||
|         const range = selection.getFirstRange(); | ||||
| 
 | ||||
|         // FIXME
 | ||||
|         for (const item of range.getItems()) { | ||||
|             // Fill in the findbox with the current selection if any
 | ||||
|             return item.data; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async performFind(searchTerm, matchCase, wholeWord) { | ||||
|         // Do this even if the searchTerm is empty so the markers are cleared and
 | ||||
|         // the counters updated
 | ||||
|  | ||||
| @ -177,7 +177,7 @@ export default class TocWidget extends RightPanelWidget { | ||||
|             const headingElement = $container.find(":header")[headingIndex]; | ||||
| 
 | ||||
|             if (headingElement != null) { | ||||
|                 headingElement.scrollIntoView(); | ||||
|                 headingElement.scrollIntoView({ behavior: "smooth" }); | ||||
|             } | ||||
|         } else { | ||||
|             const textEditor = await this.noteContext.getTextEditor(); | ||||
| @ -193,50 +193,9 @@ export default class TocWidget extends RightPanelWidget { | ||||
|             // navigate (note that the TOC rendering and other TOC
 | ||||
|             // entries' navigation could be wrong too)
 | ||||
|             if (headingNode != null) { | ||||
|                 // Setting the selection alone doesn't scroll to the
 | ||||
|                 // caret, needs to be done explicitly and outside of
 | ||||
|                 // the writer change callback so the scroll is
 | ||||
|                 // guaranteed to happen after the selection is
 | ||||
|                 // updated.
 | ||||
| 
 | ||||
|                 // In addition, scrolling to a caret later in the
 | ||||
|                 // document (ie "forward scrolls"), only scrolls
 | ||||
|                 // barely enough to place the caret at the bottom of
 | ||||
|                 // the screen, which is a usability issue, you would
 | ||||
|                 // like the caret to be placed at the top or center
 | ||||
|                 // of the screen.
 | ||||
| 
 | ||||
|                 // To work around that issue, first scroll to the
 | ||||
|                 // end of the document, then scroll to the desired
 | ||||
|                 // point. This causes all the scrolls to be
 | ||||
|                 // "backward scrolls" no matter the current caret
 | ||||
|                 // position, which places the caret at the top of
 | ||||
|                 // the screen.
 | ||||
| 
 | ||||
|                 // XXX This could be fixed in another way by using
 | ||||
|                 //     the underlying CKEditor5
 | ||||
|                 //     scrollViewportToShowTarget, which allows to
 | ||||
|                 //     provide a larger "viewportOffset", but that
 | ||||
|                 //     has coding complications (requires calling an
 | ||||
|                 //     internal CKEditor utils funcion and passing
 | ||||
|                 //     an HTML element, not a CKEditor node, and
 | ||||
|                 //     CKEditor5 doesn't seem to have a
 | ||||
|                 //     straightforward way to convert a node to an
 | ||||
|                 //     HTML element? (in CKEditor4 this was done
 | ||||
|                 //     with $(node.$) )
 | ||||
| 
 | ||||
|                 // Scroll to the end of the note to guarantee the
 | ||||
|                 // next scroll is a backwards scroll that places the
 | ||||
|                 // caret at the top of the screen
 | ||||
|                 model.change(writer => { | ||||
|                     writer.setSelection(root.getChild(root.childCount - 1), 0); | ||||
|                 $(textEditor.editing.view.domRoots.values().next().value).find(':header')[headingIndex].scrollIntoView({ | ||||
|                     behavior: 'smooth' | ||||
|                 }); | ||||
|                 textEditor.editing.view.scrollToTheSelection(); | ||||
|                 // Backwards scroll to the heading
 | ||||
|                 model.change(writer => { | ||||
|                     writer.setSelection(headingNode, 0); | ||||
|                 }); | ||||
|                 textEditor.editing.view.scrollToTheSelection(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -19,6 +19,7 @@ const TPL = ` | ||||
|             display: block; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         .excalidraw-wrapper { | ||||
|             height: 100%; | ||||
|         } | ||||
|  | ||||
| @ -48,7 +48,7 @@ export default class ImageOptions extends OptionsWidget { | ||||
|             this.updateOption('imageMaxWidthHeight', this.$imageMaxWidthHeight.val())); | ||||
| 
 | ||||
|         this.$imageJpegQuality.on('change', () => | ||||
|             this.updateOption('imageJpegQuality', this.$imageJpegQuality.val())); | ||||
|             this.updateOption('imageJpegQuality', this.$imageJpegQuality.val().trim() || "75")); | ||||
| 
 | ||||
|         this.$downloadImagesAutomatically = this.$widget.find(".download-images-automatically"); | ||||
| 
 | ||||
|  | ||||
| @ -402,6 +402,42 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th | ||||
| .bs-tooltip-left .arrow::before   { border-left-color: var(--main-border-color) !important; } | ||||
| .bs-tooltip-right .arrow::before  { border-right-color: var(--main-border-color) !important; } | ||||
| 
 | ||||
| .bs-tooltip-bottom .arrow::after { border-bottom-color: var(--tooltip-background-color) !important; } | ||||
| .bs-tooltip-top .arrow::after    { border-top-color: var(--tooltip-background-color) !important; } | ||||
| .bs-tooltip-left .arrow::after   { border-left-color: var(--tooltip-background-color) !important; } | ||||
| .bs-tooltip-right .arrow::after  { border-right-color: var(--tooltip-background-color) !important; } | ||||
| 
 | ||||
| .tooltip .arrow::after { | ||||
|     position: absolute; | ||||
|     content: ''; | ||||
|     border-color: transparent; | ||||
|     border-style: solid; | ||||
| } | ||||
| 
 | ||||
| .bs-tooltip-auto[x-placement^='left'] .arrow::after, | ||||
| .bs-tooltip-left .arrow::after { | ||||
|     left: -1px; | ||||
|     border-width: 0.4rem 0 0.4rem 0.4rem; | ||||
| } | ||||
| 
 | ||||
| .bs-tooltip-auto[x-placement^='bottom'] .arrow::after, | ||||
| .bs-tooltip-bottom .arrow::after { | ||||
|     bottom: -1px; | ||||
|     border-width: 0 0.4rem 0.4rem; | ||||
| } | ||||
| 
 | ||||
| .bs-tooltip-auto[x-placement^='right'] .arrow::after, | ||||
| .bs-tooltip-right .arrow::after { | ||||
|     right: -1px; | ||||
|     border-width: 0.4rem 0.4rem 0.4rem 0; | ||||
| } | ||||
| 
 | ||||
| .bs-tooltip-auto[x-placement^='top'] .arrow::after, | ||||
| .bs-tooltip-top .arrow::after { | ||||
|     top: -1px; | ||||
|     border-width: 0.4rem 0.4rem 0; | ||||
| } | ||||
| 
 | ||||
| .note-tooltip.tooltip .arrow { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| @ -110,8 +110,8 @@ function checkCredentials(req, res, next) { | ||||
| 
 | ||||
|     const header = req.headers['trilium-cred'] || ''; | ||||
|     const auth = new Buffer.from(header, 'base64').toString(); | ||||
|     const [username, password] = auth.split(/:/); | ||||
| 
 | ||||
|     const colonIndex = auth.indexOf(':'); | ||||
|     const password = colonIndex === -1 ? "" : auth.substr(colonIndex + 1); | ||||
|     // username is ignored
 | ||||
| 
 | ||||
|     if (!passwordEncryptionService.verifyPassword(password)) { | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| module.exports = { buildDate:"2023-04-17T21:40:35+02:00", buildRevision: "1d3272e9f8c27106a66227fbb580677ae5d70427" }; | ||||
| module.exports = { buildDate:"2023-05-18T23:31:57+02:00", buildRevision: "14dd2b882750ea5484d1aba1f2b57c931bc76e9c" }; | ||||
|  | ||||
| @ -169,7 +169,7 @@ function saveImageToAttachment(noteId, uploadBuffer, originalName, shrinkImageSw | ||||
| } | ||||
| 
 | ||||
| async function shrinkImage(buffer, originalName) { | ||||
|     let jpegQuality = optionService.getOptionInt('imageJpegQuality'); | ||||
|     let jpegQuality = optionService.getOptionInt('imageJpegQuality', 0); | ||||
| 
 | ||||
|     if (jpegQuality < 10 || jpegQuality > 100) { | ||||
|         jpegQuality = 75; | ||||
|  | ||||
| @ -29,13 +29,17 @@ function getOption(name) { | ||||
| /** | ||||
|  * @returns {integer} | ||||
|  */ | ||||
| function getOptionInt(name) { | ||||
| function getOptionInt(name, defaultValue = undefined) { | ||||
|     const val = getOption(name); | ||||
| 
 | ||||
|     const intVal = parseInt(val); | ||||
| 
 | ||||
|     if (isNaN(intVal)) { | ||||
|         if (defaultValue === undefined) { | ||||
|             throw new Error(`Could not parse '${val}' into integer for option '${name}'`); | ||||
|         } else { | ||||
|             return defaultValue; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return intVal; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam