mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 18:05:55 +01:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | b50638e85c | ||
|  | 893b6053d2 | ||
|  | a1ec6fe0aa | ||
|  | 18cc9f2475 | ||
|  | 04ea8dd4b3 | ||
|  | 28cb3976e5 | ||
|  | a665d193eb | ||
|  | cabb78b3e4 | ||
|  | d953d96fa6 | ||
|  | c71ac0302a | ||
|  | 4eb9ca7b46 | ||
|  | 42daf181d3 | ||
|  | 140f0a5dbd | 
							
								
								
									
										18
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							| @@ -9,5 +9,23 @@ | ||||
|     <JSCodeStyleSettings version="0"> | ||||
|       <option name="USE_EXPLICIT_JS_EXTENSION" value="TRUE" /> | ||||
|     </JSCodeStyleSettings> | ||||
|     <JetCodeStyleSettings> | ||||
|       <option name="PACKAGES_TO_USE_STAR_IMPORTS"> | ||||
|         <value> | ||||
|           <package name="java.util" alias="false" withSubpackages="false" /> | ||||
|           <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" /> | ||||
|           <package name="io.ktor" alias="false" withSubpackages="true" /> | ||||
|         </value> | ||||
|       </option> | ||||
|       <option name="PACKAGES_IMPORT_LAYOUT"> | ||||
|         <value> | ||||
|           <package name="" alias="false" withSubpackages="true" /> | ||||
|           <package name="java" alias="false" withSubpackages="true" /> | ||||
|           <package name="javax" alias="false" withSubpackages="true" /> | ||||
|           <package name="kotlin" alias="false" withSubpackages="true" /> | ||||
|           <package name="" alias="true" withSubpackages="true" /> | ||||
|         </value> | ||||
|       </option> | ||||
|     </JetCodeStyleSettings> | ||||
|   </code_scheme> | ||||
| </component> | ||||
| @@ -1,7 +1,7 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| PKG_DIR=dist/trilium-linux-x64-server | ||||
| NODE_VERSION=12.18.3 | ||||
| NODE_VERSION=12.19.0 | ||||
|  | ||||
| if [ "$1" != "DONTCOPY" ] | ||||
| then | ||||
|   | ||||
| @@ -7,6 +7,10 @@ fi | ||||
|  | ||||
| npm run webpack | ||||
|  | ||||
| # problem with circular dependencies: https://github.com/webpack/webpack/issues/9173 | ||||
| # source issue: https://github.com/zadam/trilium/issues/1300 | ||||
| find ./src/public/app-dist -type f -exec sed -i 's/const /var /g' {} + | ||||
|  | ||||
| DIR=$1 | ||||
|  | ||||
| rm -rf $DIR | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								db/demo.zip
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/demo.zip
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -335,7 +335,7 @@ class NoteShort { | ||||
|     getAttribute(type, name) { | ||||
|         const attributes = this.getAttributes(type, name); | ||||
|  | ||||
|         return attributes.length > 0 ? attributes[0] : 0; | ||||
|         return attributes.length > 0 ? attributes[0] : null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										77
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										77
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "trilium", | ||||
|   "version": "0.44.5", | ||||
|   "version": "0.44.6", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
| @@ -1409,6 +1409,15 @@ | ||||
|             "path-is-absolute": "^1.0.0" | ||||
|           } | ||||
|         }, | ||||
|         "rimraf": { | ||||
|           "version": "2.7.1", | ||||
|           "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", | ||||
|           "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "glob": "^7.1.3" | ||||
|           } | ||||
|         }, | ||||
|         "tmp-promise": { | ||||
|           "version": "1.1.0", | ||||
|           "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz", | ||||
| @@ -1417,6 +1426,17 @@ | ||||
|           "requires": { | ||||
|             "bluebird": "^3.5.0", | ||||
|             "tmp": "0.1.0" | ||||
|           }, | ||||
|           "dependencies": { | ||||
|             "tmp": { | ||||
|               "version": "0.1.0", | ||||
|               "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", | ||||
|               "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", | ||||
|               "optional": true, | ||||
|               "requires": { | ||||
|                 "rimraf": "^2.6.3" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| @@ -4605,7 +4625,6 @@ | ||||
|       "version": "7.1.6", | ||||
|       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", | ||||
|       "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "fs.realpath": "^1.0.0", | ||||
|         "inflight": "^1.0.4", | ||||
| @@ -8456,37 +8475,11 @@ | ||||
|       "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" | ||||
|     }, | ||||
|     "tmp": { | ||||
|       "version": "0.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", | ||||
|       "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", | ||||
|       "optional": true, | ||||
|       "version": "0.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", | ||||
|       "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", | ||||
|       "requires": { | ||||
|         "rimraf": "^2.6.3" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "glob": { | ||||
|           "version": "7.1.4", | ||||
|           "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", | ||||
|           "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "fs.realpath": "^1.0.0", | ||||
|             "inflight": "^1.0.4", | ||||
|             "inherits": "2", | ||||
|             "minimatch": "^3.0.4", | ||||
|             "once": "^1.3.0", | ||||
|             "path-is-absolute": "^1.0.0" | ||||
|           } | ||||
|         }, | ||||
|         "rimraf": { | ||||
|           "version": "2.7.1", | ||||
|           "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", | ||||
|           "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "glob": "^7.1.3" | ||||
|           } | ||||
|         } | ||||
|         "rimraf": "^3.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "tmp-promise": { | ||||
| @@ -8496,6 +8489,26 @@ | ||||
|       "optional": true, | ||||
|       "requires": { | ||||
|         "tmp": "0.1.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "rimraf": { | ||||
|           "version": "2.7.1", | ||||
|           "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", | ||||
|           "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "glob": "^7.1.3" | ||||
|           } | ||||
|         }, | ||||
|         "tmp": { | ||||
|           "version": "0.1.0", | ||||
|           "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", | ||||
|           "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", | ||||
|           "optional": true, | ||||
|           "requires": { | ||||
|             "rimraf": "^2.6.3" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "to-object-path": { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.44.6", | ||||
|   "version": "0.44.7", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
| @@ -67,6 +67,7 @@ | ||||
|     "serve-favicon": "2.5.0", | ||||
|     "session-file-store": "1.4.0", | ||||
|     "striptags": "3.1.1", | ||||
|     "tmp": "^0.2.1", | ||||
|     "turndown": "6.0.0", | ||||
|     "turndown-plugin-gfm": "1.0.2", | ||||
|     "unescape": "1.0.1", | ||||
|   | ||||
| @@ -86,7 +86,7 @@ $form.on('submit', () => { | ||||
|         textTypeWidget.addLink(notePath, linkTitle); | ||||
|     } | ||||
|     else { | ||||
|         console.error("No path to add link."); | ||||
|         logError("No path to add link."); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   | ||||
| @@ -61,7 +61,7 @@ $form.on('submit', () => { | ||||
|         cloneNotesTo(notePath); | ||||
|     } | ||||
|     else { | ||||
|         console.error("No path to clone to."); | ||||
|         logError("No path to clone to."); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   | ||||
| @@ -46,7 +46,7 @@ $form.on('submit', () => { | ||||
|         includeNote(notePath); | ||||
|     } | ||||
|     else { | ||||
|         console.error("No noteId to include."); | ||||
|         logError("No noteId to include."); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   | ||||
| @@ -51,7 +51,7 @@ $form.on('submit', () => { | ||||
|         treeCache.getBranchId(parentNoteId, noteId).then(branchId => moveNotesTo(branchId)); | ||||
|     } | ||||
|     else { | ||||
|         console.error("No path to move to."); | ||||
|         logError("No path to move to."); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   | ||||
| @@ -157,8 +157,8 @@ async function setContentPane() { | ||||
|  | ||||
|         if (fullNoteRevision.content) { | ||||
|             $table.append($("<tr>").append( | ||||
|                 $("<th>").text("Preview:"), | ||||
|                 $("<td>").append( | ||||
|                 $('<td colspan="2">').append( | ||||
|                     $('<div style="font-weight: bold;">').text("Preview:"), | ||||
|                     $('<pre class="file-preview-content"></pre>') | ||||
|                         .text(fullNoteRevision.content) | ||||
|                 ) | ||||
|   | ||||
| @@ -307,7 +307,7 @@ class NoteShort { | ||||
|     getAttribute(type, name) { | ||||
|         const attributes = this.getAttributes(type, name); | ||||
|  | ||||
|         return attributes.length > 0 ? attributes[0] : 0; | ||||
|         return attributes.length > 0 ? attributes[0] : null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -77,6 +77,17 @@ const RIGHT_PANE_CSS = ` | ||||
|     text-decoration: none; | ||||
| } | ||||
|  | ||||
| #right-pane .widget-toggle-button { | ||||
|     cursor: pointer; | ||||
|     color: var(--main-text-color) !important; | ||||
|     position: relative; | ||||
|     top: 2px; | ||||
| } | ||||
|  | ||||
| #right-pane .widget-toggle-button:hover { | ||||
|     text-decoration: none !important; | ||||
| } | ||||
|  | ||||
| #right-pane .body-wrapper { | ||||
|     overflow: auto; | ||||
| } | ||||
|   | ||||
| @@ -64,7 +64,7 @@ async function getWidgetBundlesByParent() { | ||||
|             widget = await executeBundle(bundle); | ||||
|         } | ||||
|         catch (e) { | ||||
|             console.error("Widget initialization failed: ", e); | ||||
|             logError("Widget initialization failed: ", e); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -91,6 +91,10 @@ export default class Entrypoints extends Component { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async unhoistCommand() { | ||||
|         hoistedNoteService.unhoist(); | ||||
|     } | ||||
|  | ||||
|     copyWithoutFormattingCommand() { | ||||
|         utils.copySelectionToClipboard(); | ||||
|     } | ||||
|   | ||||
| @@ -8,6 +8,10 @@ function getHoistedNoteId() { | ||||
| } | ||||
|  | ||||
| async function setHoistedNoteId(noteId) { | ||||
|     if (getHoistedNoteId() === noteId) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     await options.save('hoistedNoteId', noteId); | ||||
|  | ||||
|     await treeCache.loadInitialTree(); | ||||
|   | ||||
| @@ -111,5 +111,6 @@ export default { | ||||
| 	setElementActionHandler, | ||||
| 	updateDisplayedShortcuts, | ||||
| 	setupActionsForElement, | ||||
| 	getActionsForScope | ||||
| 	getActionsForScope, | ||||
| 	getAction | ||||
| }; | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import treeService from './tree.js'; | ||||
| import contextMenu from "./context_menu.js"; | ||||
| import appContext from "./app_context.js"; | ||||
| import treeCache from "./tree_cache.js"; | ||||
| import utils from "./utils.js"; | ||||
|  | ||||
| function getNotePathFromUrl(url) { | ||||
|     const notePathMatch = /#(root[A-Za-z0-9/]*)$/.exec(url); | ||||
| @@ -11,7 +12,7 @@ function getNotePathFromUrl(url) { | ||||
|  | ||||
| async function createNoteLink(notePath, options = {}) { | ||||
|     if (!notePath || !notePath.trim()) { | ||||
|         console.error("Missing note path"); | ||||
|         logError("Missing note path"); | ||||
|  | ||||
|         return $("<span>").text("[missing note]"); | ||||
|     } | ||||
| @@ -89,9 +90,16 @@ function goToLink(e) { | ||||
|             || $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices | ||||
|         ) { | ||||
|             const address = $link.attr('href'); | ||||
| console.log("address", address); | ||||
|             if (address) { | ||||
|                 if (address.toLowerCase().startsWith('http')) { | ||||
|                     window.open(address, '_blank'); | ||||
|                 } | ||||
|                 else if (address.toLowerCase().startsWith('file:') && utils.isElectron()) { | ||||
|                     const electron = utils.dynamicRequire('electron'); | ||||
|  | ||||
|             if (address && address.startsWith('http')) { | ||||
|                 window.open(address, '_blank'); | ||||
|                     electron.shell.openPath(address); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -149,6 +157,18 @@ async function loadReferenceLinkTitle(noteId, $el) { | ||||
| $(document).on('click', "a", goToLink); | ||||
| $(document).on('auxclick', "a", goToLink); // to handle middle button | ||||
| $(document).on('contextmenu', 'a', linkContextMenu); | ||||
| $(document).on('dblclick', "a", e => { | ||||
|     e.preventDefault(); | ||||
|     e.stopPropagation(); | ||||
|  | ||||
|     const $link = $(e.target).closest("a"); | ||||
|  | ||||
|     const address = $link.attr('href'); | ||||
|  | ||||
|     if (address && address.startsWith('http')) { | ||||
|         window.open(address, '_blank'); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     getNotePathFromUrl, | ||||
|   | ||||
| @@ -36,7 +36,7 @@ class TabContext extends Component { | ||||
|             resolvedNotePath = await treeService.resolveNotePath(inputNotePath); | ||||
|  | ||||
|             if (!resolvedNotePath) { | ||||
|                 console.error(`Cannot resolve note path ${inputNotePath}`); | ||||
|                 logError(`Cannot resolve note path ${inputNotePath}`); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -62,7 +62,7 @@ async function resolveNotePathToSegments(notePath, logErrors = true) { | ||||
|  | ||||
|             if (!parents.length) { | ||||
|                 if (logErrors) { | ||||
|                     ws.logError(`No parents found for ${childNoteId}`); | ||||
|                     ws.logError(`No parents found for ${childNoteId} (${child.title})`); | ||||
|                 } | ||||
|  | ||||
|                 return; | ||||
| @@ -70,7 +70,9 @@ async function resolveNotePathToSegments(notePath, logErrors = true) { | ||||
|  | ||||
|             if (!parents.some(p => p.noteId === parentNoteId)) { | ||||
|                 if (logErrors) { | ||||
|                     console.log(utils.now(), `Did not find parent ${parentNoteId} for child ${childNoteId}, available parents: ${parents.map(p => p.noteId)}`); | ||||
|                     const parent = treeCache.getNoteFromCache(parentNoteId); | ||||
|  | ||||
|                     console.log(utils.now(), `Did not find parent ${parentNoteId} (${parent ? parent.title : 'n/a'}) for child ${childNoteId} (${child.title}), available parents: ${parents.map(p => `${p.noteId} (${p.title})`)}`); | ||||
|                 } | ||||
|  | ||||
|                 const someNotePath = getSomeNotePath(parents[0]); | ||||
| @@ -113,7 +115,7 @@ function getSomeNotePath(note) { | ||||
|         const parents = cur.getParentNotes(); | ||||
|  | ||||
|         if (!parents.length) { | ||||
|             console.error(`Can't find parents for note ${cur.noteId}`); | ||||
|             logError(`Can't find parents for note ${cur.noteId}`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -196,7 +198,7 @@ function getNoteIdAndParentIdFromNotePath(notePath) { | ||||
|  | ||||
| function getNotePath(node) { | ||||
|     if (!node) { | ||||
|         console.error("Node is null"); | ||||
|         logError("Node is null"); | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -267,7 +267,7 @@ class TreeCache { | ||||
|     getBranch(branchId, silentNotFoundError = false) { | ||||
|         if (!(branchId in this.branches)) { | ||||
|             if (!silentNotFoundError) { | ||||
|                 console.error(`Not existing branch ${branchId}`); | ||||
|                 logError(`Not existing branch ${branchId}`); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
| @@ -283,7 +283,7 @@ class TreeCache { | ||||
|         const child = await this.getNote(childNoteId); | ||||
|  | ||||
|         if (!child) { | ||||
|             console.error(`Could not find branchId for parent=${parentNoteId}, child=${childNoteId} since child does not exist`); | ||||
|             logError(`Could not find branchId for parent=${parentNoteId}, child=${childNoteId} since child does not exist`); | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ class TreeContextMenu { | ||||
|             { title: 'Search in subtree <kbd data-command="searchInSubtree"></kbd>', command: "searchInSubtree", uiIcon: "search", | ||||
|                 enabled: notSearch && noSelectedNotes }, | ||||
|             isHoisted ? null : { title: 'Hoist note <kbd data-command="toggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "empty", enabled: noSelectedNotes && notSearch }, | ||||
|             !isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-command="ToggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "arrow-up" }, | ||||
|             !isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-command="ToggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "arrow-from-bottom" }, | ||||
|             { title: 'Edit branch prefix <kbd data-command="editBranchPrefix"></kbd>', command: "editBranchPrefix", uiIcon: "empty", | ||||
|                 enabled: isNotRoot && parentNotSearch && noSelectedNotes}, | ||||
|             { title: "Advanced", uiIcon: "empty", enabled: true, items: [ | ||||
|   | ||||
| @@ -19,8 +19,7 @@ let lastPingTs; | ||||
| let syncDataQueue = []; | ||||
|  | ||||
| function logError(message) { | ||||
|     console.log(utils.now(), message); // needs to be separate from .trace() | ||||
|     console.trace(); | ||||
|     console.error(utils.now(), message); // needs to be separate from .trace() | ||||
|  | ||||
|     if (ws && ws.readyState === 1) { | ||||
|         ws.send(JSON.stringify({ | ||||
| @@ -31,6 +30,8 @@ function logError(message) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| window.logError = logError; | ||||
|  | ||||
| function subscribeToMessages(messageHandler) { | ||||
|     messageHandlers.push(messageHandler); | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,8 @@ class ZoomService extends Component { | ||||
|  | ||||
|     async setZoomFactorAndSave(zoomFactor) { | ||||
|         if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) { | ||||
|             zoomFactor = Math.round(zoomFactor * 10) / 10; | ||||
|  | ||||
|             this.setZoomFactor(zoomFactor); | ||||
|  | ||||
|             await options.save('zoomFactor', zoomFactor); | ||||
|   | ||||
| @@ -5,14 +5,20 @@ const WIDGET_TPL = ` | ||||
| <div class="card widget"> | ||||
|     <div class="card-header"> | ||||
|         <div>            | ||||
|             <button class="btn btn-sm widget-title" data-toggle="collapse" data-target="#[to be set]"> | ||||
|             <span class="widget-title"> | ||||
|                 Collapsible Group Item | ||||
|             </button> | ||||
|             </span> | ||||
|          | ||||
|             <a class="widget-help external no-arrow bx bx-info-circle"></a> | ||||
|             <span class="widget-header-actions"></span> | ||||
|         </div> | ||||
|          | ||||
|         <div class="widget-header-actions"></div> | ||||
|         <div> | ||||
|             <a class="widget-help external no-arrow bx bx-info-circle"></a> | ||||
|               | ||||
|             <a class="widget-toggle-button no-arrow bx bx-minus"  | ||||
|                 title="Minimize/maximize widget" | ||||
|                 data-toggle="collapse" data-target="#[to be set]"></a> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div id="[to be set]" class="collapse body-wrapper" style="transition: none; "> | ||||
| @@ -38,13 +44,18 @@ export default class CollapsibleWidget extends TabAwareWidget { | ||||
|         // not using constructor name because of webpack mangling class names ... | ||||
|         this.widgetName = this.widgetTitle.replace(/[^[a-zA-Z0-9]/g, "_"); | ||||
|  | ||||
|         if (!options.is(this.widgetName + 'Collapsed')) { | ||||
|         this.$toggleButton = this.$widget.find('.widget-toggle-button'); | ||||
|  | ||||
|         const collapsed = options.is(this.widgetName + 'Collapsed'); | ||||
|         if (!collapsed) { | ||||
|             this.$bodyWrapper.collapse("show"); | ||||
|         } | ||||
|  | ||||
|         this.updateToggleButton(collapsed); | ||||
|  | ||||
|         // using immediate variants of the event so that the previous collapse is not caught | ||||
|         this.$bodyWrapper.on('hide.bs.collapse', () => this.saveCollapsed(true)); | ||||
|         this.$bodyWrapper.on('show.bs.collapse', () => this.saveCollapsed(false)); | ||||
|         this.$bodyWrapper.on('hide.bs.collapse', () => this.toggleCollapsed(true)); | ||||
|         this.$bodyWrapper.on('show.bs.collapse', () => this.toggleCollapsed(false)); | ||||
|  | ||||
|         this.$body = this.$bodyWrapper.find('.card-body'); | ||||
|  | ||||
| @@ -66,19 +77,41 @@ export default class CollapsibleWidget extends TabAwareWidget { | ||||
|         } | ||||
|  | ||||
|         this.$headerActions = this.$widget.find('.widget-header-actions'); | ||||
|         this.$headerActions.append(...this.headerActions); | ||||
|         let headerActions = this.headerActions; | ||||
|  | ||||
|         if (headerActions.length > 0) { | ||||
|             headerActions = ["(", ...headerActions, ")"]; | ||||
|         } | ||||
|  | ||||
|         this.$headerActions.append(...headerActions); | ||||
|  | ||||
|         this.initialized = this.doRenderBody(); | ||||
|  | ||||
|         this.decorateWidget(); | ||||
|     } | ||||
|  | ||||
|     saveCollapsed(collapse) { | ||||
|     toggleCollapsed(collapse) { | ||||
|         this.updateToggleButton(collapse); | ||||
|  | ||||
|         options.save(this.widgetName + 'Collapsed', collapse.toString()); | ||||
|  | ||||
|         this.triggerEvent(`widgetCollapsedStateChanged`, {widgetName: this.widgetName, collapse}); | ||||
|     } | ||||
|  | ||||
|     updateToggleButton(collapse) { | ||||
|         if (collapse) { | ||||
|             this.$toggleButton | ||||
|                 .addClass("bx-window") | ||||
|                 .removeClass("bx-minus") | ||||
|                 .attr("title", "Show"); | ||||
|         } else { | ||||
|             this.$toggleButton | ||||
|                 .addClass("bx-minus") | ||||
|                 .removeClass("bx-window") | ||||
|                 .attr("title", "Hide"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered | ||||
|      * separately but should behave uniformly for the user. | ||||
|   | ||||
| @@ -369,8 +369,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|                     data.dataTransfer.setData("text", JSON.stringify(notes)); | ||||
|                     return true; // allow dragging to start | ||||
|                 }, | ||||
|                 dragEnter: (node, data) => true, // allow drop on any node | ||||
|                 dragOver: (node, data) => true, | ||||
|                 dragEnter: (node, data) => node.data.noteType !== 'search', | ||||
|                 dragDrop: async (node, data) => { | ||||
|                     if ((data.hitMode === 'over' && node.data.noteType === 'search') || | ||||
|                         (['after', 'before'].includes(data.hitMode) | ||||
| @@ -407,7 +406,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|                             notes = JSON.parse(jsonStr); | ||||
|                         } | ||||
|                         catch (e) { | ||||
|                             console.error(`Cannot parse ${jsonStr} into notes for drop`); | ||||
|                             logError(`Cannot parse ${jsonStr} into notes for drop`); | ||||
|                             return; | ||||
|                         } | ||||
|  | ||||
| @@ -461,7 +460,11 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|                     && node.data.noteId === hoistedNoteService.getHoistedNoteId() | ||||
|                     && $span.find('.unhoist-button').length === 0) { | ||||
|  | ||||
|                     const unhoistButton = $('<span class="unhoist-button-wrapper" title="Unhoist current note to show the whole note tree">[<a class="unhoist-button">unhoist</a>]</span>'); | ||||
|                     const action = await keyboardActionsService.getAction('unhoist'); | ||||
|                     let shortcuts = action.effectiveShortcuts.join(','); | ||||
|                     shortcuts = shortcuts ? `(${shortcuts})` : ''; | ||||
|  | ||||
|                     const unhoistButton = $(`<span class="unhoist-button-wrapper" title="Unhoist current note to show the whole note tree ${shortcuts}">[<a class="unhoist-button">unhoist</a>]</span>`); | ||||
|  | ||||
|                     // prepending since appending could push out (when note title is too long) | ||||
|                     // the button too much to the right so that it's not visible | ||||
| @@ -810,7 +813,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|  | ||||
|         if (!resolvedNotePathSegments) { | ||||
|             if (logErrors) { | ||||
|                 console.error("Could not find run path for notePath:", notePath); | ||||
|                 logError("Could not find run path for notePath:", notePath); | ||||
|             } | ||||
|  | ||||
|             return; | ||||
| @@ -1153,7 +1156,19 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|     async setExpanded(branchId, isExpanded) { | ||||
|         utils.assertArguments(branchId); | ||||
|  | ||||
|         const branch = treeCache.getBranch(branchId); | ||||
|         const branch = treeCache.getBranch(branchId, true); | ||||
|  | ||||
|         if (!branch) { | ||||
|             if (branchId && branchId.startsWith('virt')) { | ||||
|                 // in case of virtual branches there's nothing to update | ||||
|                 return; | ||||
|             } | ||||
|             else { | ||||
|                 logError(`Cannot find branch=${branchId}`); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         branch.isExpanded = isExpanded; | ||||
|  | ||||
|         await server.put(`branches/${branchId}/expanded/${isExpanded ? 1 : 0}`); | ||||
|   | ||||
| @@ -75,11 +75,17 @@ export default class FileTypeWidget extends TypeWidget { | ||||
|  | ||||
|         this.$downloadButton.on('click', () => utils.download(this.getFileUrl())); | ||||
|  | ||||
|         this.$openButton.on('click', () => { | ||||
|         this.$openButton.on('click', async () => { | ||||
|             if (utils.isElectron()) { | ||||
|                 const open = utils.dynamicRequire("open"); | ||||
|                 const resp = await server.post("notes/" + this.noteId + "/saveToTmpDir"); | ||||
|  | ||||
|                 open(this.getFileUrl(), {url: true}); | ||||
|                 const electron = utils.dynamicRequire('electron'); | ||||
|                 const res = await electron.shell.openPath(resp.tmpFilePath); | ||||
|  | ||||
|                 if (res) { | ||||
|                     // fallback in case there's no default application for this file | ||||
|                     open(this.getFileUrl(), {url: true}); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 window.location.href = this.getFileUrl(); | ||||
|   | ||||
| @@ -65,10 +65,6 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { | ||||
|         this.$content.html(''); | ||||
|     } | ||||
|  | ||||
|     scrollToTop() { | ||||
|         this.$content.scrollTop(0); | ||||
|     } | ||||
|  | ||||
|     async doRefresh(note) { | ||||
|         const noteComplement = await treeCache.getNoteComplement(note.noteId); | ||||
|  | ||||
|   | ||||
| @@ -539,7 +539,7 @@ export default class RelationMapTypeWidget extends TypeWidget { | ||||
|                 const note = this.mapData.notes.find(note => note.noteId === noteId); | ||||
|  | ||||
|                 if (!note) { | ||||
|                     console.error(`Note ${noteId} not found!`); | ||||
|                     logError(`Note ${noteId} not found!`); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,12 @@ export default class TypeWidget extends TabAwareWidget { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async noteSwitched() { | ||||
|         this.scrollToTop(); | ||||
|  | ||||
|         await super.noteSwitched(); | ||||
|     } | ||||
|  | ||||
|     isActive() { | ||||
|         return this.$widget.is(":visible"); | ||||
|     } | ||||
|   | ||||
| @@ -4,6 +4,8 @@ const protectedSessionService = require('../../services/protected_session'); | ||||
| const repository = require('../../services/repository'); | ||||
| const utils = require('../../services/utils'); | ||||
| const noteRevisionService = require('../../services/note_revisions'); | ||||
| const tmp = require('tmp'); | ||||
| const fs = require('fs'); | ||||
|  | ||||
| function updateFile(req) { | ||||
|     const {noteId} = req.params; | ||||
| @@ -31,6 +33,12 @@ function updateFile(req) { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function getFilename(note) { | ||||
|     // (one) reason we're not using the originFileName (available as label) is that it's not | ||||
|     // available for older note revisions and thus would be inconsistent | ||||
|     return utils.formatDownloadTitle(note.title, note.type, note.mime); | ||||
| } | ||||
|  | ||||
| function downloadNoteFile(noteId, res, contentDisposition = true) { | ||||
|     const note = repository.getNote(noteId); | ||||
|  | ||||
| @@ -43,9 +51,7 @@ function downloadNoteFile(noteId, res, contentDisposition = true) { | ||||
|     } | ||||
|  | ||||
|     if (contentDisposition) { | ||||
|         // (one) reason we're not using the originFileName (available as label) is that it's not | ||||
|         // available for older note revisions and thus would be inconsistent | ||||
|         const filename = utils.formatDownloadTitle(note.title, note.type, note.mime); | ||||
|         const filename = getFilename(note); | ||||
|  | ||||
|         res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); | ||||
|     } | ||||
| @@ -67,9 +73,29 @@ function openFile(req, res) { | ||||
|     return downloadNoteFile(noteId, res, false); | ||||
| } | ||||
|  | ||||
| function saveToTmpDir(req) { | ||||
|     const noteId = req.params.noteId; | ||||
|  | ||||
|     const note = repository.getNote(noteId); | ||||
|  | ||||
|     if (!note) { | ||||
|         return [404,`Note ${noteId} doesn't exist.`]; | ||||
|     } | ||||
|  | ||||
|     const tmpObj = tmp.fileSync({postfix: getFilename(note)}); | ||||
|  | ||||
|     fs.writeSync(tmpObj.fd, note.getContent()); | ||||
|     fs.closeSync(tmpObj.fd); | ||||
|  | ||||
|     return { | ||||
|         tmpFilePath: tmpObj.name | ||||
|     }; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     updateFile, | ||||
|     openFile, | ||||
|     downloadFile, | ||||
|     downloadNoteFile | ||||
|     downloadNoteFile, | ||||
|     saveToTmpDir | ||||
| }; | ||||
|   | ||||
| @@ -171,6 +171,7 @@ function register(app) { | ||||
|     route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile); | ||||
|     // this "hacky" path is used for easier referencing of CSS resources | ||||
|     route(GET, '/api/notes/download/:noteId', [auth.checkApiAuthOrElectron], filesRoute.downloadFile); | ||||
|     apiRoute(POST, '/api/notes/:noteId/saveToTmpDir', filesRoute.saveToTmpDir); | ||||
|  | ||||
|     apiRoute(GET, '/api/notes/:noteId/attributes', attributesRoute.getEffectiveNoteAttributes); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/attributes', attributesRoute.updateNoteAttributes); | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| module.exports = { buildDate:"2020-10-07T00:07:23+02:00", buildRevision: "91a2bb9b266353a85afd112398025b96ea3522bf" }; | ||||
| module.exports = { buildDate:"2020-10-13T23:45:39+02:00", buildRevision: "893b6053d22c159d6197900c988f0007eca4840d" }; | ||||
|   | ||||
| @@ -6,17 +6,15 @@ const repository = require('./repository'); | ||||
| const Attribute = require('../entities/attribute'); | ||||
|  | ||||
| function runAttachedRelations(note, relationName, originEntity) { | ||||
|     const runRelations = note.getRelations(relationName); | ||||
|     // same script note can get here with multiple ways, but execute only once | ||||
|     const notesToRun = new Set( | ||||
|         note.getRelations(relationName) | ||||
|             .map(relation => relation.getTargetNote()) | ||||
|             .filter(note => !!note) | ||||
|     ); | ||||
|  | ||||
|     for (const relation of runRelations) { | ||||
|         const scriptNote = relation.getTargetNote(); | ||||
|  | ||||
|         if (scriptNote) { | ||||
|             scriptService.executeNoteNoException(scriptNote, { originEntity }); | ||||
|         } | ||||
|         else { | ||||
|             log.error(`Target note ${relation.value} of atttribute ${relation.attributeId} has not been found.`); | ||||
|         } | ||||
|     for (const noteToRun of notesToRun) { | ||||
|         scriptService.executeNoteNoException(noteToRun, { originEntity }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -352,6 +352,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [ | ||||
|         description: "Toggles note hoisting of active note", | ||||
|         scope: "window" | ||||
|     }, | ||||
|     { | ||||
|         actionName: "unhoist", | ||||
|         defaultShortcuts: ["Alt+U"], | ||||
|         description: "Unhoist from anywhere", | ||||
|         scope: "window" | ||||
|     }, | ||||
|     { | ||||
|         actionName: "reloadFrontendApp", | ||||
|         defaultShortcuts: ["F5", "CommandOrControl+R"], | ||||
|   | ||||
		Reference in New Issue
	
	Block a user