Compare commits

..

28 Commits

Author SHA1 Message Date
zadam
f4266754d8 release 0.43.4 2020-08-27 23:58:58 +02:00
zadam
dc288fb18c Merge remote-tracking branch 'origin/stable' into stable 2020-08-27 23:22:58 +02:00
zadam
26dfa1ffdb activate PDF preview also in server build, fixes #1208 2020-08-27 23:22:36 +02:00
zadam
153de63f4d update note title in link map when note is renamed 2020-08-20 15:45:33 +02:00
zadam
a89629b3de add fallback when resizing image fails, closes #1190 2020-08-20 11:34:14 +02:00
zadam
eec850c11f fix toggle sidebar issues, closes #1196 2020-08-20 11:14:27 +02:00
zadam
e8d63b5647 add "search in note" to "note actions" menu, #1184 2020-08-14 20:58:19 +02:00
zadam
bd8b83898f Merge remote-tracking branch 'origin/stable' into stable 2020-08-12 23:59:42 +02:00
zadam
97109efb6c fix attribute cache invalidation 2020-08-12 23:59:33 +02:00
Jody
3e89855aa3 Add web app manifest (#1174)
* Add web app manifest, link to new manifest on mobile template.

* Remove duplicate manifest entry.
2020-08-12 21:39:12 +02:00
zadam
3b148eb6f8 fixed demo document to correctly show included notes 2020-08-10 23:42:06 +02:00
zadam
4e8d1dac67 fixes for rendering PDF previews e.g. in include note 2020-08-10 23:39:17 +02:00
zadam
7779fd1dfe synchronize the removal of the selection in cuttonote to make it more predictable 2020-08-10 22:42:57 +02:00
zadam
960d7dede3 add scrolling margins, #1181 2020-08-09 23:20:57 +02:00
zadam
224fbdc8cd small improvements to text preview in file notes 2020-08-04 22:06:25 +02:00
zadam
8561201abc invalidate note complement cache quickly after load 2020-08-04 21:57:08 +02:00
zadam
3c1a809276 external links should open only in new window, not in the original window, closes #1171 2020-08-04 20:42:32 +02:00
zadam
4b101baf00 CKEditor 21 content styles 2020-08-03 23:36:33 +02:00
zadam
782127dd91 fix noproxy handling for image downloading 2020-08-03 23:33:44 +02:00
zadam
4fc8bace94 support horizontal line, closes #1164 2020-08-01 23:41:30 +02:00
zadam
47a22f6e8d release 0.43.3 2020-07-31 23:34:05 +02:00
zadam
17d7ff3ff1 small improvements to sync table handling 2020-07-31 00:08:01 +02:00
zadam
3582013a33 import of initial demo document sets sync.isSynced incorrectly to always 0, #1163 2020-07-31 00:07:38 +02:00
zadam
95bbdb3b6b CKEditor 21 2020-07-29 23:34:49 +02:00
zadam
8a57960c6e tweaks in sync timeout handling 2020-07-28 23:29:12 +02:00
zadam
5f4a84d967 fix extracting base64 inline images from HTML, fixes #1159 2020-07-26 23:47:06 +02:00
zadam
099e90ed64 fix extracting base64 inline images from HTML, fixes #1159 2020-07-26 22:58:22 +02:00
zadam
3d324b954d fix checking affected notes when modified attribute's owning note is not loaded into cache, #803 2020-07-15 22:36:27 +02:00
36 changed files with 335 additions and 206 deletions

Binary file not shown.

View File

@@ -0,0 +1,4 @@
UPDATE sync SET isSynced = 1 WHERE entityName != 'options' OR (
entityName = 'options'
AND 1 = (SELECT isSynced FROM options WHERE name = sync.entityId)
)

View File

@@ -1,10 +1,18 @@
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
.ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
display: none;
}
/*
* CKEditor 5 (v19.1.1) content styles.
* Generated on Fri, 19 Jun 2020 01:26:44 GMT.
* CKEditor 5 (v21.0.0) content styles.
* Generated on Wed, 29 Jul 2020 12:14:43 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
*/
:root {
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
--ck-color-mention-text: hsl(341, 100%, 30%);
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
--ck-highlight-marker-green: hsl(120, 93%, 68%);
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
@@ -15,6 +23,81 @@
--ck-todo-list-checkmark-size: 16px;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
text-align: center;
margin: 1em auto;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 50px;
}
/* ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: hsl(0, 0%, 20%);
background-color: hsl(0, 0%, 97%);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
width: 100%;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
display: block;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
list-style: none;
@@ -82,12 +165,54 @@
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 1em 0;
display: block;
min-width: 15em;
/* ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
/* ckeditor5-basic-styles/theme/code.css */
.ck-content code {
@@ -95,21 +220,6 @@
padding: .15em;
border-radius: 2px;
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 1em auto;
@@ -143,98 +253,6 @@
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
width: 100%;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
display: block;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
text-align: center;
margin: 1em auto;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 50px;
}
/* ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: hsl(0, 0%, 20%);
background-color: hsl(0, 0%, 97%);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side,
.ck-content .image-style-align-left,
.ck-content .image-style-align-center,
.ck-content .image-style-align-right {
max-width: 50%;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
@@ -271,21 +289,12 @@
-ms-user-select: none;
user-select: none;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
/* ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 1em 0;
display: block;
min-width: 15em;
}
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
@@ -307,12 +316,10 @@
padding: 0;
border-radius: 0;
}
/* ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
/* ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
@media print {
/* ckeditor5-page-break/theme/pagebreak.css */
@@ -324,9 +331,3 @@
display: none;
}
}
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
.ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
display: none;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.43.0-beta",
"version": "0.43.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -7923,9 +7923,9 @@
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
},
"node-abi": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz",
"integrity": "sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA==",
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz",
"integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==",
"requires": {
"semver": "^5.4.1"
},

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.43.2",
"version": "0.43.4",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -54,7 +54,7 @@
"jimp": "0.10.3",
"mime-types": "2.1.27",
"multer": "1.4.2",
"node-abi": "2.16.0",
"node-abi": "2.18.0",
"open": "7.0.3",
"portscanner": "2.2.0",
"rand-token": "1.0.1",

