Compare commits

...

17 Commits

Author SHA1 Message Date
zadam
ad878c078e release 0.44.8 2020-10-15 00:01:13 +02:00
zadam
8a019d617f small visual tweaks to link map, including displaying the note icon 2020-10-14 23:14:04 +02:00
zadam
4a76b7a9a5 use correct path separator, closes #1308 2020-10-14 20:42:55 +02:00
zadam
1ac1bf12a2 upgrading webpack fixes the issue with broken mobile frontend (and hacky fix which breaks desktop in turn) 2020-10-14 14:20:06 +02:00
zadam
b50638e85c release 0.44.7 2020-10-13 23:45:39 +02:00
zadam
893b6053d2 provide also explicit unhoist keyboard shortcut, #1305 2020-10-13 23:41:55 +02:00
zadam
a1ec6fe0aa fix file preview in note revisions dialog 2020-10-13 23:09:57 +02:00
zadam
18cc9f2475 "open" action will save note to temp directory to try it then open with native application, #1304 2020-10-13 22:50:45 +02:00
zadam
04ea8dd4b3 fix scroll to top for read only / editable text note after switching note 2020-10-13 22:03:16 +02:00
zadam
28cb3976e5 add explicit button to show/hide right pane widgets, #1299 2020-10-12 23:15:53 +02:00
zadam
a665d193eb same event handling note execute only once, closes #1278 2020-10-12 22:30:30 +02:00
zadam
cabb78b3e4 double click on an external link opens it 2020-10-12 22:11:49 +02:00
zadam
d953d96fa6 improvements to error logging in frontend 2020-10-12 21:05:34 +02:00
zadam
c71ac0302a use var instead of const to avoid webpack issue in mobile frontend build, closes #1300 2020-10-10 23:45:38 +02:00
zadam
4eb9ca7b46 fix demo document word count widget with contain: none 2020-10-10 21:08:28 +02:00
zadam
42daf181d3 round zoom factor 2020-10-10 20:12:39 +02:00
zadam
140f0a5dbd return null for not found attribute, closes #1294 2020-10-08 22:08:58 +02:00
38 changed files with 3928 additions and 747 deletions

View File

@@ -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>

View File

@@ -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

Binary file not shown.

View File

