mirror of
https://github.com/zadam/trilium.git
synced 2025-10-27 16:26:31 +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)
|
||||
)
|
||||
@@ -196,4 +196,4 @@ $list.on('focus', '.dropdown-item', e => {
|
||||
});
|
||||
|
||||
setContentPane();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
<a class="widget-help external no-arrow bx bx-info-circle"></a>
|
||||
</span>
|
||||
|
||||
<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");
|
||||
}
|
||||
@@ -54,4 +60,4 @@ export default class TypeWidget extends TabAwareWidget {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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