View File

@@ -52,7 +52,17 @@ class Attribute {
* 3. attribute is owned by some note's ancestor and is inheritable
*/
isAffecting(affectedNote) {
if (!affectedNote) {
return false;
}
const attrNote = this.getNote();
if (!attrNote) {
// the note (owner of the attribute) is not even loaded into the cache so it should not affect anything else
return false;
}
const owningNotes = [affectedNote, ...affectedNote.getTemplateNotes()];
for (const owningNote of owningNotes) {

View File

@@ -83,6 +83,8 @@ function setupGlobs() {
$("body").on("click", "a.external", function () {
window.open($(this).attr("href"), '_blank');
return false;
});
}

View File

@@ -107,8 +107,8 @@ export default class LoadResults {
* notably changes in note itself should not have any effect on attributes
*/
hasAttributeRelatedChanges() {
return this.branches.length === 0
&& this.attributes.length === 0;
return this.branches.length > 0
|| this.attributes.length > 0;
}
isEmpty() {

View File

@@ -24,7 +24,7 @@ async function getRenderedContent(note) {
.attr("src", `api/images/${note.noteId}/${note.title}`)
.css("max-width", "100%");
}
else if (type === 'file') {
else if (type === 'file' || type === 'pdf') {
function getFileUrl() {
return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
}
@@ -47,19 +47,21 @@ async function getRenderedContent(note) {
// open doesn't work for protected notes since it works through browser which isn't in protected session
$openButton.toggle(!note.isProtected);
$rendered = $('<div>');
$rendered = $('<div style="display: flex; flex-direction: column; height: 100%;">');
if (note.mime === 'application/pdf' && utils.isElectron()) {
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; height: 100%; flex-grow: 100;"></iframe>');
if (type === 'pdf') {
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; flex-grow: 100;"></iframe>');
$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + note.noteId + "/open"));
$rendered.append($pdfPreview);
}
$rendered
.append($downloadButton)
.append(' &nbsp; ')
.append($openButton);
$rendered.append(
$("<div>")
.append($downloadButton)
.append(' &nbsp; ')
.append($openButton)
);
}
else if (type === 'render') {
$rendered = $('<div>');
@@ -90,6 +92,10 @@ async function getRenderedContent(note) {
function getRenderingType(note) {
let type = note.type;
if (type === 'file' && note.mime === 'application/pdf') {
type = 'pdf';
}
if (note.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
@@ -104,4 +110,4 @@ function getRenderingType(note) {
export default {
getRenderedContent
};
};

View File

@@ -273,6 +273,12 @@ class TreeCache {
async getBranchId(parentNoteId, childNoteId) {
const child = await this.getNote(childNoteId);
if (!child) {
console.error(`Could not find branchId for parent=${parentNoteId}, child=${childNoteId} since child does not exist`);
return null;
}
return child.parentToBranch[parentNoteId];
}
@@ -282,6 +288,13 @@ class TreeCache {
async getNoteComplement(noteId) {
if (!this.noteComplementPromises[noteId]) {
this.noteComplementPromises[noteId] = server.get('notes/' + noteId).then(row => new NoteComplement(row));
// we don't want to keep large payloads forever in memory so we clean that up quite quickly
// this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components)
// this is also a work around for missing invalidation after change
this.noteComplementPromises[noteId].then(
() => setTimeout(() => this.noteComplementPromises[noteId] = null, 1000)
);
}
return await this.noteComplementPromises[noteId];

View File

@@ -328,6 +328,9 @@ function dynamicRequire(moduleName) {
}
function timeLimit(promise, limitMs) {
// better stack trace if created outside of promise
const error = new Error('Process exceeded time limit ' + limitMs);
return new Promise((res, rej) => {
let resolved = false;
@@ -339,7 +342,7 @@ function timeLimit(promise, limitMs) {
setTimeout(() => {
if (!resolved) {
rej(new Error('Process exceeded time limit ' + limitMs));
rej(error);
}
}, limitMs);
});

View File

@@ -1,4 +1,5 @@
import CollapsibleWidget from "../collapsible_widget.js";
import treeCache from "../../services/tree_cache.js";
let linkMapContainerIdCtr = 1;
@@ -89,5 +90,19 @@ export default class LinkMapWidget extends CollapsibleWidget {
if (loadResults.getAttributes().find(attr => attr.type === 'relation' && (attr.noteId === this.noteId || attr.value === this.noteId))) {
this.noteSwitched();
}
const changedNoteIds = loadResults.getNoteIds();
if (changedNoteIds.length > 0) {
const $linkMapContainer = this.$body.find('.link-map-container');
for (const noteId of changedNoteIds) {
const note = treeCache.notes[noteId];
if (note) {
$linkMapContainer.find(`a[data-note-path="${noteId}"]`).text(note.title);
}
}
}
}
}

View File

@@ -79,6 +79,7 @@ const TPL = `
<span class="slider checked"></span>
</span>
</div>
<a data-trigger-command="findInText" class="dropdown-item">Search in note <kbd data-command="findInText"></a>
<a data-trigger-command="showNoteRevisions" class="dropdown-item show-note-revisions-button">Revisions</a>
<a data-trigger-command="showAttributes" class="dropdown-item show-attributes-button"><kbd data-command="showAttributes"></kbd> Attributes</a>
<a data-trigger-command="showLinkMap" class="dropdown-item show-link-map-button"><kbd data-command="showLinkMap"></kbd> Link map</a>
@@ -140,4 +141,4 @@ export default class NoteActionsWidget extends TabAwareWidget {
this.refresh();
}
}
}
}

View File

@@ -331,4 +331,9 @@ export default class NoteDetailWidget extends TabAwareWidget {
saveSelection: true
});
}
// used by cutToNote in CKEditor build
async saveNoteDetailNowCommand() {
await this.spacedUpdate.updateNowIfNecessary();
}
}

View File

@@ -32,6 +32,7 @@ const TPL = `
.tree {
height: 100%;
overflow: auto;
padding-bottom: 20px;
}
.refresh-search-button {
@@ -303,10 +304,13 @@ export default class NoteTreeWidget extends TabAwareWidget {
this.$tree.fancytree({
titlesTabbable: true,
autoScroll: true,
keyboard: false, // we takover keyboard handling in the hotkeys plugin
extensions: utils.isMobile() ? ["dnd5", "clones"] : ["hotkeys", "dnd5", "clones"],
source: treeData,
scrollOfs: {
top: 100,
bottom: 100
},
scrollParent: this.$tree,
minExpandLevel: 2, // root can't be collapsed
click: (event, data) => {

View File

@@ -20,8 +20,8 @@ const TPL = `
}
</style>
<button class="hide-left-pane-button btn btn-sm icon-button bx bx-chevrons-left" title="Show sidebar"></button>
<button class="show-left-pane-button btn btn-sm icon-button bx bx-chevrons-right" title="Hide sidebar"></button>
<button class="hide-left-pane-button btn btn-sm icon-button bx bx-chevrons-left" title="Hide sidebar"></button>
<button class="show-left-pane-button btn btn-sm icon-button bx bx-chevrons-right" title="Show sidebar"></button>
<button class="hide-right-pane-button btn btn-sm icon-button bx bx-chevrons-right" title="Hide sidebar"></button>
<button class="show-right-pane-button btn btn-sm icon-button bx bx-chevrons-left" title="Show sidebar"></button>

View File

@@ -36,10 +36,10 @@ export default class AbstractTextTypeWidget extends TypeWidget {
.append($link)
);
const {renderedContent} = await noteContentRenderer.getRenderedContent(note);
const {renderedContent, type} = await noteContentRenderer.getRenderedContent(note);
$el.append(
$('<div class="include-note-content">')
$(`<div class="include-note-content type-${type}">`)
.append(renderedContent)
);
}

View File

@@ -121,10 +121,10 @@ export default class FileTypeWidget extends TypeWidget {
this.$pdfPreview.attr('src', '').empty().hide();
if (noteComplement.content) {
this.$previewContent.show();
this.$previewContent.show().scrollTop(0);
this.$previewContent.text(noteComplement.content);
}
else if (note.mime === 'application/pdf' && utils.isElectron()) {
else if (note.mime === 'application/pdf') {
this.$pdfPreview.show();
this.$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + this.noteId + "/open"));
}

View File

@@ -0,0 +1,14 @@
{
"name": "Trilium",
"short_name": "Trilium",
"theme_color": "DarkGray",
"background_color": "DarkGray",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [{
"src": "images/app-icons/ios/apple-touch-icon.png",
"sizes": "180x180",
"type": "image/png"
}]
}

View File

@@ -693,11 +693,23 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
overflow: auto;
}
.include-note.box-size-small .include-note-content.type-pdf {
height: 10em; /* PDF is rendered in iframe and must be sized absolutely */
}
.include-note.box-size-medium .include-note-content {
max-height: 20em;
overflow: auto;
}
.include-note.box-size-medium .include-note-content.type-pdf {
height: 20em; /* PDF is rendered in iframe and must be sized absolutely */
}
.include-note.box-size-full .include-note-content.type-pdf {
height: 50em; /* PDF is rendered in iframe and it's not possible to put full height so at least a large height */
}
.alert-warning {
color: var(--main-text-color) !important;
background-color: var(--accented-background-color) !important;

View File

@@ -49,7 +49,7 @@ async function loginSync(req) {
return {
sourceId: sourceIdService.getCurrentSourceId(),
maxSyncId: await sql.getValue("SELECT MAX(id) FROM sync WHERE isSynced = 1")
maxSyncId: await sql.getValue("SELECT COALESCE(MAX(id), 0) FROM sync WHERE isSynced = 1")
};
}

View File

@@ -17,8 +17,9 @@ async function getNote(req) {
if (note.isStringNote()) {
note.content = await note.getContent();
if (note.type === 'file') {
note.content = note.content.substr(0, 10000);
if (note.type === 'file' && note.content.length > 10000) {
note.content = note.content.substr(0, 10000)
+ `\r\n\r\n... and ${note.content.length - 10000} more characters.`;
}
}

View File

@@ -50,7 +50,7 @@ async function getStats() {
async function checkSync() {
return {
entityHashes: await contentHashService.getEntityHashes(),
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync WHERE isSynced = 1')
maxSyncId: await sql.getValue('SELECT COALESCE(MAX(id), 0) FROM sync WHERE isSynced = 1')
};
}
@@ -124,7 +124,7 @@ async function getChanged(req) {
const ret = {
syncs: await syncService.getSyncRecords(syncs),
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync WHERE isSynced = 1')
maxSyncId: await sql.getValue('SELECT COALESCE(MAX(id), 0) FROM sync WHERE isSynced = 1')
};
if (ret.syncs.length > 0) {

View File

@@ -23,7 +23,7 @@ async function index(req, res) {
treeFontSize: parseInt(options.treeFontSize),
detailFontSize: parseInt(options.detailFontSize),
sourceId: await sourceIdService.generateSourceId(),
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
maxSyncIdAtLoad: await sql.getValue("SELECT COALESCE(MAX(id), 0) FROM sync"),
instanceName: config.General ? config.General.instanceName : null,
appCssNoteIds: await getAppCssNoteIds(),
isDev: env.isDev(),

View File

@@ -4,7 +4,7 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 158;
const APP_DB_VERSION = 159;
const SYNC_VERSION = 14;
const CLIPPER_PROTOCOL_VERSION = "1.0";
@@ -16,4 +16,4 @@ module.exports = {
buildRevision: build.buildRevision,
dataDirectory: TRILIUM_DATA_DIR,
clipperProtocolVersion: CLIPPER_PROTOCOL_VERSION
};
};

View File

@@ -1 +1 @@
module.exports = { buildDate:"2020-07-11T23:58:59+02:00", buildRevision: "08edc521e48ea7c6de96c19290134b6552844313" };
module.exports = { buildDate:"2020-08-27T23:58:58+02:00", buildRevision: "dc288fb18c7622f6e08c574888dc0e8c90e544c2" };

View File

@@ -98,7 +98,17 @@ async function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSw
async function shrinkImage(buffer, originalName) {
// we do resizing with max (100) quality which will be trimmed during optimization step next
const resizedImage = await resize(buffer, 100);
let resizedImage;
try {
resizedImage = await resize(buffer, 100);
}
catch (e) {
log.error("Failed to resize image '" + originalName + "'\nStack: " + e.stack);
resizedImage = buffer;
}
let finalImageBuffer;
const jpegQuality = await optionService.getOptionInt('imageJpegQuality');
@@ -107,7 +117,15 @@ async function shrinkImage(buffer, originalName) {
finalImageBuffer = await optimize(resizedImage, jpegQuality);
} catch (e) {
log.error("Failed to optimize image '" + originalName + "'\nStack: " + e.stack);
finalImageBuffer = await resize(buffer, jpegQuality);
try {
finalImageBuffer = await resize(buffer, jpegQuality);
}
catch (e) {
log.error("Failed to resize image '" + originalName + "'\nStack: " + e.stack);
finalImageBuffer = buffer;
}
}
// if resizing & shrinking did not help with size then save the original

View File

@@ -279,20 +279,30 @@ const downloadImagePromises = {};
function replaceUrl(content, url, imageNote) {
const quotedUrl = utils.quoteRegex(url);
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "g"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
}
async function downloadImages(noteId, content) {
const re = /<img[^>]*?\ssrc=['"]([^'">]+)['"]/ig;
let match;
const imageRe = /<img[^>]*?\ssrc=['"]([^'">]+)['"]/ig;
let imageMatch;
const origContent = content;
while (imageMatch = imageRe.exec(content)) {
const url = imageMatch[1];
const inlineImageMatch = /^data:image\/[a-z]+;base64,/.exec(url);
while (match = re.exec(origContent)) {
const url = match[1];
if (inlineImageMatch) {
const imageBase64 = url.substr(inlineImageMatch[0].length);
const imageBuffer = Buffer.from(imageBase64, 'base64');
if (!url.includes('api/images/')
// this is and exception for the web clipper's "imageId"
const imageService = require('../services/image');
const {note} = await imageService.saveImage(noteId, imageBuffer, "inline image", true);
content = content.substr(0, imageMatch.index)
+ `<img src="api/images/${note.noteId}/${note.title}"`
+ content.substr(imageMatch.index + imageMatch[0].length);
}
else if (!url.includes('api/images/')
// this is an exception for the web clipper's "imageId"
&& (url.length !== 20 || url.toLowerCase().startsWith('http'))) {
if (url in imageUrlToNoteIdMapping) {
@@ -303,7 +313,6 @@ async function downloadImages(noteId, content) {
}
else {
content = replaceUrl(content, url, imageNote);
continue;
}
}
@@ -315,7 +324,6 @@ async function downloadImages(noteId, content) {
imageUrlToNoteIdMapping[url] = existingImage.noteId;
content = replaceUrl(content, url, existingImage);
continue;
}

View File

@@ -51,7 +51,7 @@ async function initNotSyncedOptions(initialized, startNotePath = 'root', opts =
await optionService.createOption('theme', opts.theme || 'white', false);
await optionService.createOption('syncServerHost', opts.syncServerHost || '', false);
await optionService.createOption('syncServerTimeout', '5000', false);
await optionService.createOption('syncServerTimeout', '60000', false);
await optionService.createOption('syncProxy', opts.syncProxy || '', false);
}
@@ -116,4 +116,4 @@ module.exports = {
initSyncedOptions,
initNotSyncedOptions,
initStartupOptions
};
};

View File

@@ -84,10 +84,11 @@ function exec(opts) {
}
async function getImage(imageUrl) {
const proxyConf = await syncOptions.getSyncProxy();
const opts = {
method: 'GET',
url: imageUrl,
proxy: await syncOptions.getSyncProxy()
proxy: proxyConf !== "noproxy" ? proxyConf : null
};
const client = getClient(opts);

View File

@@ -123,7 +123,7 @@ async function doLogin() {
}
async function pullSync(syncContext) {
let appliedPulls = 0;
let atLeastOnePullApplied = false;
while (true) {
const lastSyncedPull = await getLastSyncedPull();
@@ -132,6 +132,9 @@ async function pullSync(syncContext) {
const startDate = Date.now();
const resp = await syncRequest(syncContext, 'GET', changesUri);
const pulledDate = Date.now();
stats.outstandingPulls = resp.maxSyncId - lastSyncedPull;
if (stats.outstandingPulls < 0) {
@@ -147,10 +150,10 @@ async function pullSync(syncContext) {
await sql.transactional(async () => {
for (const {sync, entity} of rows) {
if (!sourceIdService.isLocalSourceId(sync.sourceId)) {
if (appliedPulls === 0 && sync.entity !== 'recent_notes') { // send only for first
if (!atLeastOnePullApplied && sync.entity !== 'recent_notes') { // send only for first
ws.syncPullInProgress();
appliedPulls++;
atLeastOnePullApplied = true;
}
await syncUpdateService.updateEntity(sync, entity, syncContext.sourceId);
@@ -162,10 +165,10 @@ async function pullSync(syncContext) {
await setLastSyncedPull(rows[rows.length - 1].sync.id);
});
log.info(`Pulled and updated ${rows.length} changes from ${changesUri} in ${Date.now() - startDate}ms`);
log.info(`Pulled ${rows.length} changes starting at syncId=${lastSyncedPull} in ${pulledDate - startDate}ms and applied them in ${Date.now() - pulledDate}ms, ${stats.outstandingPulls} outstanding pulls`);
}
if (appliedPulls > 0) {
if (atLeastOnePullApplied) {
ws.syncPullFinished();
}
@@ -365,7 +368,7 @@ async function updatePushStats() {
}
async function getMaxSyncId() {
return await sql.getValue('SELECT MAX(id) FROM sync');
return await sql.getValue('SELECT COALESCE(MAX(id), 0) FROM sync');
}
sqlInit.dbReady.then(async () => {

View File

@@ -82,7 +82,8 @@ async function fillSyncRows(entityName, entityPrimaryKey, condition = '') {
entityName: entityName,
entityId: entityId,
sourceId: "SYNC_FILL",
utcSyncDate: dateUtils.utcNowDateTime()
utcSyncDate: dateUtils.utcNowDateTime(),
isSynced: true
});
}
}
@@ -127,4 +128,4 @@ module.exports = {
fillAllSyncRows,
addEntitySyncsForSector,
getMaxSyncId: () => maxSyncId
};
};

View File

@@ -159,7 +159,11 @@ function getContentDisposition(filename) {
return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`;
}
const STRING_MIME_TYPES = ["application/x-javascript", "image/svg+xml"];
const STRING_MIME_TYPES = [
"application/javascript",
"application/x-javascript",
"image/svg+xml"
];
function isStringNote(type, mime) {
return ["text", "code", "relation-map", "search"].includes(type)
@@ -218,6 +222,9 @@ function formatDownloadTitle(filename, type, mime) {
}
function timeLimit(promise, limitMs) {
// better stack trace if created outside of promise
const error = new Error('Process exceeded time limit ' + limitMs);
return new Promise((res, rej) => {
let resolved = false;
@@ -229,7 +236,7 @@ function timeLimit(promise, limitMs) {
setTimeout(() => {
if (!resolved) {
rej(new Error('Process exceeded time limit ' + limitMs));
rej(error);
}
}, limitMs);
});

View File

@@ -5,7 +5,7 @@
<link rel="shortcut icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Trilium Notes</title>
<link rel="apple-touch-icon" sizes="180x180" href="images/app-icons/ios/apple-touch-icon.png">
<link rel="manifest" href="manifest.webmanifest">
<style>
.lds-roller {
@@ -140,4 +140,4 @@
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
</body>
</html>
</html>