Compare commits

..

51 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
zadam 443f389d73 release 0.43.2 2020-07-11 23:58:59 +02:00
zadam 08edc521e4 fix visibility of hyper-link in repeatedly opened dialog 2020-07-11 23:50:38 +02:00
zadam f54f6d09b0 fix declaring global variables on the setup page, closes #1071 2020-07-11 23:19:24 +02:00
zadam 3219441fdf fallback for missing parsed title, closes trilium-web-clipper/issues/16 2020-07-11 23:05:28 +02:00
zadam 1b0a2b41da added tab and shift-tab shortcuts for indent/outdent, closes #978 2020-07-08 21:57:09 +02:00
zadam 582429e762 updated content styles 2020-07-01 21:03:51 +02:00
zadam 238df0fb40 fix SVG single file import as image and not as file, closes #1114 2020-06-24 21:47:50 +02:00
zadam 89356918f1 fix unescaped HTML in the tree node title, closes #1127 2020-06-24 21:07:55 +02:00
zadam 263b65997c hide "type around" controls in printed PDF, closes #1129 2020-06-24 20:44:00 +02:00
zadam 2b757bfccd upgrade ckeditor to 20.0.0 2020-06-24 16:17:39 +02:00
zadam c78ca4c9db fixed triggers to sort children of notes with "sorted" label, closes #1126 2020-06-23 22:03:01 +02:00
zadam a0395e9866 release 0.43.1 2020-06-23 10:11:17 +02:00
zadam 89aa4fbc73 electron 9.0.5 2020-06-23 10:10:59 +02:00
zadam 74a7802088 fix custom resource handler, closes #1125 2020-06-22 23:13:53 +02:00
zadam a89b6711d1 refactored code to not depend on external elements, #1120 2020-06-22 22:28:45 +02:00
zadam b2549b2834 Merge remote-tracking branch 'origin/stable' into stable 2020-06-22 22:00:22 +02:00
zadam 959c4cbe64 removed icon tooltip again 2020-06-22 22:00:08 +02:00
Shon Ramamurthy d03d3603d2 Add optional support for note title tooltips under note tree widget (#1120)
* Add support for note title tooltips under note tree widget

This change adds an option to set the 'tooltip' configuration of the
Fancytree component. This allows tooltips containing the note title to
be displayed when a hover is performed over a note title in the tree
widget.

* Revert DB Upgrade

The db upgrade is reverted as this is not required for options.

* Simplify boolean option comparison

With this change, the existing 'is(key)' method is used to perform
tooltip enable option boolean comparison.

* Display tooltip only on center-pane overlap - Experimental

With this change, a straight-forward method to detect HTML element
overlap has been identified (source:
https://gist.github.com/jtsternberg/c272d7de5b967cec2d3d). It is now
possible to detect whether the center-pane element overlaps with the
Fancytree node title-bar. Using this approach we now have a rough
implementation which only displays a note-title tooltip when there is a
center-pane overlap.

At this stage, this change is experimental and the following needs to be
further addressed,
 - Register the 'mouseenter' event handler in an appropriate place. The
   current placement of this event handler is only for testing.
 - This change is now enabled by default. It needs to be seen whether it
   would still make sense to disable it via an option.

* Remove option to set tooltip

With this change, the tooltip options menu item has been removed as it
becomes relevant to have this feature enabled by default.

* Revert further changes related to the options menu

Further changes are rolled back which was earlier related to the tooltip
options setting. Some of these were missed in the previous commit.

* Remove debug logging

Remove debug logging and unnecessary line breaks.

* Move note-title tooltip handler under note_tree.js

With this change, we move the definition for the note-title tooltip
handler inside 'note_tree.js'. Registration is done inside
'side_pane_toggles.js' as we would need the handler to detect the
'center-pane' element first before detecting collisions.
2020-06-22 21:58:58 +02:00
zadam e1c2573778 add tooltip to fancytree node icon, #1120 2020-06-21 12:47:24 +02:00
zadam 2af2b45062 fix keyboard shortcut for print this note, #1118 2020-06-20 00:04:31 +02:00
zadam eabe4775bd fix click handler of links inside included note 2020-06-19 23:20:52 +02:00
zadam da9b321aa0 allow mouse interactions in ckeditor widgets, fixes #1117 2020-06-19 22:24:57 +02:00
zadam c18d8d2d1b add back lost edited notes widget, closes #1115 2020-06-18 08:56:23 +02:00
47 changed files with 489 additions and 263 deletions
BIN
View File
Binary file not shown.
@@ -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)
)
+152 -124
View File
@@ -1,23 +1,53 @@
/*
* !!!!!!! This stylesheet is heavily modified compared to the original for similarity with in-editor look !!!!!!!
* This is used for printing and tar HTML export
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
* CKEditor 5 (v17.0.0) content styles.
* Generated on Fri, 13 Mar 2020 13:27:10 GMT.
.ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
display: none;
}
/*
* 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-highlight-marker-blue: #72cdfd;
--ck-highlight-marker-green: #63f963;
--ck-highlight-marker-pink: #fc7999;
--ck-highlight-marker-yellow: #fdfd77;
--ck-highlight-pen-green: #118800;
--ck-highlight-pen-red: #e91313;
--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%);
--ck-highlight-marker-yellow: hsl(60, 97%, 73%);
--ck-highlight-pen-green: hsl(112, 100%, 27%);
--ck-highlight-pen-red: hsl(0, 85%, 49%);
--ck-image-style-spacing: 1.5em;
--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%;
@@ -32,37 +62,11 @@
.ck-content .image.image_resized > figcaption {
display: block;
}
/* ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* 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/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);
max-width: 50%;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
@@ -79,42 +83,6 @@
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: #fff;
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
@@ -130,38 +98,6 @@
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 1em 0;
display: block;
min-width: 15em;
}
/* ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 1em auto;
display: table;
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border-color: hsl(0, 0%, 75%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
list-style: none;
@@ -229,16 +165,12 @@
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* 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-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 {
@@ -266,17 +198,108 @@
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
border-width: 1px 0 0;
border-style: solid;
border-color: hsl(0, 0%, 37%);
margin: 0;
/* 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 {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 1em auto;
display: table;
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* 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 {
padding: 1em;
color: #353535;
color: hsl(0, 0%, 20.8%);
background: hsla(0, 0%, 78%, 0.3);
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
@@ -293,6 +316,11 @@
padding: 0;
border-radius: 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 */
.ck-content .page-break {
@@ -302,4 +330,4 @@
.ck-content .page-break::after {
display: none;
}
}
}
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.42.7",
"version": "0.43.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -3345,9 +3345,9 @@
}
},
"electron": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.4.tgz",
"integrity": "sha512-QzkeZNAiNB7KxcdoQKSoaiVT/GQdB4Vt0/ZZOuU8tIKABAsni2I7ztiAbUzxcsnQsqEBSfChuPuDQ5A4VbbzPg==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.5.tgz",
"integrity": "sha512-bnL9H48LuQ250DML8xUscsKiuSu+xv5umXbpBXYJ0BfvYVmFfNbG3jCfhrsH7aP6UcQKVxOG1R/oQExd0EFneQ==",
"dev": true,
"requires": {
"@electron/get": "^1.0.1",
@@ -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"
},
+3 -3
View File
@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.43.0-beta",
"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",
@@ -78,7 +78,7 @@
"yazl": "^2.5.1"
},
"devDependencies": {
"electron": "9.0.4",
"electron": "9.0.5",
"electron-builder": "22.6.0",
"electron-packager": "14.2.1",
"electron-rebuild": "1.10.1",
+3 -2
View File
@@ -17,7 +17,6 @@ export async function showDialog(widget) {
$addLinkTitleSettings.toggle(!textTypeWidget.hasSelection());
updateTitleFormGroupVisibility();
$addLinkTitleSettings.find('input[type=radio]').on('change', updateTitleFormGroupVisibility);
// with selection hyper link is implied
@@ -28,6 +27,8 @@ export async function showDialog(widget) {
$addLinkTitleSettings.find("input[value='reference-link']").prop("checked", true);
}
updateTitleFormGroupVisibility();
utils.openDialog($dialog);
$autoComplete.val('').trigger('focus');
@@ -89,4 +90,4 @@ $form.on('submit', () => {
}
return false;
});
});
+10
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) {
@@ -24,6 +24,7 @@ import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.j
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
import SidePaneToggles from "../widgets/side_pane_toggles.js";
import EditedNotesWidget from "../widgets/collapsible_widgets/edited_notes.js";
const RIGHT_PANE_CSS = `
<style>
@@ -143,6 +144,7 @@ export default class DesktopMainWindowLayout {
.hideInZenMode()
.child(new NoteInfoWidget())
.child(new TabCachingWidget(() => new CalendarWidget()))
.child(new TabCachingWidget(() => new EditedNotesWidget()))
.child(new TabCachingWidget(() => new AttributesWidget()))
.child(new TabCachingWidget(() => new LinkMapWidget()))
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
+2
View File
@@ -83,6 +83,8 @@ function setupGlobs() {
$("body").on("click", "a.external", function () {
window.open($(this).attr("href"), '_blank');
return false;
});
}
+2
View File
@@ -164,6 +164,7 @@ $(document).on('mousedown', '.note-detail-text a.reference-link', goToLink);
$(document).on('mousedown', '.note-detail-readonly-text a.reference-link', goToLink);
$(document).on('mousedown', '.note-detail-readonly-text a', goToLink);
$(document).on('mousedown', 'a.ck-link-actions__preview', goToLink);
$(document).on('click', 'section.include-note a', goToLink);
$(document).on('click', 'a.ck-link-actions__preview', e => {
e.preventDefault();
e.stopPropagation();
@@ -175,6 +176,7 @@ $(document).on('contextmenu', '.note-detail-readonly-text a', linkContextMenu);
$(document).on('contextmenu', "a[data-action='note']", linkContextMenu);
$(document).on('contextmenu', ".note-detail-render a", linkContextMenu);
$(document).on('contextmenu', ".note-paths-widget a", linkContextMenu);
$(document).on('contextmenu', "section.include-note a", linkContextMenu);
export default {
getNotePathFromUrl,
+2 -2
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() {
@@ -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
};
};
+13
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];
+16 -2
View File
@@ -64,8 +64,19 @@ function assertArguments() {
}
}
const entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;'
};
function escapeHtml(str) {
return $('<div/>').text(str).html();
return str.replace(/[&<>"'`=\/]/g, s => entityMap[s]);
}
async function stopWatch(what, func) {
@@ -317,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;
@@ -328,7 +342,7 @@ function timeLimit(promise, limitMs) {
setTimeout(() => {
if (!resolved) {
rej(new Error('Process exceeded time limit ' + limitMs));
rej(error);
}
}, limitMs);
});
@@ -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);
}
}
}
}
}
+2 -1
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();
}
}
}
}
+5
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();
}
}
+38 -3
View File
@@ -32,6 +32,7 @@ const TPL = `
.tree {
height: 100%;
overflow: auto;
padding-bottom: 20px;
}
.refresh-search-button {
@@ -249,9 +250,39 @@ export default class NoteTreeWidget extends TabAwareWidget {
this.initialized = this.initFancyTree();
this.setupNoteTitleTooltip();
return this.$widget;
}
setupNoteTitleTooltip() {
// the following will dynamically set tree item's tooltip if the whole item's text is not currently visible
// if the whole text is visible then no tooltip is show since that's unnecessarily distracting
// see https://github.com/zadam/trilium/pull/1120 for discussion
// code inspired by https://gist.github.com/jtsternberg/c272d7de5b967cec2d3d
const isEnclosing = ($container, $sub) => {
const conOffset = $container.offset();
const conDistanceFromTop = conOffset.top + $container.outerHeight(true);
const conDistanceFromLeft = conOffset.left + $container.outerWidth(true);
const subOffset = $sub.offset();
const subDistanceFromTop = subOffset.top + $sub.outerHeight(true);
const subDistanceFromLeft = subOffset.left + $sub.outerWidth(true);
return conDistanceFromTop > subDistanceFromTop
&& conOffset.top < subOffset.top
&& conDistanceFromLeft > subDistanceFromLeft
&& conOffset.left < subOffset.left;
};
this.$tree.on("mouseenter", "span.fancytree-title", e => {
e.currentTarget.title = isEnclosing(this.$tree, $(e.currentTarget))
? ""
: e.currentTarget.innerText;
});
}
get hideArchivedNotes() {
return options.is("hideArchivedNotes_" + this.treeName);
}
@@ -273,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) => {
@@ -747,7 +781,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
utils.assertArguments(notePath);
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
/** @var {FancytreeNode} */
/** @const {FancytreeNode} */
let parentNode = null;
const runPath = await treeService.getRunPath(notePath, logErrors);
@@ -832,13 +866,14 @@ export default class NoteTreeWidget extends TabAwareWidget {
const branch = treeCache.getBranch(node.data.branchId);
const isFolder = this.isFolder(note);
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
node.data.isProtected = note.isProtected;
node.data.noteType = note.type;
node.folder = isFolder;
node.icon = this.getIcon(note, isFolder);
node.extraClasses = this.getExtraClasses(note);
node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
node.title = utils.escapeHtml(title);
if (node.isExpanded() !== branch.isExpanded) {
node.setExpanded(branch.isExpanded, {noEvents: true});
+2 -2
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>
@@ -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)
);
}
+2 -2
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"));
}
+14
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"
}]
}
+12
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;
+6 -2
View File
@@ -65,7 +65,11 @@ async function addClipping(req) {
}
async function createNote(req) {
const {title, content, pageUrl, images, clipType} = req.body;
let {title, content, pageUrl, images, clipType} = req.body;
if (!title || !title.trim()) {
title = "Clipped note from " + pageUrl;
}
log.info(`Creating clipped note from ${pageUrl}`);
@@ -155,4 +159,4 @@ module.exports = {
addClipping,
openNote,
handshake
};
};
+1 -1
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")
};
}
+10 -3
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.`;
}
}
@@ -164,10 +165,16 @@ async function changeTitle(req) {
return [400, `Note ${noteId} is not available for change`];
}
const noteTitleChanged = note.title !== title;
note.title = title;
await note.save();
if (noteTitleChanged) {
await noteService.triggerNoteTitleChanged(note);
}
return note;
}
@@ -189,4 +196,4 @@ module.exports = {
getRelationMap,
changeTitle,
duplicateNote
};
};
+2 -2
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) {
+62 -53
View File
@@ -2,67 +2,76 @@ const repository = require('../services/repository');
const log = require('../services/log');
const fileUploadService = require('./api/files.js');
const scriptService = require('../services/script');
const cls = require('../services/cls');
async function handleRequest(req, res) {
// express puts content after first slash into 0 index element
const path = req.params.path + req.params[0];
const attrs = await repository.getEntities("SELECT * FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
for (const attr of attrs) {
const regex = new RegExp(attr.value);
let match;
try {
match = path.match(regex);
}
catch (e) {
log.error(`Testing path for label ${attr.attributeId}, regex=${attr.value} failed with error ` + e.stack);
continue;
}
if (!match) {
continue;
}
if (attr.name === 'customRequestHandler') {
const note = await attr.getNote();
log.info(`Handling custom request "${path}" with note ${note.noteId}`);
try {
await scriptService.executeNote(note, {
pathParams: match.slice(1),
req,
res
});
}
catch (e) {
log.error(`Custom handler ${note.noteId} failed with ${e.message}`);
res.status(500).send(e.message);
}
}
else if (attr.name === 'customResourceProvider') {
await fileUploadService.downloadNoteFile(attr.noteId, res);
}
else {
throw new Error("Unrecognized attribute name " + attr.name);
}
return; // only first handler is executed
}
const message = `No handler matched for custom ${path} request.`;
log.info(message);
res.status(404).send(message);
}
function register(router) {
// explicitly no CSRF middleware since it's meant to allow integration from external services
router.all('/custom/:path*', async (req, res, next) => {
// express puts content after first slash into 0 index element
const path = req.params.path + req.params[0];
cls.namespace.bindEmitter(req);
cls.namespace.bindEmitter(res);
const attrs = await repository.getEntities("SELECT * FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
for (const attr of attrs) {
const regex = new RegExp(attr.value);
let match;
try {
match = path.match(regex);
}
catch (e) {
log.error(`Testing path for label ${attr.attributeId}, regex=${attr.value} failed with error ` + e.stack);
continue;
}
if (!match) {
continue;
}
if (attr.name === 'customRequestHandler') {
const note = await attr.getNote();
log.info(`Handling custom request "${path}" with note ${note.noteId}`);
try {
await scriptService.executeNote(note, {
pathParams: match.slice(1),
req,
res
});
}
catch (e) {
log.error(`Custom handler ${note.noteId} failed with ${e.message}`);
res.status(500).send(e.message);
}
}
else if (attr.name === 'customResourceProvider') {
await fileUploadService.downloadNoteFile(attr.noteId, res);
}
else {
throw new Error("Unrecognized attribute name " + attr.name);
}
return; // only first handler is executed
}
const message = `No handler matched for custom ${path} request.`;
log.info(message);
res.status(404).send(message);
cls.init(() => handleRequest(req, res));
});
}
module.exports = {
register
};
};
+1 -1
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(),
+2 -2
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
};
};
+1 -1
View File
@@ -1 +1 @@
module.exports = { buildDate:"2020-06-15T23:26:12+02:00", buildRevision: "9791dab97d9e86c4b02ca593198caffd1b72bbfb" };
module.exports = { buildDate:"2020-08-27T23:58:58+02:00", buildRevision: "dc288fb18c7622f6e08c574888dc0e8c90e544c2" };
+1 -2
View File
@@ -3,7 +3,6 @@
const html = require('html');
const repository = require('../repository');
const dateUtils = require('../date_utils');
const zip = require('tar-stream');
const path = require('path');
const mimeTypes = require('mime-types');
const mdService = require('./md');
@@ -444,4 +443,4 @@ ${content}
module.exports = {
exportToZip
};
};
+4 -1
View File
@@ -68,6 +68,9 @@ eventService.subscribe(eventService.ENTITY_CREATED, async ({ entityName, entity
await note.setContent(await targetNote.getContent());
}
else if (entity.type === 'label' && entity.name === 'sorted') {
await treeService.sortNotesAlphabetically(entity.noteId);
}
}
else if (entityName === 'notes') {
await runAttachedRelations(entity, 'runOnNoteCreation', entity);
@@ -135,4 +138,4 @@ eventService.subscribe(eventService.ENTITY_DELETED, async ({ entityName, entity
targetNote.invalidateAttributeCache();
}
});
});
});
+20 -2
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
+2 -2
View File
@@ -24,7 +24,7 @@ async function importSingleFile(taskContext, file, parentNote) {
return await importCodeNote(taskContext, file, parentNote);
}
if (["image/jpeg", "image/gif", "image/png", "image/webp"].includes(mime)) {
if (mime.startsWith("image/")) {
return await importImage(file, parentNote, taskContext);
}
@@ -166,4 +166,4 @@ function getFileNameWithoutExtension(filePath) {
module.exports = {
importSingleFile
};
};
+1 -1
View File
@@ -327,7 +327,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
{
actionName: "printActiveNote",
defaultShortcuts: [],
scope: "note-detail"
scope: "window"
},
{
actionName: "runActiveNote",
+20 -11
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;
}
@@ -778,5 +786,6 @@ module.exports = {
protectNoteRecursively,
scanForLinks,
duplicateNote,
getUndeletedParentBranches
getUndeletedParentBranches,
triggerNoteTitleChanged
};
+2 -2
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
};
};
+2 -1
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);
+9 -6
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 () => {
+3 -2
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
};
};
+9 -2
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);
});
+2 -2
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>
+3 -2
View File
@@ -193,10 +193,11 @@
</div>
<script type="text/javascript">
const glob = {
window.glob = {
sourceId: ''
};
const syncInProgress = <%= syncInProgress ? 'true' : 'false' %>;
window.syncInProgress = <%= syncInProgress ? 'true' : 'false' %>;
</script>
<!-- Required for correct loading of scripts in Electron -->