@@ -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;
}
/**

4226
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.44.6",
"version": "0.44.8",
"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",
@@ -85,8 +86,8 @@
"jsdoc": "3.6.6",
"lorem-ipsum": "2.0.3",
"rcedit": "2.2.0",
"webpack": "5.0.0-beta.32",
"webpack-cli": "4.0.0-beta.8"
"webpack": "5.1.0",
"webpack-cli": "4.0.0"
},
"optionalDependencies": {
"electron-installer-debian": "2.0.1"

View File

@@ -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;

View File

@@ -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;

View File

@@ -46,7 +46,7 @@ $form.on('submit', () => {
includeNote(notePath);
}
else {
console.error("No noteId to include.");
logError("No noteId to include.");
}
return false;

View File

@@ -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;

View File

@@ -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();
});
});

View File

@@ -5,6 +5,16 @@ import noteAttributeCache from "../services/note_attribute_cache.js";
const LABEL = 'label';
const RELATION = 'relation';
const NOTE_TYPE_ICONS = {
"file": "bx bx-file",
"image": "bx bx-image",
"code": "bx bx-code",
"render": "bx bx-extension",
"search": "bx bx-file-find",
"relation-map": "bx bx-map-alt",
"book": "bx bx-book"
};
/**
* FIXME: since there's no "full note" anymore we can rename this to Note
*
@@ -254,6 +264,31 @@ class NoteShort {
return this.getAttributes(LABEL, name);
}
getIcon(isFolder = false) {
const iconCassLabels = this.getLabels('iconClass');
if (iconCassLabels.length > 0) {
return iconCassLabels.map(l => l.value).join(' ');
}
else if (this.noteId === 'root') {
return "bx bx-chevrons-right";
}
else if (this.type === 'text') {
if (isFolder) {
return "bx bx-folder";
}
else {
return "bx bx-note";
}
}
else if (this.type === 'code' && this.mime.startsWith('text/x-sql')) {
return "bx bx-data";
}
else {
return NOTE_TYPE_ICONS[this.type];
}
}
/**
* @param {string} [name] - relation name to filter
* @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
@@ -307,7 +342,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;
}
/**

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -91,6 +91,10 @@ export default class Entrypoints extends Component {
}
}
async unhoistCommand() {
hoistedNoteService.unhoist();
}
copyWithoutFormattingCommand() {
utils.copySelectionToClipboard();
}

View File

@@ -8,6 +8,10 @@ function getHoistedNoteId() {
}
async function setHoistedNoteId(noteId) {
if (getHoistedNoteId() === noteId) {
return;
}
await options.save('hoistedNoteId', noteId);
await treeCache.loadInitialTree();

View File

@@ -111,5 +111,6 @@ export default {
setElementActionHandler,
updateDisplayedShortcuts,
setupActionsForElement,
getActionsForScope
getActionsForScope,
getAction
};

View File

@@ -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,

View File

@@ -63,6 +63,27 @@ export default class LinkMap {
noteIds.add(this.note.noteId);
}
await treeCache.getNotes(Array.from(noteIds));
// pre-fetch the link titles, it's important to have hte construction afterwards synchronous
// since jsPlumb caculates width of the element
const $linkTitles = {};
for (const noteId of noteIds) {
const note = await treeCache.getNote(noteId);
$linkTitles[noteId] = await linkService.createNoteLink(noteId, {title: note.title});
$linkTitles[noteId].on('click', e => {
try {
$linkTitles[noteId].tooltip('dispose');
}
catch (e) {}
linkService.goToLink(e);
})
}
// preload all notes
const notes = await treeCache.getNotes(Array.from(noteIds), true);
@@ -98,18 +119,15 @@ export default class LinkMap {
.addClass("note-box")
.prop("id", noteBoxId);
linkService.createNoteLink(noteId, {title: note.title}).then($link => {
$link.on('click', e => {
try {
$link.tooltip('dispose');
}
catch (e) {}
const $link = $linkTitles[noteId];
linkService.goToLink(e);
});
$noteBox.append($("<span>").addClass("title").append($link));
});
$noteBox.append(
$("<span>")
.addClass(note.getIcon()),
$("<span>")
.addClass("title")
.append($link)
);
if (noteId === this.note.noteId) {
$noteBox.addClass("link-map-active-note");
@@ -273,4 +291,4 @@ export default class LinkMap {
noteIdToId(noteId) {
return this.linkMapContainerId + "-note-" + noteId;
}
}
}

View File

@@ -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;
}

View File

@@ -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 "";
}

View File

@@ -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;
}

View File

@@ -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: [

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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>
&nbsp;
<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.

View File

@@ -159,16 +159,6 @@ const TPL = `
</div>
`;
const NOTE_TYPE_ICONS = {
"file": "bx bx-file",
"image": "bx bx-image",
"code": "bx bx-code",
"render": "bx bx-extension",
"search": "bx bx-file-find",
"relation-map": "bx bx-map-alt",
"book": "bx bx-book"
};
export default class NoteTreeWidget extends TabAwareWidget {
constructor(treeName) {
super();
@@ -369,8 +359,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 +396,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 +450,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
@@ -559,40 +552,14 @@ export default class NoteTreeWidget extends TabAwareWidget {
return noteList;
}
getIconClass(note) {
const labels = note.getLabels('iconClass');
return labels.map(l => l.value).join(' ');
}
getIcon(note, isFolder) {
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
const iconClass = this.getIconClass(note);
if (iconClass) {
return iconClass;
}
else if (note.noteId === 'root') {
return "bx bx-chevrons-right";
}
else if (note.noteId === hoistedNoteId) {
if (note.noteId !== 'root' && note.noteId === hoistedNoteId) {
return "bx bxs-arrow-from-bottom";
}
else if (note.type === 'text') {
if (isFolder) {
return "bx bx-folder";
}
else {
return "bx bx-note";
}
}
else if (note.type === 'code' && note.mime.startsWith('text/x-sql')) {
return "bx bx-data";
}
else {
return NOTE_TYPE_ICONS[note.type];
}
return note.getIcon(isFolder);
}
updateNode(node) {
@@ -810,7 +777,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 +1120,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}`);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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();
}
}
}
}

View File

@@ -4,22 +4,21 @@
}
.link-map-container .note-box {
padding: 8px;
padding: 0px 8px 8px 8px;
position: absolute !important;
background-color: var(--main-background-color);
color: var(--main-text-color);
z-index: 4;
border: 1px solid #666;
box-shadow: 2px 2px 19px #999;
border-radius: 8px;
opacity: 0.8;
font-size: 11px;
width: auto;
height: auto;
max-width: 200px;
min-width: 120px;
max-height: 100px;
max-width: 250px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.link-map-container .note-box:hover {
@@ -47,4 +46,21 @@
.link-map-widget .note-box .title {
font-size: 19px !important;
}
font-weight: 600
}
#link-map-dialog .note-box .bx {
font-size: 24px !important;
position: relative;
top: 6px;
display: inline-block;
margin-right: 5px;
}
.link-map-widget .note-box .bx {
font-size: 30px !important;
position: relative;
top: 6px;
display: inline-block;
margin-right: 5px;
}

View File

@@ -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
};

View File

@@ -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);

View File

@@ -1 +1 @@
module.exports = { buildDate:"2020-10-07T00:07:23+02:00", buildRevision: "91a2bb9b266353a85afd112398025b96ea3522bf" };
module.exports = { buildDate:"2020-10-15T00:01:13+02:00", buildRevision: "8a019d617f1f1ecd1ef61812838bbe40532a0aa7" };

View File

@@ -10,6 +10,7 @@
const os = require('os');
const fs = require('fs');
const path = require('path');
function getAppDataDir() {
let appDataDir = os.homedir(); // fallback if OS is not recognized
@@ -43,13 +44,13 @@ function getTriliumDataDir() {
return process.env.TRILIUM_DATA_DIR;
}
const homePath = os.homedir() + "/" + DIR_NAME;
const homePath = os.homedir() + path.sep + DIR_NAME;
if (fs.existsSync(homePath)) {
return homePath;
}
const appDataPath = getAppDataDir() + '/' + DIR_NAME;
const appDataPath = getAppDataDir() + path.sep + DIR_NAME;
if (!fs.existsSync(appDataPath)) {
fs.mkdirSync(appDataPath, 0o700);
@@ -59,10 +60,10 @@ function getTriliumDataDir() {
}
const TRILIUM_DATA_DIR = getTriliumDataDir();
const DOCUMENT_PATH = TRILIUM_DATA_DIR + "/document.db";
const BACKUP_DIR = TRILIUM_DATA_DIR + "/backup";
const LOG_DIR = TRILIUM_DATA_DIR + "/log";
const ANONYMIZED_DB_DIR = TRILIUM_DATA_DIR + "/anonymized-db";
const DOCUMENT_PATH = TRILIUM_DATA_DIR + path.sep + "document.db";
const BACKUP_DIR = TRILIUM_DATA_DIR + path.sep + "backup";
const LOG_DIR = TRILIUM_DATA_DIR + path.sep + "log";
const ANONYMIZED_DB_DIR = TRILIUM_DATA_DIR + path.sep + "anonymized-db";
module.exports = {
TRILIUM_DATA_DIR,
@@ -70,4 +71,4 @@ module.exports = {
BACKUP_DIR,
LOG_DIR,
ANONYMIZED_DB_DIR
};
};

View File

@@ -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 });
}
}

View File

@@ -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"],