Compare commits

..

46 Commits

Author SHA1 Message Date
zadam 7f9bcc162e release 0.44.3-beta 2020-09-19 23:45:46 +02:00
zadam 27052143ab fix placement of "Edit" button on read only notes 2020-09-19 23:44:30 +02:00
zadam 50d3ddbed5 added remove formatting plugin, closes #1239 2020-09-19 23:43:50 +02:00
zadam 7ee50113d7 fix export root note 2020-09-19 22:47:14 +02:00
zadam 07bd5129ca similarity tweaks 2020-09-19 21:59:01 +02:00
zadam ec7021b436 added debugModeEnabled option 2020-09-18 23:22:28 +02:00
zadam c75439a4b5 similarity tweaks 2020-09-18 21:47:59 +02:00
zadam d21cb78baa fix wrong dateLastEdited timestamp, closes #1254 2020-09-18 21:47:15 +02:00
zadam c371873b7e support unicode in button names, closes #1250 2020-09-17 14:41:00 +02:00
zadam 6220b02ef0 similarity tweaks 2020-09-17 14:34:10 +02:00
zadam 4e15bc0bb1 similarity tweaks 2020-09-17 09:33:43 +02:00
zadam 2f720df9d2 migration to convert H1 to H2, #1240 2020-09-16 20:51:57 +02:00
zadam d57a303251 fix in ENEX import 2020-09-16 20:32:20 +02:00
zadam 13b9f5231c similar note tweaks 2020-09-16 17:34:48 +02:00
zadam 71ed24344c performance optimizations in similar notes 2020-09-16 14:23:59 +02:00
zadam 0c8a2a23a7 refactor similarity logic 2020-09-16 11:50:47 +02:00
zadam 5738f2a5b3 similar notes tweaks 2020-09-16 11:24:29 +02:00
zadam d345c4850f custom implementation of similar notes algorithm 2020-09-15 22:46:51 +02:00
zadam eeacd8118f similar notes changes 2020-09-15 16:46:03 +02:00
zadam f5216e4799 close note type dropdown after click 2020-09-15 00:13:25 +02:00
zadam 85d13b1d62 resort relationships in tree cache after note reordering 2020-09-14 22:48:20 +02:00
zadam f5f48ef6c4 copy child: attrs also when defined on a template/parent note 2020-09-14 21:08:11 +02:00
zadam f1ed114167 small blink on attr save as a visual hint 2020-09-14 21:07:00 +02:00
zadam 70995a7691 fix broken saved search, closes #1234 2020-09-14 20:00:36 +02:00
zadam a82b8cdb37 release 0.44.2-beta 2020-09-13 23:03:28 +02:00
zadam 33511c0c10 fix hiding similar notes widget by label 2020-09-13 22:47:23 +02:00
zadam 04c93d6736 fix attr detail related notes 2020-09-13 22:23:03 +02:00
zadam 5e11806110 sidebar widgets can be now disabled with labels 2020-09-13 21:59:31 +02:00
zadam 1e1709ca6a mobile layout fixes + fix mobile delete note 2020-09-13 21:12:22 +02:00
zadam 37602cfcae small tweaks 2020-09-13 00:19:50 +02:00
zadam 0ca0996751 no difference in styling when active tree node is in focus or not because focused class is unreliable 2020-09-12 23:06:03 +02:00
zadam 63ebb46049 fix inverse relation detection in relation maps 2020-09-12 22:21:44 +02:00
zadam 847766b434 fancytree 2.37.0 2020-09-12 21:25:47 +02:00
zadam 056c40c0d0 similar notes algorithm tweaks (WIP) 2020-09-10 21:01:46 +02:00
zadam bff5b015ea wrap note autocomplete for very long note path titles, closes #1221 2020-09-09 20:49:44 +02:00
zadam ee4ce3ffd8 display unhoist button on the left of the note title so that it's always visible, #1223 2020-09-08 23:25:21 +02:00
zadam a73733d0fc renamed ParsingContext to SearchContext 2020-09-08 21:47:37 +02:00
zadam 0c5f842626 improvements to note tooltip - include (some) notePath and attributes 2020-09-08 21:45:07 +02:00
zadam 8b4cf474bd cleanup/fixes of label/relation definition usages in the old style 2020-09-08 20:42:50 +02:00
zadam 80f269d844 moved similar notes from sidebar to the center pane 2020-09-07 23:35:41 +02:00
zadam 75c9db6432 fixed search API and its default settings to include archived notes 2020-09-07 22:11:16 +02:00
zadam 69e36d2677 refactoring for archived notes handling in note cache 2020-09-07 00:05:01 +02:00
zadam eb4d86f41d changed presentation of similar notes to "tag cloud" style 2020-09-06 22:48:52 +02:00
zadam b0bf88c487 fix storing dateLastEdited timestamp in note revisions 2020-09-06 22:04:11 +02:00
zadam 032ffa8367 fix printing port to console #1219 2020-09-06 20:58:26 +02:00
zadam 7400c91a4f missing space in inherited attribute list, fixes #1217 2020-09-06 20:53:59 +02:00
75 changed files with 1622 additions and 839 deletions
+2
View File
@@ -13,6 +13,8 @@ echo "Copying required linux-x64 binaries"
cp -r bin/better-sqlite3/linux-desktop-better_sqlite3.node $SRC_DIR/node_modules/better-sqlite3/build/Release/better_sqlite3.node
echo "Packaging linux x64 electron build"
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
BUILD_DIR=./dist/trilium-linux-x64
+2
View File
@@ -13,6 +13,8 @@ cp -r bin/better-sqlite3/mac-better_sqlite3.node $SRC_DIR/node_modules/better-sq
rm -r $SRC_DIR/src/public/app-dist/*.mobile.*
echo "Packaging mac x64 electron build"
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=images/app-icons/mac/icon.icns
BUILD_DIR=./dist/trilium-mac-x64
+2
View File
@@ -13,6 +13,8 @@ cp -r bin/better-sqlite3/win-better_sqlite3.node $SRC_DIR/node_modules/better-sq
rm -r $SRC_DIR/src/public/app-dist/*.mobile.*
echo "Packaging windows x64 electron build"
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=win32 --arch=x64 --overwrite --icon=images/app-icons/win/icon.ico
BUILD_DIR=./dist/trilium-windows-x64
+1
View File
@@ -31,6 +31,7 @@ cp webpack-* $DIR/
# cleanup of useless files in dependencies
rm -r $DIR/node_modules/image-q/demo
rm -r $DIR/node_modules/better-sqlite3/Release
rm -r $DIR/node_modules/better-sqlite3/deps/sqlite3.tar.gz
rm -r $DIR/node_modules/@jimp/plugin-print/fonts
rm -r $DIR/node_modules/jimp/browser
@@ -0,0 +1 @@
UPDATE attributes SET name = 'archived' WHERE name = 'hideInAutocomplete';
+18
View File
@@ -0,0 +1,18 @@
const repository = require('../../src/services/repository');
module.exports = () => {
for (const note of repository.getEntities("SELECT * FROM notes WHERE type = 'text' AND isProtected = 0")) {
try {
let content = note.getContent();
content = content
.replace(/<h1/ig, "<h2")
.replace(/<\/h1/ig, "</h2");
note.setContent(content);
}
catch (e) {
console.log(`Changing note content for note ${note.noteId} failed with: ${e.message} ${e.stack}`);
}
}
};
+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
+129 -83
View File
@@ -1,4 +1,4 @@
/*! jQuery Fancytree Plugin - 2.36.0 - 2020-07-15T20:15:15Z
/*! jQuery Fancytree Plugin - 2.37.0 - 2020-09-11T18:58:08Z
* https://github.com/mar10/fancytree
* Copyright (c) 2020 Martin Wendt; Licensed MIT
*/
@@ -1365,8 +1365,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
/** Core Fancytree module.
@@ -5763,6 +5763,12 @@ var uniqueId = $.fn.extend( {
dataType: source.dataType,
}
);
if (res.error) {
tree.warn(
"postProcess returned error:",
res
);
}
} catch (e) {
res = {
error: e,
@@ -6742,6 +6748,13 @@ var uniqueId = $.fn.extend( {
// node.debug("nodeSetExpanded(" + flag + ")");
if ($(node.li).hasClass(opts._classNames.animating)) {
node.warn(
"setExpanded(" + flag + ") while animating: ignored."
);
return _getRejectedPromise(node, ["recursion"]);
}
if ((node.expanded && flag) || (!node.expanded && !flag)) {
// Nothing to do
// node.debug("nodeSetExpanded(" + flag + "): nothing to do");
@@ -8057,7 +8070,7 @@ var uniqueId = $.fn.extend( {
{
/** Version number `"MAJOR.MINOR.PATCH"`
* @type {string} */
version: "2.36.0", // Set to semver by 'grunt release'
version: "2.37.0", // Set to semver by 'grunt release'
/** @type {string}
* @description `"production" for release builds` */
buildType: "production", // Set to 'production' by 'grunt build'
@@ -8692,8 +8705,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
// To keep the global namespace clean, we wrap everything in a closure.
@@ -8812,7 +8825,7 @@ var uniqueId = $.fn.extend( {
// Every extension must be registered by a unique name.
name: "childcounter",
// Version information should be compliant with [semver](http://semver.org)
version: "2.36.0",
version: "2.37.0",
// Extension specific options and their defaults.
// This options will be available as `tree.options.childcounter.hideExpanded`
@@ -8923,8 +8936,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -9282,7 +9295,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "clones",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
highlightActiveClones: true, // set 'fancytree-active-clone' on active clones and all peers
@@ -9444,8 +9457,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
/*
@@ -10019,9 +10032,10 @@ var uniqueId = $.fn.extend( {
if (dndOpts.multiSource === false) {
SOURCE_NODE_LIST = [node];
} else if (dndOpts.multiSource === true) {
SOURCE_NODE_LIST = tree.getSelectedNodes();
if (!node.isSelected()) {
SOURCE_NODE_LIST.unshift(node);
if (node.isSelected()) {
SOURCE_NODE_LIST = tree.getSelectedNodes();
} else {
SOURCE_NODE_LIST = [node];
}
} else {
SOURCE_NODE_LIST = dndOpts.multiSource(node, data);
@@ -10462,13 +10476,14 @@ var uniqueId = $.fn.extend( {
$.ui.fancytree.registerExtension({
name: "dnd5",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
dropMarkerParent: document.body, // Root Container used for drop marker (could be a shadow root)
// #1021 `document.body` is not available yet
dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
// dropEffect: "auto", // 'copy'|'link'|'move'|'auto'(calculate from `effectAllowed`+modifier keys) or callback(node, data) that returns such string.
@@ -10597,8 +10612,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -10805,7 +10820,7 @@ var uniqueId = $.fn.extend( {
}
// Set keyboard focus, even if setFocus() claims 'nothing to do'
$(tree.$container).focus({ preventScroll: true });
tree.$container.get(0).focus({ preventScroll: true });
eventData.input = null;
instOpts.close.call(node, { type: "close" }, eventData);
return true;
@@ -10890,7 +10905,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "edit",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
adjustWidthOfs: 4, // null: don't adjust input size to content
@@ -11001,8 +11016,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -11340,7 +11355,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "filter",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
autoApply: true, // Re-apply last filter if lazy data is loaded
@@ -11460,8 +11475,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -11617,13 +11632,32 @@ var uniqueId = $.fn.extend( {
},
};
function setIcon(span, baseClass, opts, type) {
function setIcon(node, span, baseClass, opts, type) {
var map = opts.map,
icon = map[type],
$span = $(span),
$counter = $span.find(".fancytree-childcounter"),
setClass = baseClass + " " + (map._addClass || "");
// #871 Allow a callback
if ($.isFunction(icon)) {
icon = icon.call(this, node, span, type);
}
// node.debug( "setIcon(" + baseClass + ", " + type + "): " + "oldIcon" + " -> " + icon );
// #871: propsed this, but I am not sure how robust this is, e.g.
// the prefix (fas, far) class changes are not considered?
// if (span.tagName === "svg" && opts.preset === "awesome5") {
// // fa5 script converts <i> to <svg> so call a specific handler.
// var oldIcon = "fa-" + $span.data("icon");
// // node.debug( "setIcon(" + baseClass + ", " + type + "): " + oldIcon + " -> " + icon );
// if (typeof oldIcon === "string") {
// $span.removeClass(oldIcon);
// }
// if (typeof icon === "string") {
// $span.addClass(icon);
// }
// return;
// }
if (typeof icon === "string") {
// #883: remove inner html that may be added by prev. mode
span.innerHTML = "";
@@ -11644,7 +11678,7 @@ var uniqueId = $.fn.extend( {
$.ui.fancytree.registerExtension({
name: "glyph",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
preset: null, // 'awesome3', 'awesome4', 'bootstrap3', 'material'
@@ -11681,7 +11715,7 @@ var uniqueId = $.fn.extend( {
if (node.isRootNode()) {
return res;
}
span = $span.children("span.fancytree-expander").get(0);
span = $span.children(".fancytree-expander").get(0);
if (span) {
// if( node.isLoading() ){
// icon = "loading";
@@ -11695,15 +11729,15 @@ var uniqueId = $.fn.extend( {
icon = "noExpander";
}
// span.className = "fancytree-expander " + map[icon];
setIcon(span, "fancytree-expander", opts, icon);
setIcon(node, span, "fancytree-expander", opts, icon);
}
if (node.tr) {
span = $("td", node.tr)
.find("span.fancytree-checkbox")
.find(".fancytree-checkbox")
.get(0);
} else {
span = $span.children("span.fancytree-checkbox").get(0);
span = $span.children(".fancytree-checkbox").get(0);
}
if (span) {
checkbox = FT.evalOption("checkbox", node, node, opts, false);
@@ -11713,6 +11747,7 @@ var uniqueId = $.fn.extend( {
) {
icon = node.selected ? "radioSelected" : "radio";
setIcon(
node,
span,
"fancytree-checkbox fancytree-radio",
opts,
@@ -11726,13 +11761,13 @@ var uniqueId = $.fn.extend( {
? "checkboxUnknown"
: "checkbox";
// span.className = "fancytree-checkbox " + map[icon];
setIcon(span, "fancytree-checkbox", opts, icon);
setIcon(node, span, "fancytree-checkbox", opts, icon);
}
}
// Standard icon (note that this does not match .fancytree-custom-icon,
// that might be set by opts.icon callbacks)
span = $span.children("span.fancytree-icon").get(0);
span = $span.children(".fancytree-icon").get(0);
if (span) {
if (node.statusNodeType) {
icon = node.statusNodeType; // loading, error
@@ -11744,7 +11779,7 @@ var uniqueId = $.fn.extend( {
} else {
icon = node.expanded ? "docOpen" : "doc";
}
setIcon(span, "fancytree-icon", opts, icon);
setIcon(node, span, "fancytree-icon", opts, icon);
}
return res;
},
@@ -11762,9 +11797,9 @@ var uniqueId = $.fn.extend( {
status === "nodata"
) {
if (node.parent) {
span = $("span.fancytree-expander", node.span).get(0);
span = $(".fancytree-expander", node.span).get(0);
if (span) {
setIcon(span, "fancytree-expander", opts, status);
setIcon(node, span, "fancytree-expander", opts, status);
}
} else {
//
@@ -11772,10 +11807,10 @@ var uniqueId = $.fn.extend( {
".fancytree-statusnode-" + status,
node[this.nodeContainerAttrName]
)
.find("span.fancytree-icon")
.find(".fancytree-icon")
.get(0);
if (span) {
setIcon(span, "fancytree-icon", opts, status);
setIcon(node, span, "fancytree-icon", opts, status);
}
}
}
@@ -11798,8 +11833,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -11916,7 +11951,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "gridnav",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
autofocusInput: false, // Focus first embedded input if node gets activated
@@ -12023,8 +12058,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -12053,7 +12088,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "multi",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
allowNoSelect: false, //
@@ -12155,8 +12190,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -12179,38 +12214,49 @@ var uniqueId = $.fn.extend( {
* Private functions and variables
*/
var cookieStore = null,
localStorageStore = window.localStorage
? {
get: function(key) {
return window.localStorage.getItem(key);
},
set: function(key, value) {
window.localStorage.setItem(key, value);
},
remove: function(key) {
window.localStorage.removeItem(key);
},
}
: null,
sessionStorageStore = window.sessionStorage
? {
get: function(key) {
return window.sessionStorage.getItem(key);
},
set: function(key, value) {
window.sessionStorage.setItem(key, value);
},
remove: function(key) {
window.sessionStorage.removeItem(key);
},
}
: null,
localStorageStore = null,
sessionStorageStore = null,
_assert = $.ui.fancytree.assert,
ACTIVE = "active",
EXPANDED = "expanded",
FOCUS = "focus",
SELECTED = "selected";
// Accessing window.xxxStorage may raise security exceptions (see #1022)
try {
_assert(window.localStorage && window.localStorage.getItem);
localStorageStore = {
get: function(key) {
return window.localStorage.getItem(key);
},
set: function(key, value) {
window.localStorage.setItem(key, value);
},
remove: function(key) {
window.localStorage.removeItem(key);
},
};
} catch (e) {
$.ui.fancytree.warn("Could not access window.localStorage", e);
}
try {
_assert(window.sessionStorage && window.sessionStorage.getItem);
sessionStorageStore = {
get: function(key) {
return window.sessionStorage.getItem(key);
},
set: function(key, value) {
window.sessionStorage.setItem(key, value);
},
remove: function(key) {
window.sessionStorage.removeItem(key);
},
};
} catch (e) {
$.ui.fancytree.warn("Could not access window.sessionStorage", e);
}
if (typeof Cookies === "function") {
// Assume https://github.com/js-cookie/js-cookie
cookieStore = {
@@ -12356,7 +12402,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "persist",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
cookieDelimiter: "~",
@@ -12649,8 +12695,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -12733,7 +12779,7 @@ var uniqueId = $.fn.extend( {
$.ui.fancytree.registerExtension({
name: "table",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
checkboxColumnIdx: null, // render the checkboxes into the this column index (default: nodeColumnIdx)
@@ -13199,8 +13245,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -13223,7 +13269,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "themeroller",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
activeClass: "ui-state-active", // Class added to active node
@@ -13319,8 +13365,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
*/
(function(factory) {
@@ -13450,7 +13496,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "wide",
version: "2.36.0",
version: "2.37.0",
// Default options for this extension.
options: {
iconWidth: null, // Adjust this if @fancy-icon-width != "16px"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -13,8 +13,8 @@
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.36.0
* @date 2020-07-15T20:15:15Z
* @version 2.37.0
* @date 2020-09-11T18:58:08Z
******************************************************************************/
/*------------------------------------------------------------------------------
* Helpers
File diff suppressed because one or more lines are too long
+352 -198
View File
@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.43.4",
"version": "0.44.2-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -35,9 +35,9 @@
},
"dependencies": {
"ajv": {
"version": "6.12.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
"integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
"version": "6.12.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
@@ -649,9 +649,9 @@
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
},
"@types/eslint": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.1.tgz",
"integrity": "sha512-WjKivjZyeL65Qt8HLLGwyhC3NkhoPVgCvxPcCMb02k8fCSJH1vvDRefXmSHA4U9TcjOj024yCOxFgCPKC0gMXQ==",
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.2.tgz",
"integrity": "sha512-psWuwNXuKR2e6vMU5d2qH0Kqzrb2Zxwk+uBCF2LsyEph+Nex3lFIPMJXwxfGesdtJM2qtjKoCYsyh76K3x9wLg==",
"dev": true,
"requires": {
"@types/estree": "*",
@@ -695,9 +695,9 @@
}
},
"@types/json-schema": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/minimatch": {
@@ -1139,9 +1139,9 @@
}
},
"aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
"integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"requires": {
"clean-stack": "^2.0.0",
@@ -1160,9 +1160,9 @@
}
},
"ajv-keywords": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true
},
"ansi-align": {
@@ -1250,27 +1250,27 @@
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
},
"app-builder-bin": {
"version": "3.5.9",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.9.tgz",
"integrity": "sha512-NSjtqZ3x2kYiDp3Qezsgukx/AUzKPr3Xgf9by4cYt05ILWGAptepeeu0Uv+7MO+41o6ujhLixTou8979JGg2Kg==",
"version": "3.5.10",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.10.tgz",
"integrity": "sha512-Jd+GW68lR0NeetgZDo47PdWBEPdnD+p0jEa7XaxjRC8u6Oo/wgJsfKUkORRgr2NpkD19IFKN50P6JYy04XHFLQ==",
"dev": true
},
"app-builder-lib": {
"version": "22.8.0",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.8.0.tgz",
"integrity": "sha512-RGaIRjCUrqkmh6QOGsyekQPEOaVynHfmeh8JZuyUymFYUOFdzBbPamkA2nhBVBTkkgfjRHsxK7LhedFKPzvWEQ==",
"version": "22.8.1",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.8.1.tgz",
"integrity": "sha512-D/ac1+vuGIAAwEeTtXl8b+qWl7Gz/IQatFyzYl2ocag/7N8LqUjKzZFJJISQPWt6PFDPDH0oCj8/GMh63aV0yw==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@develar/schema-utils": "~2.6.5",
"async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "22.8.0",
"builder-util": "22.8.1",
"builder-util-runtime": "8.7.2",
"chromium-pickle-js": "^0.2.0",
"debug": "^4.1.1",
"debug": "^4.2.0",
"ejs": "^3.1.3",
"electron-publish": "22.8.0",
"electron-publish": "22.8.1",
"fs-extra": "^9.0.1",
"hosted-git-info": "^3.0.5",
"is-ci": "^2.0.0",
@@ -1285,6 +1285,15 @@
"temp-file": "^3.3.7"
},
"dependencies": {
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"hosted-git-info": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz",
@@ -1294,19 +1303,10 @@
"lru-cache": "^6.0.0"
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
@@ -1595,9 +1595,9 @@
}
},
"better-sqlite3": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.1.0.tgz",
"integrity": "sha512-FV/snQ8F/kyqhdxsevzbojVtMowDWOfe1A5N3lYu1KJwoho2t7JgITmdlSc7DkOh3Zq65I+ZyeNWXQrkLEDFTg==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.1.1.tgz",
"integrity": "sha512-AkvGGyhAVZhRBOul2WT+5CB2EuveM3ZkebEKe1wxMqDZUy1XB/1RBgM66t0ybHC4DIni8+pr7NaLqEX87NUTwg==",
"requires": {
"bindings": "^1.5.0",
"prebuild-install": "^5.3.3",
@@ -1628,6 +1628,33 @@
"file-uri-to-path": "1.0.0"
}
},
"bl": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"bluebird": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz",
@@ -1856,19 +1883,19 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"builder-util": {
"version": "22.8.0",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.8.0.tgz",
"integrity": "sha512-H80P1JzVy3TGpi63x81epQDK24XalL034+jAZlrPb5IhLtYmnNNdxCCAVJvg3VjSISd73Y71O+uhqCxWpqbPHw==",
"version": "22.8.1",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.8.1.tgz",
"integrity": "sha512-LZG+E1xszMdut5hL5h7RkJQ7yOsQqdhJYgn1wvOP7MmF3MoUPRNDiRodLpYiWlaqZmgYhcfaipR/Mb8F/RqK8w==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@types/debug": "^4.1.5",
"@types/fs-extra": "^9.0.1",
"app-builder-bin": "3.5.9",
"app-builder-bin": "3.5.10",
"bluebird-lst": "^1.0.9",
"builder-util-runtime": "8.7.2",
"chalk": "^4.1.0",
"debug": "^4.1.1",
"debug": "^4.2.0",
"fs-extra": "^9.0.1",
"is-ci": "^2.0.0",
"js-yaml": "^3.14.0",
@@ -1911,6 +1938,21 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
},
@@ -2057,9 +2099,9 @@
},
"dependencies": {
"get-stream": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"requires": {
"pump": "^3.0.0"
@@ -2138,9 +2180,9 @@
}
},
"chownr": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"chrome-trace-event": {
"version": "1.0.2",
@@ -2192,9 +2234,9 @@
"dev": true
},
"cli-boxes": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz",
"integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
"integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
"dev": true
},
"cli-cursor": {
@@ -2467,14 +2509,26 @@
"dev": true
},
"commonmark": {
"version": "0.29.1",
"resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.29.1.tgz",
"integrity": "sha512-DafPdNYFXoEhsSiR4O+dJ45UJBfDL4cBTks4B+agKiaWt7qjG0bIhg5xuCE0RqU71ikJcBIf4/sRHh9vYQVF8Q==",
"version": "0.29.2",
"resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.29.2.tgz",
"integrity": "sha512-spe43MvEIaPpHss1T7z4yQaFQfLGmMu+yvCwv6xqhELIwkG/ZGgDpxOPzKxnuYzYT2c+aziCCc8m2rBVLA7jUA==",
"requires": {
"entities": "~1.1.1",
"entities": "~2.0",
"mdurl": "~1.0.1",
"minimist": "~1.2.0",
"minimist": ">=1.2.2",
"string.prototype.repeat": "^0.2.0"
},
"dependencies": {
"entities": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
}
}
},
"compare-version": {
@@ -2801,9 +2855,9 @@
}
},
"dayjs": {
"version": "1.8.35",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.35.tgz",
"integrity": "sha512-isAbIEenO4ilm6f8cpqvgjZCsuerDAz2Kb7ri201AiNn58aqXuaLJEnCtfIMdCvERZHNGRY5lDMTr/jdAnKSWQ=="
"version": "1.8.36",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz",
"integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw=="
},
"debug": {
"version": "4.1.1",
@@ -2986,13 +3040,13 @@
}
},
"dmg-builder": {
"version": "22.8.0",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.8.0.tgz",
"integrity": "sha512-orePWjcrl97SYLA8F/6UUtbXJSoZCYu5KOP1lVqD4LOomr8bjGDyEVYZmZYcg5WqKmXucdmO6OpqgzH/aRMMuA==",
"version": "22.8.1",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.8.1.tgz",
"integrity": "sha512-WeGom1moM00gBII6swljl4DQGrlJuEivoUhOmh8U9p1ALgeJL+EiTHbZFERlj8Ejy62xUUjURV+liOxUKmJFWg==",
"dev": true,
"requires": {
"app-builder-lib": "22.8.0",
"builder-util": "22.8.0",
"app-builder-lib": "22.8.1",
"builder-util": "22.8.1",
"fs-extra": "^9.0.1",
"iconv-lite": "^0.6.2",
"js-yaml": "^3.14.0",
@@ -3055,9 +3109,9 @@
}
},
"dot-prop": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
"integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
"integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
"dev": true,
"requires": {
"is-obj": "^2.0.0"
@@ -3104,9 +3158,9 @@
}
},
"electron": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.0.tgz",
"integrity": "sha512-7zPLEZ+kOjVJqfawMQ0vVuZZRqvZIeiID3tbjjbVybbxXIlFMpZ2jogoh7PV3rLrtm+dKRfu7Qc4E7ob1d0FqQ==",
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.1.tgz",
"integrity": "sha512-DScrhqBT4a54KfdF0EoipALpHmdQTn3m7SSCtbpTcEcG+UDUiXad2cOfW6DHeVH7N+CVDKDG12q2PhVJjXkFAA==",
"dev": true,
"requires": {
"@electron/get": "^1.0.1",
@@ -3115,25 +3169,25 @@
}
},
"electron-builder": {
"version": "22.8.0",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.8.0.tgz",
"integrity": "sha512-dUv4F3srJouqxhWivtKqSoQP4Df6vYgjooGdzms+iYMTFi9f0b4LlEbr7kgsPvte8zAglee7VOGOODkCRJDkUQ==",
"version": "22.8.1",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.8.1.tgz",
"integrity": "sha512-Hs7KTMq1rGSvT0fwGKXrjbLiJkK6sAKDQooUSwklOkktUgWi4ATjlP0fVE3l8SmS7zcLoww2yDZonSDqxEFhaQ==",
"dev": true,
"requires": {
"@types/yargs": "^15.0.5",
"app-builder-lib": "22.8.0",
"app-builder-lib": "22.8.1",
"bluebird-lst": "^1.0.9",
"builder-util": "22.8.0",
"builder-util": "22.8.1",
"builder-util-runtime": "8.7.2",
"chalk": "^4.1.0",
"dmg-builder": "22.8.0",
"dmg-builder": "22.8.1",
"fs-extra": "^9.0.1",
"is-ci": "^2.0.0",
"lazy-val": "^1.0.4",
"read-config-file": "6.0.0",
"sanitize-filename": "^1.6.3",
"update-notifier": "^4.1.0",
"yargs": "^15.3.1"
"yargs": "^15.4.1"
},
"dependencies": {
"ansi-styles": {
@@ -3484,14 +3538,14 @@
}
},
"electron-publish": {
"version": "22.8.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.8.0.tgz",
"integrity": "sha512-uM0Zdi9hUqqGOrPj478v7toTvV1Kgto1w11rIiI168batiXAJvNLD8VZRfehOrZT0ibUyZlw8FtxoGCrjyHUOw==",
"version": "22.8.1",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.8.1.tgz",
"integrity": "sha512-zqI66vl7j1CJZJ60J+1ez1tQNQeuqVspW44JvYDa5kZbM5wSFDAJFMK9RWHOqRF1Ezd4LDeiBa4aeTOwOt9syA==",
"dev": true,
"requires": {
"@types/fs-extra": "^9.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "22.8.0",
"builder-util": "22.8.1",
"builder-util-runtime": "8.7.2",
"chalk": "^4.1.0",
"fs-extra": "^9.0.1",
@@ -3543,9 +3597,9 @@
}
},
"electron-rebuild": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-2.0.1.tgz",
"integrity": "sha512-oXCnKKS+FpLxXiiSHtSCFI3zo+4H2y6zUegSQTI031RJXn2fzQV9UJMAfBrnW7Z083chIo3/L4+xFM4R8mreOQ==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-2.0.3.tgz",
"integrity": "sha512-I8Oeey9afU+trFLd8/qRRiHC083CCoBnmw3q0qQaRFsg0OzMaeJQn7Nl6EYKPpntuQ/3yOqZQ7b3ObNuETN/Ig==",
"dev": true,
"requires": {
"@malept/cross-spawn-promise": "^1.1.0",
@@ -3555,8 +3609,122 @@
"fs-extra": "^9.0.1",
"node-abi": "^2.19.1",
"node-gyp": "^7.1.0",
"ora": "^5.0.0",
"yargs": "^15.4.1"
"ora": "^5.1.0",
"yargs": "^16.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"cliui": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.1.tgz",
"integrity": "sha512-rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ==",
"dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"y18n": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.1.tgz",
"integrity": "sha512-/jJ831jEs4vGDbYPQp4yGKDYPSCCEQ45uZWJHE1AoYBzqdZi8+LDWas0z4HrmJXmKdpFsTiowSHXdxyFhpmdMg==",
"dev": true
},
"yargs": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.0.3.tgz",
"integrity": "sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA==",
"dev": true,
"requires": {
"cliui": "^7.0.0",
"escalade": "^3.0.2",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.1",
"yargs-parser": "^20.0.0"
}
},
"yargs-parser": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz",
"integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==",
"dev": true
}
}
},
"electron-window-state": {
@@ -3595,13 +3763,21 @@
}
},
"enhanced-resolve": {
"version": "5.0.0-beta.10",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.0.0-beta.10.tgz",
"integrity": "sha512-vEyxvHv3f8xl7i7QmTQ6BqKY32acSPQ4dTZo8WRMtcqTDYH9YyXnDxqXsQqBLvdRHUiwl9nVivESiM1RcrxbKQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.0.0.tgz",
"integrity": "sha512-6F037vvK16tgLlRgUx6ZEZISMysNvnnk09SILFrx3bNa1UsSLpIXFzWOmtiDxf1ISPAG6/wHBI61PEkeuTLVNA==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"tapable": "^2.0.0-beta.10"
"graceful-fs": "^4.2.4",
"tapable": "^2.0.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
}
}
},
"enquirer": {
@@ -3640,6 +3816,12 @@
"dev": true,
"optional": true
},
"escalade": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz",
"integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==",
"dev": true
},
"escape-goat": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
@@ -3668,12 +3850,12 @@
}
},
"eslint-scope": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz",
"integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
}
},
@@ -3689,12 +3871,20 @@
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"esrecurse": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"requires": {
"estraverse": "^4.1.0"
"estraverse": "^5.2.0"
},
"dependencies": {
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
}
}
},
"estraverse": {
@@ -4017,9 +4207,9 @@
}
},
"file-type": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-15.0.0.tgz",
"integrity": "sha512-l0JCuF5F7NIybCfa9G2H0lKhhGaf0z+HJyLOmB2feknY7/HBVNyD4PLesGKLGqznwyVXGNnfpIOr+Fvca6bOEg==",
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-15.0.1.tgz",
"integrity": "sha512-0LieQlSA3bWUdErNrxzxfI4rhsvNAVPBO06R8pTc1hp9SE6nhqlVyvhcaXoMmtXkBTPnQenbMPLW9X76hH76oQ==",
"requires": {
"readable-web-to-node-stream": "^2.0.0",
"strtok3": "^6.0.3",
@@ -4604,9 +4794,9 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"helmet": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-4.1.0.tgz",
"integrity": "sha512-KWy75fYN8hOG2Rhl8e5B3WhOzb0by1boQum85TiddIE9iu6gV+TXbUjVC17wfej0o/ZUpqB9kxM0NFCZRMzf+Q=="
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-4.1.1.tgz",
"integrity": "sha512-Avg4XxSBrehD94mkRwEljnO+6RZx7AGfk8Wa6K1nxaU+hbXlFOhlOIMgPfFqOYQB/dBCsTpootTGuiOG+CHiQA=="
},
"hosted-git-info": {
"version": "2.8.5",
@@ -6341,9 +6531,9 @@
}
},
"ora": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.0.0.tgz",
"integrity": "sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz",
"integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==",
"dev": true,
"requires": {
"chalk": "^4.1.0",
@@ -6748,9 +6938,9 @@
}
},
"prebuild-install": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.4.tgz",
"integrity": "sha512-AkKN+pf4fSEihjapLEEj8n85YIw/tN6BQqkhzbDc0RvEZGdkpJBGMUYx66AAMcPG2KzmPQS7Cm16an4HVBRRMA==",
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.5.tgz",
"integrity": "sha512-YmMO7dph9CYKi5IR/BzjOJlRzpxGGVo1EsLSUZ0mt/Mq0HWZIHOKHHcHdT69yG54C9m6i45GpItwRHpk0Py7Uw==",
"requires": {
"detect-libc": "^1.0.3",
"expand-template": "^2.0.3",
@@ -6773,14 +6963,6 @@
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
}
}
},
@@ -7391,20 +7573,20 @@
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
"ajv-keywords": "^3.5.2"
},
"dependencies": {
"ajv": {
"version": "6.12.4",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
"version": "6.12.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
@@ -7512,9 +7694,9 @@
}
},
"serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
"integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
"dev": true,
"requires": {
"randombytes": "^2.1.0"
@@ -7633,9 +7815,9 @@
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"simple-concat": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
"version": "3.1.0",
@@ -7993,11 +8175,6 @@
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
},
"string-similarity": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.2.tgz",
"integrity": "sha512-eCsPPyoQBgY4TMpVD6DVfO7pLrimUONriaO4Xjp3WPUW0YnNLqdHgRj23xotLlqrL90eJhBeq3zdAJf2mQgfBQ=="
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@@ -8117,9 +8294,9 @@
"dev": true
},
"tapable": {
"version": "2.0.0-beta.11",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.0.0-beta.11.tgz",
"integrity": "sha512-cAhRzCvMdyJsxmdrSXG8/SUlJG4WJUxD/csuYAybUFjKVt74Y6pTyZ/I1ZK+enmCkWZN0JWxh14G69temaGSiA==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.0.0.tgz",
"integrity": "sha512-bjzn0C0RWoffnNdTzNi7rNDhs1Zlwk2tRXgk8EiHKAOX1Mag3d6T0Y5zNa7l9CJ+EoUne/0UHdwS8tMbkh9zDg==",
"dev": true
},
"tar": {
@@ -8196,25 +8373,17 @@
}
},
"tar-stream": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
"integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
"requires": {
"bl": "^4.0.1",
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"dependencies": {
"bl": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.1.tgz",
"integrity": "sha512-FL/TdvchukRCuWVxT0YMO/7+L5TNeNrVFvRU2IY63aUyv9mpt8splf2NEr6qXtPo5fya5a66YohQKvGNmLrWNA==",
"requires": {
"readable-stream": "^3.4.0"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -8257,9 +8426,9 @@
"dev": true
},
"terser": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.2.1.tgz",
"integrity": "sha512-/AOtjRtAMNGO0fIF6m8HfcvXTw/2AKpsOzDn36tA5RfhRdeXyb4RvHxJ5Pah7iL6dFkLk+gOnCaNHGwJPl6TrQ==",
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.2.tgz",
"integrity": "sha512-H67sydwBz5jCUA32ZRL319ULu+Su1cAoZnnc+lXnenGRYWyLE3Scgkt8mNoAsMx0h5kdo758zdoS0LG9rYZXDQ==",
"dev": true,
"requires": {
"commander": "^2.20.0",
@@ -8276,19 +8445,19 @@
}
},
"terser-webpack-plugin": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.1.0.tgz",
"integrity": "sha512-0ZWDPIP8BtEDZdChbufcXUigOYk6dOX/P/X0hWxqDDcVAQLb8Yy/0FAaemSfax3PAA67+DJR778oz8qVbmy4hA==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.1.tgz",
"integrity": "sha512-D0IZQNl1ZN/JivFNDFzOeU2Bk2LdQQESHJhKTHsodpUmISkaeRwVFk7gzHzX4OuQwanDGelOxIEsBt1SZ+s6nA==",
"dev": true,
"requires": {
"cacache": "^15.0.5",
"find-cache-dir": "^3.3.1",
"jest-worker": "^26.3.0",
"p-limit": "^3.0.2",
"schema-utils": "^2.6.6",
"serialize-javascript": "^4.0.0",
"schema-utils": "^2.7.1",
"serialize-javascript": "^5.0.1",
"source-map": "^0.6.1",
"terser": "^5.0.0",
"terser": "^5.3.1",
"webpack-sources": "^1.4.3"
},
"dependencies": {
@@ -8716,9 +8885,9 @@
}
},
"update-notifier": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz",
"integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.1.tgz",
"integrity": "sha512-9y+Kds0+LoLG6yN802wVXoIfxYEwh3FlZwzMwpCZp62S2i1/Jzeqb9Eeeju3NSHccGGasfGlK5/vEHbAifYRDg==",
"dev": true,
"requires": {
"boxen": "^4.2.0",
@@ -8878,9 +9047,9 @@
}
},
"watchpack": {
"version": "2.0.0-beta.14",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.0.0-beta.14.tgz",
"integrity": "sha512-mToSRWik+KvbvQ0c9Oxy2PjhiCUz5CXx6kpKI9MdJ/fiBDxi0HYx4UTGxQoHIEA7aXQKJrfNUhw9OeH2bc4Wvg==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.0.0.tgz",
"integrity": "sha512-xSdCxxYZWNk3VK13bZRYhsQpfa8Vg63zXG+3pyU8ouqSLRCv4IGXIp9Kr226q6GBkGRlZrST2wwKtjfKz2m7Cg==",
"dev": true,
"requires": {
"glob-to-regexp": "^0.4.1",
@@ -8902,9 +9071,9 @@
"integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="
},
"webpack": {
"version": "5.0.0-beta.28",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.0.0-beta.28.tgz",
"integrity": "sha512-NVaE2s4YlhP07eFtfw8mmBKlRQYcc+vjLIVyLSaawCvJdt7i53oHDe2K8zTDNCNcP/HWluEg9kghV19LBE2T4A==",
"version": "5.0.0-beta.32",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.0.0-beta.32.tgz",
"integrity": "sha512-PrEyICK8AGeY/yK6yuHk7iuo5y1lMOuD2tI0rM+pG8ZYR6tjdkmvvu0kJGBeor9AaPjB46YA4BDBigMdBLCFCA==",
"dev": true,
"requires": {
"@types/eslint-scope": "^3.7.0",
@@ -8915,8 +9084,7 @@
"@webassemblyjs/wasm-parser": "1.9.0",
"acorn": "^7.4.0",
"chrome-trace-event": "^1.0.2",
"core-js": "^3.6.5",
"enhanced-resolve": "5.0.0-beta.10",
"enhanced-resolve": "^5.0.0",
"eslint-scope": "^5.1.0",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
@@ -8927,10 +9095,10 @@
"neo-async": "^2.6.2",
"pkg-dir": "^4.2.0",
"schema-utils": "^2.7.0",
"tapable": "^2.0.0-beta.11",
"tapable": "^2.0.0",
"terser-webpack-plugin": "^4.1.0",
"watchpack": "2.0.0-beta.14",
"webpack-sources": "2.0.0-beta.9"
"watchpack": "^2.0.0",
"webpack-sources": "^2.0.0"
},
"dependencies": {
"acorn": {
@@ -8939,12 +9107,6 @@
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
"dev": true
},
"core-js": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
"dev": true
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
@@ -9190,9 +9352,9 @@
}
},
"webpack-sources": {
"version": "2.0.0-beta.9",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.0.0-beta.9.tgz",
"integrity": "sha512-q6O+gKGojLye0Fm05vgthtmx5LPbYpAU4FsdMsX8YBgkOZovDbQmBMhjY/CiWhxD9u/0AWeWdXFNbQz17mkdLw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.0.0.tgz",
"integrity": "sha512-CpCkDjEKa5vYVRDFDRABBkBomz+82lz9bpXViN1LBc8L/WDXvSyELKcBvBnTeDEiRfMJCGAFG9+04406PLSsIA==",
"dev": true,
"requires": {
"source-list-map": "^2.0.1",
@@ -9582,14 +9744,6 @@
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"dependencies": {
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
}
}
},
"yauzl": {
+10 -11
View File
@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.44.1-beta",
"version": "0.44.3-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -26,13 +26,13 @@
"dependencies": {
"async-mutex": "0.2.4",
"axios": "0.20.0",
"better-sqlite3": "7.1.0",
"better-sqlite3": "7.1.1",
"body-parser": "1.19.0",
"cls-hooked": "4.2.2",
"commonmark": "0.29.1",
"commonmark": "0.29.2",
"cookie-parser": "1.4.5",
"csurf": "1.11.0",
"dayjs": "1.8.35",
"dayjs": "1.8.36",
"debug": "4.1.1",
"ejs": "3.1.5",
"electron-debug": "3.1.0",
@@ -41,9 +41,9 @@
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.17.1",
"file-type": "15.0.0",
"file-type": "15.0.1",
"fs-extra": "9.0.1",
"helmet": "4.1.0",
"helmet": "4.1.1",
"html": "1.0.0",
"html2plaintext": "2.1.2",
"http-proxy-agent": "4.0.1",
@@ -66,7 +66,6 @@
"semver": "7.3.2",
"serve-favicon": "2.5.0",
"session-file-store": "1.4.0",
"string-similarity": "4.0.2",
"striptags": "3.1.1",
"turndown": "6.0.0",
"turndown-plugin-gfm": "1.0.2",
@@ -77,16 +76,16 @@
},
"devDependencies": {
"cross-env": "7.0.2",
"electron": "9.3.0",
"electron-builder": "22.8.0",
"electron": "9.3.1",
"electron-builder": "22.8.1",
"electron-packager": "15.1.0",
"electron-rebuild": "2.0.1",
"electron-rebuild": "2.0.3",
"esm": "3.2.25",
"jasmine": "3.6.1",
"jsdoc": "3.6.5",
"lorem-ipsum": "2.0.3",
"rcedit": "2.2.0",
"webpack": "5.0.0-beta.28",
"webpack": "5.0.0-beta.32",
"webpack-cli": "4.0.0-beta.8"
},
"optionalDependencies": {
+23 -23
View File
@@ -1,4 +1,4 @@
const ParsingContext = require("../../src/services/search/parsing_context.js");
const SearchContext = require("../../src/services/search/search_context.js");
const parse = require('../../src/services/search/services/parse.js');
function tokens(toks, cur = 0) {
@@ -24,7 +24,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: tokens(["hello", "hi"]),
expressionTokens: [],
parsingContext: new ParsingContext({includeNoteContent: false})
searchContext: new SearchContext({includeNoteContent: false})
});
expect(rootExp.constructor.name).toEqual("AndExp");
@@ -36,7 +36,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: tokens(["hello", "hi"]),
expressionTokens: [],
parsingContext: new ParsingContext({includeNoteContent: true})
searchContext: new SearchContext({includeNoteContent: true})
});
expect(rootExp.constructor.name).toEqual("AndExp");
@@ -59,7 +59,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["#mylabel", "=", "text"]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("LabelComparisonExp");
@@ -72,7 +72,7 @@ describe("Parser", () => {
let rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["#!mylabel"]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("NotExp");
@@ -83,7 +83,7 @@ describe("Parser", () => {
rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["~!myrelation"]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("NotExp");
@@ -96,7 +96,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "=", "text", "and", "#second", "=", "text"]),
parsingContext: new ParsingContext(true)
searchContext: new SearchContext(true)
});
expect(rootExp.constructor.name).toEqual("AndExp");
@@ -113,7 +113,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "=", "text", "#second", "=", "text"]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("AndExp");
@@ -130,7 +130,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "=", "text", "or", "#second", "=", "text"]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("OrExp");
@@ -147,7 +147,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: tokens(["hello"]),
expressionTokens: tokens(["#mylabel", "=", "text"]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("AndExp");
@@ -165,7 +165,7 @@ describe("Parser", () => {
const rootExp = parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "=", "text", "or", ["#second", "=", "text", "and", "#third", "=", "text"]]),
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("OrExp");
@@ -187,39 +187,39 @@ describe("Parser", () => {
describe("Invalid expressions", () => {
it("incomplete comparison", () => {
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "="]),
parsingContext
searchContext
});
expect(parsingContext.error).toEqual('Misplaced or incomplete expression "="')
expect(searchContext.error).toEqual('Misplaced or incomplete expression "="')
});
it("comparison between labels is impossible", () => {
let parsingContext = new ParsingContext();
parsingContext.originalQuery = "#first = #second";
let searchContext = new SearchContext();
searchContext.originalQuery = "#first = #second";
parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "=", "#second"]),
parsingContext
searchContext
});
expect(parsingContext.error).toEqual(`Error near token "#second" in "#first = #second", it's possible to compare with constant only.`);
expect(searchContext.error).toEqual(`Error near token "#second" in "#first = #second", it's possible to compare with constant only.`);
parsingContext = new ParsingContext();
parsingContext.originalQuery = "#first = note.relations.second";
searchContext = new SearchContext();
searchContext.originalQuery = "#first = note.relations.second";
parse({
fulltextTokens: [],
expressionTokens: tokens(["#first", "=", "note", ".", "relations", "second"]),
parsingContext
searchContext
});
expect(parsingContext.error).toEqual(`Error near token "note" in "#first = note.relations.second", it's possible to compare with constant only.`);
expect(searchContext.error).toEqual(`Error near token "note" in "#first = note.relations.second", it's possible to compare with constant only.`);
const rootExp = parse({
fulltextTokens: [],
@@ -228,7 +228,7 @@ describe("Invalid expressions", () => {
{ token: "=", inQuotes: false },
{ token: "#second", inQuotes: true },
],
parsingContext: new ParsingContext()
searchContext: new SearchContext()
});
expect(rootExp.constructor.name).toEqual("LabelComparisonExp");
+72 -72
View File
@@ -1,7 +1,7 @@
const searchService = require('../../src/services/search/services/search.js');
const Note = require('../../src/services/note_cache/entities/note.js');
const Branch = require('../../src/services/note_cache/entities/branch.js');
const ParsingContext = require('../../src/services/search/parsing_context.js');
const SearchContext = require('../../src/services/search/search_context.js');
const dateUtils = require('../../src/services/date_utils.js');
const noteCache = require('../../src/services/note_cache/note_cache.js');
const {NoteBuilder, findNoteByTitle, note} = require('./note_cache_mocking.js');
@@ -22,8 +22,8 @@ describe("Search", () => {
.child(note("Austria"))
);
const parsingContext = new ParsingContext();
const searchResults = searchService.findNotesWithQuery('europe austria', parsingContext);
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('europe austria', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
@@ -39,13 +39,13 @@ describe("Search", () => {
.child(vienna
.label('inhabitants', '1888776'));
const parsingContext = new ParsingContext();
let searchResults = searchService.findNotesWithQuery('capital', parsingContext);
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('capital', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('inhabitants', parsingContext);
searchResults = searchService.findNotesWithQuery('inhabitants', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Vienna")).toBeTruthy();
@@ -56,18 +56,18 @@ describe("Search", () => {
.child(note("Effective Java", 'book', ''))
.child(note("Hello World.java", 'code', 'text/x-java'));
const parsingContext = new ParsingContext();
let searchResults = searchService.findNotesWithQuery('book', parsingContext);
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('book', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Effective Java")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('text', parsingContext); // should match mime
searchResults = searchService.findNotesWithQuery('text', searchContext); // should match mime
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Hello World.java")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('java', parsingContext);
searchResults = searchService.findNotesWithQuery('java', searchContext);
expect(searchResults.length).toEqual(2);
});
@@ -78,8 +78,8 @@ describe("Search", () => {
.child(note("Austria"))
);
const parsingContext = new ParsingContext();
const searchResults = searchService.findNotesWithQuery('europe', parsingContext);
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('europe', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
@@ -92,9 +92,9 @@ describe("Search", () => {
.label('capital', 'Vienna'))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('Vienna', parsingContext);
const searchResults = searchService.findNotesWithQuery('Vienna', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
@@ -108,9 +108,9 @@ describe("Search", () => {
.label('capital', 'Prague'))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('#capital=Vienna', parsingContext);
let searchResults = searchService.findNotesWithQuery('#capital=Vienna', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
@@ -124,9 +124,9 @@ describe("Search", () => {
.label('capital', 'Prague'))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.labels.capital=Prague', parsingContext);
let searchResults = searchService.findNotesWithQuery('# note.labels.capital=Prague', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
@@ -141,9 +141,9 @@ describe("Search", () => {
.label('population', '10650000'))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('#country #population >= 10000000', parsingContext);
const searchResults = searchService.findNotesWithQuery('#country #population >= 10000000', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
@@ -162,13 +162,13 @@ describe("Search", () => {
.label('established', '1920-06-04'))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('#established <= "1955-01-01"', parsingContext);
let searchResults = searchService.findNotesWithQuery('#established <= "1955-01-01"', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Hungary")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('#established > "1955-01-01"', parsingContext);
searchResults = searchService.findNotesWithQuery('#established > "1955-01-01"', searchContext);
expect(searchResults.length).toEqual(2);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
@@ -185,10 +185,10 @@ describe("Search", () => {
.label('dateTime', dateUtils.localNowDateTime())
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
function test(query, expectedResultCount) {
const searchResults = searchService.findNotesWithQuery(query, parsingContext);
const searchResults = searchService.findNotesWithQuery(query, searchContext);
expect(searchResults.length).toEqual(expectedResultCount);
if (expectedResultCount === 1) {
@@ -238,9 +238,9 @@ describe("Search", () => {
.label('languageFamily', 'finnougric'))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('#languageFamily = slavic OR #languageFamily = germanic', parsingContext);
const searchResults = searchService.findNotesWithQuery('#languageFamily = slavic OR #languageFamily = germanic', searchContext);
expect(searchResults.length).toEqual(2);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
@@ -255,20 +255,20 @@ describe("Search", () => {
.child(note("Czech Republic")
.label('languageFamily', 'slavic')));
let parsingContext = new ParsingContext({fuzzyAttributeSearch: false});
let searchContext = new SearchContext({fuzzyAttributeSearch: false});
let searchResults = searchService.findNotesWithQuery('#language', parsingContext);
let searchResults = searchService.findNotesWithQuery('#language', searchContext);
expect(searchResults.length).toEqual(0);
searchResults = searchService.findNotesWithQuery('#languageFamily=ger', parsingContext);
searchResults = searchService.findNotesWithQuery('#languageFamily=ger', searchContext);
expect(searchResults.length).toEqual(0);
parsingContext = new ParsingContext({fuzzyAttributeSearch: true});
searchContext = new SearchContext({fuzzyAttributeSearch: true});
searchResults = searchService.findNotesWithQuery('#language', parsingContext);
searchResults = searchService.findNotesWithQuery('#language', searchContext);
expect(searchResults.length).toEqual(2);
searchResults = searchService.findNotesWithQuery('#languageFamily=ger', parsingContext);
searchResults = searchService.findNotesWithQuery('#languageFamily=ger', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
@@ -279,9 +279,9 @@ describe("Search", () => {
.child(note("Austria"))
.child(note("Czech Republic")));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('# note.title =* czech', parsingContext);
const searchResults = searchService.findNotesWithQuery('# note.title =* czech', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
@@ -296,18 +296,18 @@ describe("Search", () => {
.child(note("Asia")
.child(note('Taiwan')));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe', parsingContext);
let searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe', searchContext);
expect(searchResults.length).toEqual(2);
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('# note.parents.title = Asia', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Asia', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Taiwan")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('# note.parents.parents.title = Europe', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.parents.title = Europe', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
});
@@ -324,13 +324,13 @@ describe("Search", () => {
.child(note('Taipei').label('city')))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('#city AND note.ancestors.title = Europe', parsingContext);
let searchResults = searchService.findNotesWithQuery('#city AND note.ancestors.title = Europe', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('#city AND note.ancestors.title = Asia', parsingContext);
searchResults = searchService.findNotesWithQuery('#city AND note.ancestors.title = Asia', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Taipei")).toBeTruthy();
});
@@ -345,18 +345,18 @@ describe("Search", () => {
.child(note("Oceania")
.child(note('Australia')));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.children.title =* Aust', parsingContext);
let searchResults = searchService.findNotesWithQuery('# note.children.title =* Aust', searchContext);
expect(searchResults.length).toEqual(2);
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Oceania")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('# note.children.title =* Aust AND note.children.title *= republic', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.children.title =* Aust AND note.children.title *= republic', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('# note.children.children.title = Prague', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.children.children.title = Prague', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
});
@@ -375,13 +375,13 @@ describe("Search", () => {
.relation('neighbor', portugal.note))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# ~neighbor.title = Austria', parsingContext);
let searchResults = searchService.findNotesWithQuery('# ~neighbor.title = Austria', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('# ~neighbor.title = Portugal', parsingContext);
searchResults = searchService.findNotesWithQuery('# ~neighbor.title = Portugal', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Spain")).toBeTruthy();
});
@@ -400,9 +400,9 @@ describe("Search", () => {
.relation('neighbor', portugal.note))
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
const searchResults = searchService.findNotesWithQuery('# note.relations.neighbor.title = Austria', parsingContext);
const searchResults = searchService.findNotesWithQuery('# note.relations.neighbor.title = Austria', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
@@ -426,13 +426,13 @@ describe("Search", () => {
.child(ukraine)
);
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.relations.neighbor.relations.neighbor.title = Italy', parsingContext);
let searchResults = searchService.findNotesWithQuery('# note.relations.neighbor.relations.neighbor.title = Italy', searchContext);
expect(searchResults.length).toEqual(1);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
searchResults = searchService.findNotesWithQuery('# note.relations.neighbor.relations.neighbor.title = Ukraine', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.relations.neighbor.relations.neighbor.title = Ukraine', searchContext);
expect(searchResults.length).toEqual(2);
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
@@ -463,10 +463,10 @@ describe("Search", () => {
austria.note.utcDateModified = '2020-05-14 11:11:42.001Z';
austria.note.contentLength = 1001;
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
function test(propertyName, value, expectedResultCount) {
const searchResults = searchService.findNotesWithQuery(`# note.${propertyName} = ${value}`, parsingContext);
const searchResults = searchService.findNotesWithQuery(`# note.${propertyName} = ${value}`, searchContext);
expect(searchResults.length).toEqual(expectedResultCount);
}
@@ -520,38 +520,38 @@ describe("Search", () => {
.child(austria)
.child(italy));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.title', parsingContext);
let searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.title', searchContext);
expect(searchResults.length).toEqual(4);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Austria");
expect(noteCache.notes[searchResults[1].noteId].title).toEqual("Italy");
expect(noteCache.notes[searchResults[2].noteId].title).toEqual("Slovakia");
expect(noteCache.notes[searchResults[3].noteId].title).toEqual("Ukraine");
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.labels.capital', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.labels.capital', searchContext);
expect(searchResults.length).toEqual(4);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Slovakia");
expect(noteCache.notes[searchResults[1].noteId].title).toEqual("Ukraine");
expect(noteCache.notes[searchResults[2].noteId].title).toEqual("Italy");
expect(noteCache.notes[searchResults[3].noteId].title).toEqual("Austria");
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.labels.capital DESC', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.labels.capital DESC', searchContext);
expect(searchResults.length).toEqual(4);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Austria");
expect(noteCache.notes[searchResults[1].noteId].title).toEqual("Italy");
expect(noteCache.notes[searchResults[2].noteId].title).toEqual("Ukraine");
expect(noteCache.notes[searchResults[3].noteId].title).toEqual("Slovakia");
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.labels.capital DESC limit 2', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy note.labels.capital DESC limit 2', searchContext);
expect(searchResults.length).toEqual(2);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Austria");
expect(noteCache.notes[searchResults[1].noteId].title).toEqual("Italy");
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 0', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 0', searchContext);
expect(searchResults.length).toEqual(0);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1000', parsingContext);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1000', searchContext);
expect(searchResults.length).toEqual(4);
});
@@ -564,13 +564,13 @@ describe("Search", () => {
.child(slovakia)
.child(italy));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# not(#capital) and note.noteId != root', parsingContext);
let searchResults = searchService.findNotesWithQuery('# not(#capital) and note.noteId != root', searchContext);
expect(searchResults.length).toEqual(1);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Europe");
searchResults = searchService.findNotesWithQuery('#!capital and note.noteId != root', parsingContext);
searchResults = searchService.findNotesWithQuery('#!capital and note.noteId != root', searchContext);
expect(searchResults.length).toEqual(1);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Europe");
});
@@ -584,9 +584,9 @@ describe("Search", () => {
.child(slovakia)
.child(italy));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.text *=* rati and note.noteId != root', parsingContext);
let searchResults = searchService.findNotesWithQuery('# note.text *=* rati and note.noteId != root', searchContext);
expect(searchResults.length).toEqual(1);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Slovakia");
});
@@ -601,9 +601,9 @@ describe("Search", () => {
.child(note('Post Y')))
.child(note ('Reddit is bad'));
const parsingContext = new ParsingContext();
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('reddit', parsingContext);
let searchResults = searchService.findNotesWithQuery('reddit', searchContext);
expect(searchResults.length).toEqual(1);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Reddit is bad");
});
@@ -624,9 +624,9 @@ describe("Search", () => {
// .label('largestCity', 'Prague'))
// );
//
// const parsingContext = new ParsingContext();
// const searchContext = new SearchContext();
//
// const searchResults = searchService.findNotesWithQuery('#capital = #largestCity', parsingContext);
// const searchResults = searchService.findNotesWithQuery('#capital = #largestCity', searchContext);
// expect(searchResults.length).toEqual(2);
// expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
// expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
+17 -9
View File
@@ -1,8 +1,10 @@
"use strict";
const Entity = require('./entity');
const dateUtils = require('../services/date_utils');
const sql = require('../services/sql');
const promotedAttributeDefinitionParser = require("../services/promoted_attribute_definition_parser");
/**
* Attribute is key value pair owned by a note.
@@ -30,14 +32,6 @@ class Attribute extends Entity {
super(row);
this.isInheritable = !!this.isInheritable;
if (this.isDefinition()) {
try {
this.value = JSON.parse(this.value);
}
catch (e) {
}
}
}
/**
@@ -66,7 +60,21 @@ class Attribute extends Entity {
* @return {boolean}
*/
isDefinition() {
return this.type === 'label-definition' || this.type === 'relation-definition';
return this.type === 'label' && (this.name.startsWith('label:') || this.name.startsWith('relation:'));
}
getDefinition() {
return promotedAttributeDefinitionParser.parse(this.value);
}
getDefinedName() {
if (this.type === 'label' && this.name.startsWith('label:')) {
return this.name.substr(6);
} else if (this.type === 'label' && this.name.startsWith('relation:')) {
return this.name.substr(9);
} else {
return this.name;
}
}
beforeSaving() {
+14 -21
View File
@@ -9,9 +9,7 @@ const dateUtils = require('../services/date_utils');
const entityChangesService = require('../services/entity_changes.js');
const LABEL = 'label';
const LABEL_DEFINITION = 'label-definition';
const RELATION = 'relation';
const RELATION_DEFINITION = 'relation-definition';
/**
* This represents a Note which is a central object in the Trilium Notes project.
@@ -105,6 +103,7 @@ class Note extends Entity {
}
}
/** @returns {{contentLength, dateModified, utcDateModified}} */
getContentMetadata() {
return sql.getRow(`
SELECT
@@ -301,14 +300,6 @@ class Note extends Entity {
return this.getOwnedAttributes(LABEL, name);
}
/**
* @param {string} [name] - label name to filter
* @returns {Attribute[]} all note's label definitions, including inherited ones
*/
getLabelDefinitions(name) {
return this.getAttributes(LABEL_DEFINITION, name);
}
/**
* @param {string} [name] - relation name to filter
* @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
@@ -340,14 +331,6 @@ class Note extends Entity {
return targets;
}
/**
* @param {string} [name] - relation name to filter
* @returns {Attribute[]} all note's relation definitions including inherited ones
*/
getRelationDefinitions(name) {
return this.getAttributes(RELATION_DEFINITION, name);
}
/**
* Clear note's attributes cache to force fresh reload for next attribute request.
* Cache is note instance scoped.
@@ -397,15 +380,15 @@ class Note extends Entity {
return firstDefinitionIndex === index;
}
else {
const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name);
const definitionAttr = attributes.find(el => el.type === 'label' && el.name === attr.type + ':' + attr.name);
if (!definitionAttr) {
return true;
}
const definition = definitionAttr.value;
const definition = definitionAttr.getDefinition();
if (definition.multiplicityType === 'multi') {
if (definition.multiplicity === 'multi') {
return true;
}
else {
@@ -885,6 +868,16 @@ class Note extends Entity {
return notePaths;
}
getRelationDefinitions() {
return this.getLabels()
.filter(l => l.name.startsWith("relation:"));
}
getLabelDefinitions() {
return this.getLabels()
.filter(l => l.name.startsWith("relation:"));
}
/**
* @param ancestorNoteId
* @return {boolean} - true if ancestorNoteId occurs in at least one of the note's paths
+1
View File
@@ -33,6 +33,7 @@ class NoteRevision extends Entity {
constructor(row) {
super(row);
this.isErased = !!this.isErased;
this.isProtected = !!this.isProtected;
if (this.isProtected) {
+4 -18
View File
@@ -3,9 +3,7 @@ import Attribute from './attribute.js';
import noteAttributeCache from "../services/note_attribute_cache.js";
const LABEL = 'label';
const LABEL_DEFINITION = 'label-definition';
const RELATION = 'relation';
const RELATION_DEFINITION = 'relation-definition';
/**
* FIXME: since there's no "full note" anymore we can rename this to Note
@@ -74,6 +72,10 @@ class NoteShort {
this.childToBranch[childNoteId] = branchId;
this.sortChildren();
}
sortChildren() {
const branchIdPos = {};
for (const branchId of Object.values(this.childToBranch)) {
@@ -243,14 +245,6 @@ class NoteShort {
return this.getAttributes(LABEL, name);
}
/**
* @param {string} [name] - label name to filter
* @returns {Attribute[]} all note's label definitions, including inherited ones
*/
getLabelDefinitions(name) {
return this.getAttributes(LABEL_DEFINITION, name);
}
/**
* @param {string} [name] - relation name to filter
* @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
@@ -267,14 +261,6 @@ class NoteShort {
return this.getAttributes(RELATION, name);
}
/**
* @param {string} [name] - relation name to filter
* @returns {Attribute[]} all note's relation definitions including inherited ones
*/
getRelationDefinitions(name) {
return this.getAttributes(RELATION_DEFINITION, name);
}
/**
* @param {string} type - attribute type (label, relation, etc.)
* @param {string} name - attribute name
@@ -20,7 +20,7 @@ import NoteInfoWidget from "../widgets/collapsible_widgets/note_info.js";
import CalendarWidget from "../widgets/collapsible_widgets/calendar.js";
import LinkMapWidget from "../widgets/collapsible_widgets/link_map.js";
import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.js";
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
import SimilarNotesWidget from "../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";
@@ -138,6 +138,7 @@ export default class DesktopMainWindowLayout {
)
.child(new TabCachingWidget(() => new AttributeListWidget()))
.child(new TabCachingWidget(() => new NoteDetailWidget()))
.child(new TabCachingWidget(() => new SimilarNotesWidget()))
.child(...this.customWidgets.get('center-pane'))
)
.child(new SidePaneContainer('right')
@@ -148,7 +149,6 @@ export default class DesktopMainWindowLayout {
.child(new TabCachingWidget(() => new EditedNotesWidget()))
.child(new TabCachingWidget(() => new LinkMapWidget()))
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
.child(new TabCachingWidget(() => new SimilarNotesWidget()))
.child(new TabCachingWidget(() => new WhatLinksHereWidget()))
.child(...this.customWidgets.get('right-pane'))
)
+5 -5
View File
@@ -76,13 +76,13 @@ export default class MobileLayout {
.child(new NoteTreeWidget("main").cssBlock(FANCYTREE_CSS)))
.child(new ScreenContainer("detail", "column")
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-8")
.child(new FlexContainer('row')
.child(new FlexContainer('row').overflowing()
.css('font-size', 'larger')
.css('align-items', 'center')
.child(new MobileDetailMenuWidget())
.child(new NoteTitleWidget()
.css('padding', '10px')
.css('font-size', 'larger'))
.child(new NoteTitleWidget())
.child(new CloseDetailButtonWidget()))
.child(new NoteDetailWidget()
.css('padding', '5px 20px 10px 0')));
}
}
}
+38 -14
View File
@@ -1,29 +1,32 @@
import ws from "./ws.js";
import linkService from "./link.js";
import treeCache from "./tree_cache.js";
function renderAttribute(attribute, $container, renderIsInheritable) {
async function renderAttribute(attribute, renderIsInheritable) {
const isInheritable = renderIsInheritable && attribute.isInheritable ? `(inheritable)` : '';
const $attr = $("<span>");
if (attribute.type === 'label') {
$container.append(document.createTextNode('#' + attribute.name + isInheritable));
$attr.append(document.createTextNode('#' + attribute.name + isInheritable));
if (attribute.value) {
$container.append('=');
$container.append(document.createTextNode(formatValue(attribute.value)));
$attr.append('=');
$attr.append(document.createTextNode(formatValue(attribute.value)));
}
} else if (attribute.type === 'relation') {
if (attribute.isAutoLink) {
return;
return $attr;
}
// when the relation has just been created then it might not have a value
if (attribute.value) {
$container.append(document.createTextNode('~' + attribute.name + isInheritable + "="));
$container.append(createNoteLink(attribute.value));
$attr.append(document.createTextNode('~' + attribute.name + isInheritable + "="));
$attr.append(await createNoteLink(attribute.value));
}
} else {
ws.logError("Unknown attr type: " + attribute.type);
}
return $attr;
}
function formatValue(val) {
@@ -44,18 +47,39 @@ function formatValue(val) {
}
}
function createNoteLink(noteId) {
const $link = $("<a>", {
async function createNoteLink(noteId) {
const note = await treeCache.getNote(noteId);
if (!note) {
return;
}
return $("<a>", {
href: '#' + noteId,
class: 'reference-link',
'data-note-path': noteId
});
})
.text(note.title);
}
linkService.loadReferenceLinkTitle(noteId, $link);
async function renderAttributes(attributes, renderIsInheritable) {
const $container = $("<span>");
return $link;
for (let i = 0; i < attributes.length; i++) {
const attribute = attributes[i];
const $attr = await renderAttribute(attribute, renderIsInheritable);
$container.append($attr.html()); // .html() to get only inner HTML, we don't want any spans
if (i < attributes.length - 1) {
$container.append(" ");
}
}
return $container;
}
export default {
renderAttribute
renderAttribute,
renderAttributes
}
+1 -1
View File
@@ -162,7 +162,7 @@ export default class Entrypoints extends Component {
}
async searchForResultsCommand({searchText}) {
const response = await server.get('search/' + encodeURIComponent(searchText));
const response = await server.get('search/' + encodeURIComponent(searchText) + '?includeNoteContent=true&excludeArchived=true&fuzzyAttributeSearch=false');
if (!response.success) {
toastService.showError("Search failed.", 3000);
@@ -87,7 +87,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
* @param {ToolbarButtonOptions} opts
*/
this.addButtonToToolbar = opts => {
const buttonId = "toolbar-button-" + opts.title.replace(/[^a-zA-Z0-9]/g, "-");
const buttonId = "toolbar-button-" + opts.title.replace(/\s/g, "-");
const button = $('<button>')
.addClass("btn btn-sm")
@@ -169,7 +169,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
* "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
@@ -185,7 +185,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
* "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
+3
View File
@@ -1,6 +1,7 @@
import options from './options.js';
import appContext from "./app_context.js";
import treeService from "./tree.js";
import treeCache from "./tree_cache.js";
function getHoistedNoteId() {
return options.get('hoistedNoteId');
@@ -9,6 +10,8 @@ function getHoistedNoteId() {
async function setHoistedNoteId(noteId) {
await options.save('hoistedNoteId', noteId);
await treeCache.loadInitialTree();
// FIXME - just use option load event
appContext.triggerEvent('hoistedNoteChanged', {noteId});
}
+12 -41
View File
@@ -2,6 +2,7 @@ import treeService from "./tree.js";
import linkService from "./link.js";
import treeCache from "./tree_cache.js";
import utils from "./utils.js";
import attributeRenderer from "./attribute_renderer.js";
function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
@@ -82,51 +83,21 @@ async function renderTooltip(note, noteComplement) {
return '<div>Note has been deleted.</div>';
}
const attributes = note.getAttributes();
const someNotePath = treeService.getSomeNotePath(note);
let content = '';
if (!someNotePath) {
return;
}
const promoted = attributes
.filter(attr => attr.type === 'label-definition' || attr.type === 'relation-definition')
.filter(attr => !attr.name.startsWith("child:"))
.filter(attr => {
const json = attr.jsonValue;
let content = $("<h5>").text(await treeService.getNotePathTitle(someNotePath)).prop('outerHTML');
return json && json.isPromoted;
});
const attributes = note.getAttributes()
.filter(attr => !attr.isDefinition());
if (promoted.length > 0) {
const $table = $("<table>").addClass("promoted-attributes-in-tooltip");
for (const definitionAttr of promoted) {
const definitionType = definitionAttr.type;
const valueType = definitionType.substr(0, definitionType.length - 11);
let valueAttrs = attributes.filter(el => el.name === definitionAttr.name && el.type === valueType);
for (const valueAttr of valueAttrs) {
if (!valueAttr.value) {
continue;
}
let $value = "";
if (valueType === 'label') {
$value = $("<td>").text(valueAttr.value);
}
else if (valueType === 'relation' && valueAttr.value) {
$value = $("<td>").append(await linkService.createNoteLink(valueAttr.value));
}
const $row = $("<tr>")
.append($("<th>").text(definitionAttr.name))
.append($value);
$table.append($row);
}
}
content += $table.prop('outerHTML');
if (attributes.length > 0) {
content += '<div class="tooltip-attributes"><strong>Attributes: </strong> '
+ (await attributeRenderer.renderAttributes(attributes)).html()
+ '</div>'
}
if (note.type === 'text' && !utils.isHtmlEmpty(noteComplement.content)) {
+8 -1
View File
@@ -158,6 +158,12 @@ function getNoteIdFromNotePath(notePath) {
return lastSegment.split("-")[0];
}
async function getBranchIdFromNotePath(notePath) {
const {noteId, parentNoteId} = getNoteIdAndParentIdFromNotePath(notePath);
return await treeCache.getBranchId(parentNoteId, noteId);
}
function getNoteIdAndParentIdFromNotePath(notePath) {
if (notePath === 'root') {
return {
@@ -185,7 +191,7 @@ function getNoteIdAndParentIdFromNotePath(notePath) {
return {
parentNoteId,
noteId
}
};
}
function getNotePath(node) {
@@ -286,6 +292,7 @@ export default {
getNotePath,
getNoteIdFromNotePath,
getNoteIdAndParentIdFromNotePath,
getBranchIdFromNotePath,
getNoteTitle,
getNotePathTitle,
getHashValueFromAddress,
+4
View File
@@ -276,6 +276,10 @@ class TreeCache {
}
async getBranchId(parentNoteId, childNoteId) {
if (childNoteId === 'root') {
return 'root';
}
const child = await this.getNote(childNoteId);
if (!child) {
@@ -3,7 +3,6 @@ import treeCache from "./tree_cache.js";
import hoistedNoteService from './hoisted_note.js';
import clipboard from './clipboard.js';
import protectedSessionHolder from "./protected_session_holder.js";
import appContext from "./app_context.js";
import noteCreateService from "./note_create.js";
import contextMenu from "./context_menu.js";
+21 -3
View File
@@ -150,15 +150,21 @@ async function consumeSyncData() {
const nonProcessedSyncRows = allSyncRows.filter(sync => !processedEntityChangeIds.has(sync.id));
try {
await utils.timeLimit(processSyncRows(nonProcessedSyncRows), 5000);
await utils.timeLimit(processSyncRows(nonProcessedSyncRows), 30000);
}
catch (e) {
logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`);
// if there's an error in updating the frontend then the easy option to recover is to reload the frontend completely
if (!glob.isDev) {
if (!glob.isDev && !options.is('debugModeEnabled')) {
// if there's an error in updating the frontend then the easy option to recover is to reload the frontend completely
utils.reloadApp();
}
else {
console.log("nonProcessedSyncRows causing the timeout", nonProcessedSyncRows);
alert(`Encountered error "${e.message}", check out the console.`);
}
}
for (const syncRow of nonProcessedSyncRows) {
@@ -306,11 +312,23 @@ async function processSyncRows(syncRows) {
}
for (const sync of syncRows.filter(sync => sync.entityName === 'note_reordering')) {
const parentNoteIdsToSort = new Set();
for (const branchId in sync.positions) {
const branch = treeCache.branches[branchId];
if (branch) {
branch.notePosition = sync.positions[branchId];
parentNoteIdsToSort.add(branch.parentNoteId);
}
}
for (const parentNoteId of parentNoteIdsToSort) {
const parentNote = treeCache.notes[parentNoteId];
if (parentNote) {
parentNote.sortChildren();
}
}
+8 -13
View File
@@ -26,6 +26,7 @@ const TPL = `
color: var(--muted-text-color);
max-height: 100px;
overflow: auto;
transition: opacity .1s linear;
}
.save-attributes-button {
@@ -307,6 +308,12 @@ export default class AttributeEditorWidget extends TabAwareWidget {
await server.put(`notes/${this.noteId}/attributes`, attributes, this.componentId);
this.$saveAttributesButton.fadeOut();
// blink the attribute text to give visual hint that save has been executed
this.$editor.css('opacity', 0);
// revert back
setTimeout(() => this.$editor.css('opacity', 1), 100);
}
}
@@ -465,19 +472,7 @@ export default class AttributeEditorWidget extends TabAwareWidget {
async renderOwnedAttributes(ownedAttributes, saved) {
ownedAttributes = ownedAttributes.filter(oa => !oa.isDeleted);
const $attributesContainer = $("<div>");
for (let i = 0; i < ownedAttributes.length; i++) {
const attribute = ownedAttributes[i];
attributeRenderer.renderAttribute(attribute, $attributesContainer, true);
if (i < ownedAttributes.length - 1) {
$attributesContainer.append(" ");
}
}
let htmlAttrs = $attributesContainer.html();
let htmlAttrs = (await attributeRenderer.renderAttributes(ownedAttributes, true)).html();
if (htmlAttrs.length > 0) {
htmlAttrs += "&nbsp;";
+6 -6
View File
@@ -56,7 +56,7 @@ const TPL = `
cursor: pointer;
}
.attr-expander:not(.error):hover hr {
.attr-expander:hover hr {
border-color: var(--main-text-color);
}
@@ -205,9 +205,9 @@ export default class AttributeListWidget extends TabAwareWidget {
return 'attribute' + (number === 1 ? '' : 's');
}
renderInheritedAttributes(attributes, $container) {
async renderInheritedAttributes(attributes, $container) {
for (const attribute of attributes) {
const $span = $("<span>")
const $attr = (await attributeRenderer.renderAttribute(attribute, false))
.on('click', e => this.attributeDetailWidget.showAttributeDetail({
attribute: {
noteId: attribute.noteId,
@@ -220,9 +220,9 @@ export default class AttributeListWidget extends TabAwareWidget {
y: e.pageY
}));
attributeRenderer.renderAttribute(attribute, $span, false);
$container.append($span);
$container
.append($attr)
.append(" ");
}
}
@@ -10,6 +10,10 @@ const TPL = `
`;
export default class LinkMapWidget extends CollapsibleWidget {
isEnabled() {
return super.isEnabled() && !this.note.hasLabel('linkMapWidgetDisabled');
}
get widgetTitle() { return "Link map"; }
get help() {
@@ -105,4 +109,4 @@ export default class LinkMapWidget extends CollapsibleWidget {
}
}
}
}
}
@@ -42,6 +42,10 @@ const TPL = `
`;
export default class NoteInfoWidget extends CollapsibleWidget {
isEnabled() {
return super.isEnabled() && !this.note.hasLabel('noteInfoWidgetDisabled');
}
get widgetTitle() { return "Note info"; }
async doRenderBody() {
@@ -7,6 +7,10 @@ const TPL = `
`;
class NoteRevisionsWidget extends CollapsibleWidget {
isEnabled() {
return super.isEnabled() && !this.note.hasLabel('noteRevisionsWidgetDisabled');
}
get widgetTitle() { return "Note revisions"; }
get help() {
@@ -1,69 +0,0 @@
import CollapsibleWidget from "../collapsible_widget.js";
import linkService from "../../services/link.js";
import server from "../../services/server.js";
import treeCache from "../../services/tree_cache.js";
export default class SimilarNotesWidget extends CollapsibleWidget {
get widgetTitle() { return "Similar notes"; }
get help() {
return {
title: "This list contains notes which might be similar to the current note based on textual similarity of note title."
};
}
noteSwitched() {
const noteId = this.noteId;
this.$body.empty();
// avoid executing this expensive operation multiple times when just going through notes (with keyboard especially)
// until the users settles on a note
setTimeout(() => {
if (this.noteId === noteId) {
this.refresh();
}
}, 1000);
}
async refreshWithNote(note) {
// remember which title was when we found the similar notes
this.title = note.title;
const similarNotes = await server.get('similar-notes/' + this.noteId);
if (similarNotes.length === 0) {
this.$body.text("No similar notes found ...");
return;
}
const noteIds = similarNotes.flatMap(note => note.notePath);
await treeCache.getNotes(noteIds, true); // preload all at once
const $list = $('<ul>');
for (const similarNote of similarNotes) {
const note = await treeCache.getNote(similarNote.noteId, true);
if (!note) {
continue;
}
const $item = $("<li>")
.append(await linkService.createNoteLink(similarNote.notePath.join("/"), {showNotePath: true}));
$list.append($item);
}
this.$body.empty().append($list);
}
entitiesReloadedEvent({loadResults}) {
if (this.note && this.title !== this.note.title) {
this.rendered = false;
this.refresh();
}
}
}
@@ -2,6 +2,10 @@ import CollapsibleWidget from "../collapsible_widget.js";
import linkService from "../../services/link.js";
export default class WhatLinksHereWidget extends CollapsibleWidget {
isEnabled() {
return super.isEnabled() && !this.note.hasLabel('whatLinksHereWidgetDisabled');
}
get widgetTitle() { return "What links here"; }
get help() {
@@ -1,13 +1,14 @@
import BasicWidget from "../basic_widget.js";
const TPL = `
<button type="button" class="action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
<button type="button" class="action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close" style="padding-top: 10px;">
<span aria-hidden="true">&times;</span>
</button>`;
class CloseDetailButtonWidget extends BasicWidget {
doRender() {
this.$widget = $(TPL);
this.overflowing();
this.$widget.on('click', () => this.triggerCommand('setActiveScreen', {screen:'tree'}));
}
@@ -3,12 +3,14 @@ import appContext from "../../services/app_context.js";
import contextMenu from "../../services/context_menu.js";
import noteCreateService from "../../services/note_create.js";
import branchService from "../../services/branches.js";
import treeService from "../../services/tree.js";
const TPL = `<button type="button" class="action-button bx bx-menu"></button>`;
const TPL = `<button type="button" class="action-button bx bx-menu" style="padding-top: 10px;"></button>`;
class MobileDetailMenuWidget extends BasicWidget {
doRender() {
this.$widget = $(TPL);
this.overflowing();
this.$widget.on("click", async e => {
const note = appContext.tabManager.getActiveTabNote();
@@ -27,9 +29,15 @@ class MobileDetailMenuWidget extends BasicWidget {
noteCreateService.createNote(note.noteId);
}
else if (command === "delete") {
if (await branchService.deleteNotes(note.getBranchIds()[0])) {
// move to the tree
togglePanes();
const notePath = appContext.tabManager.getActiveTabNotePath();
const branchId = await treeService.getBranchIdFromNotePath(notePath);
if (!branchId) {
throw new Error(`Cannot get branchId for notePath ${notePath}`);
}
if (await branchService.deleteNotes([branchId])) {
this.triggerCommand('setActiveScreen', {screen:'tree'})
}
}
else {
@@ -32,8 +32,9 @@ const WIDGET_TPL = `
class MobileGlobalButtonsWidget extends BasicWidget {
doRender() {
return this.$widget = $(WIDGET_TPL);
this.$widget = $(WIDGET_TPL);
this.overflowing();
}
}
export default MobileGlobalButtonsWidget;
export default MobileGlobalButtonsWidget;
+5
View File
@@ -36,6 +36,11 @@ const TPL = `
overflow: hidden;
text-overflow: ellipsis;
}
.note-path-list {
max-height: 600px;
overflow-y: auto;
}
</style>
<div class="current-path"></div>
+10 -9
View File
@@ -32,7 +32,7 @@ const TPL = `
.tree {
height: 100%;
overflow: auto;
padding-bottom: 20px;
padding-bottom: 35px;
}
.refresh-search-button {
@@ -51,7 +51,7 @@ const TPL = `
.tree-settings-button {
position: absolute;
top: 10px;
right: 20px;
right: 10px;
z-index: 100;
}
@@ -104,11 +104,7 @@ const TPL = `
border-radius: 5px;
}
span.fancytree-active:not(.fancytree-focused) .fancytree-title {
border-style: dashed !important;
}
span.fancytree-focused .fancytree-title, span.fancytree-focused.fancytree-selected .fancytree-title {
span.fancytree-active .fancytree-title, span.fancytree-active.fancytree-selected .fancytree-title {
color: var(--active-item-text-color) !important;
background-color: var(--active-item-background-color) !important;
border-color: var(--main-background-color) !important; /* invisible border */
@@ -464,9 +460,11 @@ export default class NoteTreeWidget extends TabAwareWidget {
&& node.data.noteId === hoistedNoteService.getHoistedNoteId()
&& $span.find('.unhoist-button').length === 0) {
const unhoistButton = $('<span>&nbsp; (<a class="unhoist-button">unhoist</a>)</span>');
const unhoistButton = $('<span class="unhoist-button-wrapper" title="Unhoist current note to show the whole note tree">[<a class="unhoist-button">unhoist</a>]</span>');
$span.append(unhoistButton);
// 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
$span.prepend(unhoistButton);
}
const note = await treeCache.getNote(node.data.noteId);
@@ -1068,6 +1066,9 @@ export default class NoteTreeWidget extends TabAwareWidget {
parentNode.addChildren([this.prepareNode(branch, true)]);
this.sortChildren(parentNode);
// this might be a first child which would force an icon change
noteIdsToUpdate.add(branch.parentNoteId);
}
}
}
+2
View File
@@ -64,6 +64,8 @@ export default class NoteTypeWidget extends TabAwareWidget {
const noteType = NOTE_TYPES.find(nt => nt.type === type);
this.save(noteType.type, noteType.mime);
this.$widget.find('.dropdown-toggle').dropdown('toggle');
});
if (this.note.type === noteType.type) {
+178
View File
@@ -0,0 +1,178 @@
import linkService from "../services/link.js";
import server from "../services/server.js";
import treeCache from "../services/tree_cache.js";
import TabAwareWidget from "./tab_aware_widget.js";
import options from "../services/options.js";
const TPL = `
<div class="similar-notes-widget hide-in-zen-mode">
<style>
.similar-notes-wrapper {
max-height: 75px;
overflow: auto;
}
.similar-notes-wrapper a {
display: inline-block;
border: 1px dotted var(--main-border-color);
border-radius: 20px;
background-color: var(--accented-background-color);
padding: 0 10px 0 10px;
margin: 0 3px 0 3px;
max-width: 10em;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.similar-notes-expander {
display: flex;
flex-direction: row;
color: var(--muted-text-color);
font-size: 90%;
m
}
.similar-notes-expander hr {
height: 1px;
border-color: var(--main-border-color);
position: relative;
top: 4px;
margin-top: 5px;
}
.similar-notes-expander-text {
padding-left: 20px;
padding-right: 20px;
white-space: nowrap;
}
.similar-notes-expander:hover {
cursor: pointer;
}
.similar-notes-expander:hover hr {
border-color: var(--main-text-color);
}
.similar-notes-expander:hover .similar-notes-expander-text {
color: var(--main-text-color);
}
</style>
<div class="similar-notes-expander">
<hr class="w-100">
<div class="similar-notes-expander-text"
title="This list contains notes which might be similar to the current note based on textual similarity of note title, its labels and relations."></div>
<hr class="w-100">
</div>
<div class="similar-notes-wrapper"></div>
</div>
`;
export default class SimilarNotesWidget extends TabAwareWidget {
isEnabled() {
return super.isEnabled() && !this.note.hasLabel('similarNotesWidgetDisabled');
}
doRender() {
this.$widget = $(TPL);
this.overflowing();
this.$similarNotesWrapper = this.$widget.find(".similar-notes-wrapper");
this.$similarNotesExpanderText = this.$widget.find(".similar-notes-expander-text");
this.$expander = this.$widget.find('.similar-notes-expander');
this.$expander.on('click', async () => {
const collapse = this.$similarNotesWrapper.is(":visible");
await options.save('similarNotesExpanded', !collapse);
this.triggerEvent(`similarNotesCollapsedStateChanged`, {collapse});
});
return this.$widget;
}
noteSwitched() {
const noteId = this.noteId;
this.toggleInt(false);
this.$similarNotesWrapper.hide(); // we'll open it in refresh() if needed
// avoid executing this expensive operation multiple times when just going through notes (with keyboard especially)
// until the users settles on a note
setTimeout(() => {
if (this.noteId === noteId) {
this.refresh();
}
}, 1000);
}
async refresh() {
if (!this.isEnabled()) {
return;
}
// remember which title was when we found the similar notes
this.title = this.note.title;
const similarNotes = await server.get('similar-notes/' + this.noteId);
this.toggleInt(similarNotes.length > 0);
if (similarNotes.length === 0) {
return;
}
if (options.is('similarNotesExpanded')) {
this.$similarNotesWrapper.show();
}
this.$similarNotesExpanderText.text(`${similarNotes.length} similar note${similarNotes.length === 1 ? '': "s"}`);
const noteIds = similarNotes.flatMap(note => note.notePath);
await treeCache.getNotes(noteIds, true); // preload all at once
const $list = $('<div>');
for (const similarNote of similarNotes) {
const note = await treeCache.getNote(similarNote.noteId, true);
if (!note) {
continue;
}
const $item = (await linkService.createNoteLink(similarNote.notePath.join("/")))
.css("font-size", 24 * (1 - 1 / (1 + similarNote.score)));
$list.append($item);
}
this.$similarNotesWrapper.empty().append($list);
}
entitiesReloadedEvent({loadResults}) {
if (this.note && this.title !== this.note.title) {
this.rendered = false;
this.refresh();
}
}
/**
* 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.
*/
similarNotesCollapsedStateChangedEvent({collapse}) {
if (collapse) {
this.$similarNotesWrapper.slideUp(200);
} else {
this.$similarNotesWrapper.slideDown(200);
}
}
}
@@ -82,7 +82,7 @@ export default class StandardTopWidget extends BasicWidget {
this.$leaveProtectedSessionButton = this.$widget.find(".leave-protected-session-button");
this.$leaveProtectedSessionButton.on('click', protectedSessionService.leaveProtectedSession);
return this.$widget
return this.$widget;
}
protectedSessionStartedEvent() {
@@ -18,6 +18,7 @@ const TPL = `
height: 100%;
padding: 10px;
font-family: var(--detail-text-font-family);
position: relative;
}
.note-detail-readonly-text p:first-child, .note-detail-editable-text::before {
+19 -2
View File
@@ -387,12 +387,19 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
}
.tooltip-inner {
padding: 15px 15px 0 15px;
background-color: var(--tooltip-background-color) !important;
border: 1px solid var(--main-border-color);
border-radius: 5px;
text-align: left;
color: var(--main-text-color) !important;
max-width: 500px;
box-shadow: 10px 10px 93px -25px #aaaaaa;
}
.tooltip-attributes {
color: var(--muted-text-color);
margin-bottom: 7px;
}
.note-tooltip-content {
@@ -533,6 +540,11 @@ div[data-notify="container"] {
padding: 2px;
}
.unhoist-button-wrapper {
position: relative;
top: -4px;
}
.unhoist-button {
text-decoration: underline !important;
color: var(--link-color) !important;
@@ -637,7 +649,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
}
.component {
contain: layout size;
contain: size;
}
.toast {
@@ -824,7 +836,12 @@ body {
}
.ck.ck-mentions > .ck-list__item {
max-width: 600px;
max-width: 700px;
}
.ck.ck-mentions > .ck-list__item .ck-button {
/* wrap text for very long note path title */
white-space: normal !important;
}
ul.fancytree-container li {
+9 -6
View File
@@ -53,7 +53,7 @@ function addClipping(req) {
clippingNote.setLabel('pageUrl', pageUrl);
}
const rewrittenContent = addImagesToNote(images, clippingNote, content);
const rewrittenContent = processContent(images, clippingNote, content);
const existingContent = clippingNote.getContent();
@@ -85,7 +85,7 @@ function createNote(req) {
note.setLabel('pageUrl', pageUrl);
}
const rewrittenContent = addImagesToNote(images, note, content);
const rewrittenContent = processContent(images, note, content);
note.setContent(rewrittenContent);
@@ -94,8 +94,11 @@ function createNote(req) {
};
}
function addImagesToNote(images, note, content) {
let rewrittenContent = content;
function processContent(images, note, content) {
// H1 is not supported so convert it to H2
let rewrittenContent = content
.replace(/<h1/ig, "<h2")
.replace(/<\/h1/ig, "</h2");
if (images) {
for (const {src, dataUrl, imageId} of images) {
@@ -113,8 +116,8 @@ function addImagesToNote(images, note, content) {
new Attribute({
noteId: imageNote.noteId,
type: 'label',
name: 'hideInAutocomplete'
}).save();
name: 'archived'
}).save(); // so that these image notes don't show up in search / autocomplete
new Attribute({
noteId: note.noteId,
+2 -1
View File
@@ -86,8 +86,9 @@ function downloadNoteRevision(req, res) {
function eraseOneNoteRevision(noteRevision) {
noteRevision.isErased = true;
noteRevision.title = null;
noteRevision.setContent(null);
noteRevision.save();
noteRevision.setContent(null);
}
function eraseAllNoteRevisions(req) {
+5 -3
View File
@@ -139,7 +139,7 @@ function getRelationMap(req) {
for (const note of notes) {
resp.noteTitles[note.noteId] = note.title;
resp.relations = resp.relations.concat((note.getRelations())
resp.relations = resp.relations.concat(note.getRelations()
.filter(relation => noteIds.includes(relation.value))
.map(relation => ({
attributeId: relation.attributeId,
@@ -149,8 +149,10 @@ function getRelationMap(req) {
})));
for (const relationDefinition of note.getRelationDefinitions()) {
if (relationDefinition.value.inverseRelation) {
resp.inverseRelations[relationDefinition.name] = relationDefinition.value.inverseRelation;
const def = relationDefinition.getDefinition();
if (def.inverseRelation) {
resp.inverseRelations[relationDefinition.getDefinedName()] = def.inverseRelation;
}
}
}
+2 -1
View File
@@ -39,7 +39,8 @@ const ALLOWED_OPTIONS = new Set([
'rightPaneVisible',
'nativeTitleBarVisible',
'attributeListExpanded',
'promotedAttributesExpanded'
'promotedAttributesExpanded',
'similarNotesExpanded'
]);
function getOptions() {
+23 -5
View File
@@ -1,13 +1,19 @@
"use strict";
const repository = require('../../services/repository');
const noteCacheService = require('../../services/note_cache/note_cache_service');
const SearchContext = require('../../services/search/search_context.js');
const log = require('../../services/log');
const scriptService = require('../../services/script');
const searchService = require('../../services/search/services/search');
function searchNotes(req) {
const {count, results} = searchService.searchTrimmedNotes(req.params.searchString);
const searchContext = new SearchContext({
includeNoteContent: req.query.includeNoteContent === 'true',
excludeArchived: req.query.excludeArchived === 'true',
fuzzyAttributeSearch: req.query.fuzzyAttributeSearch === 'true'
});
const {count, results} = searchService.searchTrimmedNotes(req.params.searchString, searchContext);
try {
return {
@@ -52,7 +58,13 @@ async function searchFromNote(req) {
searchResultNoteIds = await searchFromRelation(note, relationName);
} else {
searchResultNoteIds = searchService.searchNotes(json.searchString)
const searchContext = new SearchContext({
includeNoteContent: true,
excludeArchived: true,
fuzzyAttributeSearch: false
});
searchResultNoteIds = searchService.findNotesWithQuery(json.searchString, searchContext)
.map(sr => sr.noteId);
}
}
@@ -113,8 +125,14 @@ async function searchFromRelation(note, relationName) {
function getRelatedNotes(req) {
const attr = req.body;
const matchingNameAndValue = searchService.searchNotes(formatAttrForSearch(attr, true));
const matchingName = searchService.searchNotes(formatAttrForSearch(attr, false));
const searchSettings = {
includeNoteContent: false,
excludeArchived: true,
fuzzyAttributeSearch: false
};
const matchingNameAndValue = searchService.findNotesWithQuery(formatAttrForSearch(attr, true), new SearchContext(searchSettings));
const matchingName = searchService.findNotesWithQuery(formatAttrForSearch(attr, false), new SearchContext(searchSettings));
const results = [];
+3 -6
View File
@@ -1,9 +1,9 @@
"use strict";
const noteCacheService = require('../../services/note_cache/note_cache_service');
const similarityService = require('../../services/note_cache/similarity.js');
const repository = require('../../services/repository');
function getSimilarNotes(req) {
async function getSimilarNotes(req) {
const noteId = req.params.noteId;
const note = repository.getNote(noteId);
@@ -12,10 +12,7 @@ function getSimilarNotes(req) {
return [404, `Note ${noteId} not found.`];
}
const results = noteCacheService.findSimilarNotes(noteId);
return results
.filter(note => note.noteId !== noteId);
return await similarityService.findSimilarNotes(noteId);
}
module.exports = {
+1 -1
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 = 167;
const APP_DB_VERSION = 169;
const SYNC_VERSION = 16;
const CLIPPER_PROTOCOL_VERSION = "1.0";
+5
View File
@@ -27,6 +27,11 @@ const BUILTIN_ATTRIBUTES = [
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
{ type: 'label', name: 'bookZoomLevel', isDangerous: false },
{ type: 'label', name: 'widget', isDangerous: true },
{ type: 'label', name: 'noteInfoWidgetDisabled' },
{ type: 'label', name: 'linkMapWidgetDisabled' },
{ type: 'label', name: 'noteRevisionsWidgetDisabled' },
{ type: 'label', name: 'whatLinksHereWidgetDisabled' },
{ type: 'label', name: 'similarNotesWidgetDisabled' },
// relation names
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
+12 -3
View File
@@ -11,7 +11,8 @@ const axios = require('axios');
const dayjs = require('dayjs');
const cloningService = require('./cloning');
const appInfo = require('./app_info');
const searchService = require('./search/services/search.js');
const searchService = require('./search/services/search');
const SearchContext = require("./search/search_context.js");
/**
* This is the main backend API interface for scripts. It's published in the local "api" object.
@@ -90,10 +91,18 @@ function BackendScriptApi(currentNote, apiParams) {
* "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @param {string} query
* @param {SearchContext} [searchContext]
* @returns {Note[]}
*/
this.searchForNotes = searchService.searchNoteEntities;
this.searchForNotes = (query, searchContext) => {
searchContext = searchContext || new SearchContext();
const noteIds = searchService.findNotesWithQuery(query, searchContext)
.map(sr => sr.noteId);
return repository.getNotes(noteIds);
};
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
+1 -1
View File
@@ -1 +1 @@
module.exports = { buildDate:"2020-09-05T23:43:06+02:00", buildRevision: "a7505682eddb754f0ba951716f63f9e9ffe61131" };
module.exports = { buildDate:"2020-09-19T23:45:46+02:00", buildRevision: "27052143abdd9c093f46e30ae9eac09eb423b22f" };
+1 -3
View File
@@ -500,9 +500,7 @@ class ConsistencyChecks {
FROM attributes
WHERE isDeleted = 0
AND type != 'label'
AND type != 'label-definition'
AND type != 'relation'
AND type != 'relation-definition'`,
AND type != 'relation'`,
({attributeId, type}) => {
if (this.autoFix) {
const attribute = repository.getAttribute(attributeId);
+2 -2
View File
@@ -250,14 +250,14 @@ function importEnex(taskContext, file, parentNote) {
}
const createFileNote = () => {
const resourceNote = (noteService.createNewNote({
const resourceNote = noteService.createNewNote({
parentNoteId: noteEntity.noteId,
title: resource.title,
content: resource.content,
type: 'file',
mime: resource.mime,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note;
}).note;
for (const attr of resource.attributes) {
noteEntity.addAttribute(attr.type, attr.name, attr.value);
+1 -1
View File
@@ -364,7 +364,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
},
{
actionName: "findInText",
defaultShortcuts: ["CommandOrControl+F"],
defaultShortcuts: isElectron ? ["CommandOrControl+F"] : [],
scope: "window"
},
{
+25 -15
View File
@@ -138,12 +138,6 @@ class Note {
return this.hasAttribute('label', 'archived');
}
get isHideInAutocompleteOrArchived() {
return this.attributes.find(attr =>
attr.type === 'label'
&& ["archived", "hideInAutocomplete"].includes(attr.name));
}
get hasInheritableOwnedArchivedLabel() {
return !!this.ownedAttributes.find(attr => attr.type === 'label' && attr.name === 'archived' && attr.isInheritable);
}
@@ -155,20 +149,19 @@ class Note {
}
/**
* @return {string} - returns flattened textual representation of note, prefixes and attributes usable for searching
* This is used for:
* - fast searching
* - note similarity evaluation
*
* @return {string} - returns flattened textual representation of note, prefixes and attributes
*/
get flatText() {
if (!this.flatTextCache) {
if (this.isHideInAutocompleteOrArchived) {
this.flatTextCache = " "; // can't be empty
return this.flatTextCache;
}
this.flatTextCache = this.noteId + ' ' + this.type + ' ' + this.mime;
this.flatTextCache = this.noteId + ' ' + this.type + ' ' + this.mime + ' ';
for (const branch of this.parentBranches) {
if (branch.prefix) {
this.flatTextCache += branch.prefix + ' - ';
this.flatTextCache += branch.prefix + ' ';
}
}
@@ -176,11 +169,13 @@ class Note {
for (const attr of this.attributes) {
// it's best to use space as separator since spaces are filtered from the search string by the tokenization into words
this.flatTextCache += ' ' + (attr.type === 'label' ? '#' : '@') + attr.name;
this.flatTextCache += (attr.type === 'label' ? '#' : '~') + attr.name;
if (attr.value) {
this.flatTextCache += '=' + attr.value;
}
this.flatTextCache += ' ';
}
this.flatTextCache = this.flatTextCache.toLowerCase();
@@ -337,6 +332,21 @@ class Note {
this.isDecrypted = true;
}
}
// for logging etc
get pojo() {
const pojo = {...this};
delete pojo.noteCache;
delete pojo.ancestorCache;
delete pojo.attributeCache;
delete pojo.flatTextCache;
delete pojo.children;
delete pojo.parents;
delete pojo.parentBranches;
return pojo;
}
}
module.exports = Note;
+1 -60
View File
@@ -3,7 +3,6 @@
const noteCache = require('./note_cache');
const hoistedNoteService = require('../hoisted_note');
const protectedSessionService = require('../protected_session');
const stringSimilarity = require('string-similarity');
const log = require('../log');
function isNotePathArchived(notePath) {
@@ -174,64 +173,6 @@ function getNotePath(noteId) {
}
}
function evaluateSimilarity(sourceNote, candidateNote, results) {
let coeff = stringSimilarity.compareTwoStrings(sourceNote.flatText, candidateNote.flatText);
if (coeff > 0.4) {
const notePath = getSomePath(candidateNote);
// this takes care of note hoisting
if (!notePath) {
return;
}
if (isNotePathArchived(notePath)) {
coeff -= 0.2; // archived penalization
}
results.push({coeff, notePath, noteId: candidateNote.noteId});
}
}
/**
* Point of this is to break up long running sync process to avoid blocking
* see https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/
*/
function setImmediatePromise() {
return new Promise((resolve) => {
setTimeout(() => resolve(), 0);
});
}
function findSimilarNotes(noteId) {
const results = [];
let i = 0;
const origNote = noteCache.notes[noteId];
if (!origNote) {
return [];
}
for (const note of Object.values(noteCache.notes)) {
if (note.isProtected && !note.isDecrypted) {
continue;
}
evaluateSimilarity(origNote, note, results);
i++;
if (i % 200 === 0) {
setImmediatePromise();
}
}
results.sort((a, b) => a.coeff > b.coeff ? -1 : 1);
return results.length > 50 ? results.slice(0, 50) : results;
}
/**
* @param noteId
* @returns {boolean} - true if note exists (is not deleted) and is available in current note hoisting
@@ -250,5 +191,5 @@ module.exports = {
isAvailable,
isArchived,
isInAncestor,
findSimilarNotes
isNotePathArchived
};
+394
View File
@@ -0,0 +1,394 @@
const noteCache = require('./note_cache');
const noteCacheService = require('./note_cache_service.js');
const dateUtils = require('../date_utils');
const DEBUG = true;
const IGNORED_ATTRS = [
"datenote",
"monthnote",
"yearnote"
];
const IGNORED_ATTR_NAMES = [
"includenotelink",
"internallink",
"imagelink",
"relationmaplink",
"template",
"disableversioning",
"archived",
"hidepromotedattributes",
"keyboardshortcut",
"bookzoomlevel",
"noteinfowidgetdisabled",
"linkmapwidgetdisabled",
"noterevisionswidgetdisabled",
"whatlinksherewidgetdisabled",
"similarnoteswidgetdisabled",
"disableinclusion",
"rendernote",
"pageurl",
];
function filterUrlValue(value) {
return value
.replace(/https?:\/\//ig, "")
.replace(/www\./ig, "")
.replace(/(\.net|\.com|\.org|\.info|\.edu)/ig, "");
}
/**
* @param {Note} note
*/
function buildRewardMap(note) {
const map = {};
function addToRewardMap(text, rewardFactor) {
if (!text) {
return;
}
for (const word of splitToWords(text)) {
if (word) {
map[word] = map[word] || 0;
// reward grows with the length of matched string
const length = word.length
- 0.9; // to penalize specifically very short words - 1 and 2 characters
map[word] += rewardFactor * Math.pow(length, 0.7);
}
}
}
for (const ancestorNote of note.ancestors) {
if (ancestorNote.noteId === 'root') {
continue;
}
if (ancestorNote.isDecrypted) {
addToRewardMap(ancestorNote.title, 0.3);
}
for (const branch of ancestorNote.parentBranches) {
addToRewardMap(branch.prefix, 0.3);
}
}
addToRewardMap(trimMime(note.mime), 0.5);
if (note.isDecrypted) {
addToRewardMap(note.title, 1);
}
for (const branch of note.parentBranches) {
addToRewardMap(branch.prefix, 1);
}
for (const attr of note.attributes) {
if (attr.name.startsWith('child:')
|| attr.name.startsWith('relation:')
|| attr.name.startsWith('label:')) {
continue;
}
// inherited notes get small penalization
let reward = note.noteId === attr.noteId ? 0.8 : 0.5;
if (IGNORED_ATTRS.includes(attr.name)) {
continue;
}
if (!IGNORED_ATTR_NAMES.includes(attr.name)) {
addToRewardMap(attr.name, reward);
}
let value = attr.value;
if (value.startsWith('http')) {
value = filterUrlValue(value);
// words in URLs are not that valuable
reward = reward / 2;
}
addToRewardMap(value, reward);
}
return map;
}
const mimeCache = {};
function trimMime(mime) {
if (!mime || mime === 'text/html') {
return;
}
if (!(mime in mimeCache)) {
const chunks = mime.split('/');
let str = "";
if (chunks.length >= 2) {
// we're not interested in 'text/' or 'application/' prefix
str = chunks[1];
if (str.startsWith('-x')) {
str = str.substr(2);
}
}
mimeCache[mime] = str;
mimeCache[mime] = str;
}
return mimeCache[mime];
}
function buildDateLimits(baseNote) {
const dateCreatedTs = dateUtils.parseDateTime(baseNote.utcDateCreated);
return {
minDate: dateUtils.utcDateStr(new Date(dateCreatedTs - 3600)),
minExcludedDate: dateUtils.utcDateStr(new Date(dateCreatedTs - 5)),
maxExcludedDate: dateUtils.utcDateStr(new Date(dateCreatedTs + 5)),
maxDate: dateUtils.utcDateStr(new Date(dateCreatedTs + 3600)),
};
}
const wordCache = {};
const WORD_BLACKLIST = [
"a", "the", "in", "for", "from", "but", "s", "so", "if", "while", "until",
"whether", "after", "before", "because", "since", "when", "where", "how",
"than", "then", "and", "either", "or", "neither", "nor", "both", "also"
];
function splitToWords(text) {
let words = wordCache[text];
if (!words) {
wordCache[text] = words = text.toLowerCase().split(/[^\p{L}\p{N}]+/u);
for (const idx in words) {
if (WORD_BLACKLIST.includes(words[idx])) {
words[idx] = "";
}
// special case for english plurals
else if (words[idx].length > 2 && words[idx].endsWith("es")) {
words[idx] = words[idx].substr(0, words[idx] - 2);
}
else if (words[idx].length > 1 && words[idx].endsWith("s")) {
words[idx] = words[idx].substr(0, words[idx] - 1);
}
}
}
return words;
}
/**
* includeNoteLink and imageLink relation mean that notes are clearly related, but so clearly
* that it doesn't actually need to be shown to the user.
*/
function hasConnectingRelation(sourceNote, targetNote) {
return sourceNote.attributes.find(attr => attr.type === 'relation'
&& ['includenotelink', 'imagelink'].includes(attr.name)
&& attr.value === targetNote.noteId);
}
async function findSimilarNotes(noteId) {
const results = [];
let i = 0;
const baseNote = noteCache.notes[noteId];
if (!baseNote) {
return [];
}
const dateLimits = buildDateLimits(baseNote);
const rewardMap = buildRewardMap(baseNote);
const ancestorRewardCache = {};
const ancestorNoteIds = new Set(baseNote.ancestors.map(note => note.noteId));
let displayRewards = false;
function gatherRewards(text, factor = 1) {
if (!text) {
return 0;
}
let counter = 0;
// when the title is very long then weight of each individual word should be lower
// also pretty important in e.g. long URLs in label values
const lengthPenalization = 1 / Math.pow(text.length, 0.3);
for (const word of splitToWords(text)) {
const reward = rewardMap[word] * factor * lengthPenalization || 0;
if (displayRewards && reward > 0) {
console.log(`Reward ${Math.round(reward * 10) / 10} for word: ${word}`);
}
counter += reward;
}
return counter;
}
function gatherAncestorRewards(note) {
if (!(note.noteId in ancestorRewardCache)) {
let score = 0;
for (const parentNote of note.parents) {
if (!ancestorNoteIds.has(parentNote.noteId)) {
if (parentNote.isDecrypted) {
score += gatherRewards(parentNote.title, 0.3);
}
for (const branch of parentNote.parentBranches) {
score += gatherRewards(branch.prefix, 0.3)
+ gatherAncestorRewards(branch.parentNote);
}
}
}
ancestorRewardCache[note.noteId] = score;
}
return ancestorRewardCache[note.noteId];
}
function computeScore(candidateNote) {
let score = gatherRewards(trimMime(candidateNote.mime))
+ gatherAncestorRewards(candidateNote);
if (candidateNote.isDecrypted) {
score += gatherRewards(candidateNote.title);
}
for (const branch of candidateNote.parentBranches) {
score += gatherRewards(branch.prefix);
}
for (const attr of candidateNote.attributes) {
if (attr.name.startsWith('child:')
|| attr.name.startsWith('relation:')
|| attr.name.startsWith('label:')) {
continue;
}
if (IGNORED_ATTRS.includes(attr.name)) {
continue;
}
if (!IGNORED_ATTR_NAMES.includes(attr.name)) {
score += gatherRewards(attr.name);
}
let value = attr.value;
let factor = 1;
if (value.startsWith('http')) {
value = filterUrlValue(value);
// words in URLs are not that valuable
factor = 0.5;
}
score += gatherRewards(attr.value, factor);
}
if (candidateNote.type === baseNote.type) {
score += 0.2;
}
/**
* We want to improve standing of notes which have been created in similar time to each other since
* there's a good chance they are related.
*
* But there's an exception - if they were created really close to each other (withing few seconds) then
* they are probably part of the import and not created by hand - these OTOH should not benefit.
*/
const {utcDateCreated} = candidateNote;
if (utcDateCreated < dateLimits.minExcludedDate && utcDateCreated > dateLimits.maxExcludedDate) {
if (utcDateCreated >= dateLimits.minDate && utcDateCreated <= dateLimits.maxDate) {
score += 1;
}
else if (utcDateCreated.substr(0, 10) === dateLimits.minDate.substr(0, 10) || utcDateCreated.substr(0, 10) === dateLimits.maxDate.substr(0, 10)) {
// smaller bonus when outside of the window but within same date
score += 0.5;
}
}
return score;
}
for (const candidateNote of Object.values(noteCache.notes)) {
if (candidateNote.noteId === baseNote.noteId
|| hasConnectingRelation(candidateNote, baseNote)
|| hasConnectingRelation(baseNote, candidateNote)) {
continue;
}
let score = computeScore(candidateNote);
if (score >= 2) {
const notePath = noteCacheService.getSomePath(candidateNote);
// this takes care of note hoisting
if (!notePath) {
return;
}
if (noteCacheService.isNotePathArchived(notePath)) {
score -= 0.5; // archived penalization
}
results.push({score, notePath, noteId: candidateNote.noteId});
}
i++;
if (i % 1000 === 0) {
await setImmediatePromise();
}
}
results.sort((a, b) => a.score > b.score ? -1 : 1);
if (DEBUG) {
console.log("REWARD MAP", rewardMap);
if (results.length >= 1) {
const note = noteCache.notes[results[0].noteId];
console.log("WINNER", note.pojo);
displayRewards = true;
const totalReward = computeScore(note);
console.log("Total reward:", Math.round(totalReward * 10) / 10);
}
}
return results.length > 200 ? results.slice(0, 200) : results;
}
/**
* Point of this is to break up long running sync process to avoid blocking
* see https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/
*/
function setImmediatePromise() {
return new Promise((resolve) => {
setTimeout(() => resolve(), 0);
});
}
module.exports = {
findSimilarNotes
};
+15 -3
View File
@@ -30,6 +30,14 @@ function createNoteRevision(note) {
return;
}
const content = note.getContent();
if (!content) {
return;
}
const contentMetadata = note.getContentMetadata();
const noteRevision = new NoteRevision({
noteId: note.noteId,
// title and text should be decrypted now
@@ -37,14 +45,18 @@ function createNoteRevision(note) {
type: note.type,
mime: note.mime,
isProtected: false, // will be fixed in the protectNoteRevisions() call
utcDateLastEdited: note.utcDateModified,
utcDateLastEdited: note.utcDateModified > contentMetadata.utcDateModified
? note.utcDateModified
: contentMetadata.utcDateModified,
utcDateCreated: dateUtils.utcNowDateTime(),
utcDateModified: dateUtils.utcNowDateTime(),
dateLastEdited: note.dateModified,
dateLastEdited: note.dateModified > contentMetadata.dateModified
? note.dateModified
: contentMetadata.dateModified,
dateCreated: dateUtils.localNowDateTime()
}).save();
noteRevision.setContent(note.getContent());
noteRevision.setContent(content);
return noteRevision;
}
+4 -16
View File
@@ -55,13 +55,15 @@ function deriveMime(type, mime) {
mime = 'application/json';
} else if (['render', 'book'].includes(type)) {
mime = '';
} else {
mime = 'application/octet-stream';
}
return mime;
}
function copyChildAttributes(parentNote, childNote) {
for (const attr of parentNote.getOwnedAttributes()) {
for (const attr of parentNote.getAttributes()) {
if (attr.name.startsWith("child:")) {
new Attribute({
noteId: childNote.noteId,
@@ -464,21 +466,7 @@ function saveNoteRevision(note) {
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime();
if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
const noteRevision = new NoteRevision({
noteId: note.noteId,
// title and text should be decrypted now
title: note.title,
type: note.type,
mime: note.mime,
isProtected: false, // will be fixed in the protectNoteRevisions() call
utcDateLastEdited: note.utcDateModified,
utcDateCreated: dateUtils.utcNowDateTime(),
utcDateModified: dateUtils.utcNowDateTime(),
dateLastEdited: note.dateModified,
dateCreated: dateUtils.localNowDateTime()
}).save();
noteRevision.setContent(note.getContent());
noteRevisionService.createNoteRevision(note);
}
}
+3 -1
View File
@@ -86,7 +86,9 @@ const defaultOptions = [
{ name: 'hideArchivedNotes_main', value: 'false', isSynced: false },
{ name: 'hideIncludedImages_main', value: 'true', isSynced: false },
{ name: 'attributeListExpanded', value: 'false', isSynced: false },
{ name: 'promotedAttributesExpanded', value: 'false', isSynced: true }
{ name: 'promotedAttributesExpanded', value: 'true', isSynced: true },
{ name: 'similarNotesExpanded', value: 'true', isSynced: true },
{ name: 'debugModeEnabled', value: 'false', isSynced: false }
];
function initStartupOptions() {
@@ -0,0 +1,35 @@
function parse(value) {
const tokens = value.split(',').map(t => t.trim());
const defObj = {};
for (const token of tokens) {
if (token === 'promoted') {
defObj.isPromoted = true;
}
else if (['text', 'number', 'boolean', 'date', 'url'].includes(token)) {
defObj.labelType = token;
}
else if (['single', 'multi'].includes(token)) {
defObj.multiplicity = token;
}
else if (token.startsWith('precision')) {
const chunks = token.split('=');
defObj.numberPrecision = parseInt(chunks[1]);
}
else if (token.startsWith('inverse')) {
const chunks = token.split('=');
defObj.inverseRelation = chunks[1];
}
else {
console.log("Unrecognized attribute definition token:", token);
}
}
return defObj;
}
module.exports = {
parse
};
@@ -1,8 +1,9 @@
"use strict";
class ParsingContext {
class SearchContext {
constructor(params = {}) {
this.includeNoteContent = !!params.includeNoteContent;
this.excludeArchived = !!params.excludeArchived;
this.fuzzyAttributeSearch = !!params.fuzzyAttributeSearch;
this.highlightedTokens = [];
this.originalQuery = "";
@@ -17,4 +18,4 @@ class ParsingContext {
}
}
module.exports = ParsingContext;
module.exports = SearchContext;
+37 -43
View File
@@ -18,39 +18,32 @@ const OrderByAndLimitExp = require('../expressions/order_by_and_limit.js');
const buildComparator = require('./build_comparator.js');
const ValueExtractor = require('../value_extractor.js');
function getFulltext(tokens, parsingContext) {
function getFulltext(tokens, searchContext) {
tokens = tokens.map(t => t.token);
parsingContext.highlightedTokens.push(...tokens);
searchContext.highlightedTokens.push(...tokens);
if (tokens.length === 0) {
return null;
}
let textSearchExpression;
if (parsingContext.includeNoteContent) {
textSearchExpression = new OrExp([
if (searchContext.includeNoteContent) {
return new OrExp([
new NoteCacheFulltextExp(tokens),
new NoteContentProtectedFulltextExp('*=*', tokens),
new NoteContentUnprotectedFulltextExp('*=*', tokens)
]);
}
else {
textSearchExpression = new NoteCacheFulltextExp(tokens);
return new NoteCacheFulltextExp(tokens);
}
return new AndExp([
textSearchExpression,
new PropertyComparisonExp("isarchived", buildComparator("=", "false"))
]);
}
function isOperator(str) {
return str.match(/^[=<>*]+$/);
}
function getExpression(tokens, parsingContext, level = 0) {
function getExpression(tokens, searchContext, level = 0) {
if (tokens.length === 0) {
return null;
}
@@ -63,11 +56,11 @@ function getExpression(tokens, parsingContext, level = 0) {
function context(i) {
let {startIndex, endIndex} = tokens[i];
startIndex = Math.max(0, startIndex - 20);
endIndex = Math.min(parsingContext.originalQuery.length, endIndex + 20);
endIndex = Math.min(searchContext.originalQuery.length, endIndex + 20);
return '"' + (startIndex !== 0 ? "..." : "")
+ parsingContext.originalQuery.substr(startIndex, endIndex - startIndex)
+ (endIndex !== parsingContext.originalQuery.length ? "..." : "") + '"';
+ searchContext.originalQuery.substr(startIndex, endIndex - startIndex)
+ (endIndex !== searchContext.originalQuery.length ? "..." : "") + '"';
}
function resolveConstantOperand() {
@@ -75,7 +68,7 @@ function getExpression(tokens, parsingContext, level = 0) {
if (!operand.inQuotes
&& (operand.token.startsWith('#') || operand.token.startsWith('~') || operand.token === 'note')) {
parsingContext.addError(`Error near token "${operand.token}" in ${context(i)}, it's possible to compare with constant only.`);
searchContext.addError(`Error near token "${operand.token}" in ${context(i)}, it's possible to compare with constant only.`);
return null;
}
@@ -121,7 +114,7 @@ function getExpression(tokens, parsingContext, level = 0) {
function parseNoteProperty() {
if (tokens[i].token !== '.') {
parsingContext.addError('Expected "." to separate field path');
searchContext.addError('Expected "." to separate field path');
return;
}
@@ -133,7 +126,7 @@ function getExpression(tokens, parsingContext, level = 0) {
const operator = tokens[i].token;
if (!isOperator(operator)) {
parsingContext.addError(`After content expected operator, but got "${tokens[i].token}" in ${context(i)}`);
searchContext.addError(`After content expected operator, but got "${tokens[i].token}" in ${context(i)}`);
return;
}
@@ -165,7 +158,7 @@ function getExpression(tokens, parsingContext, level = 0) {
if (tokens[i].token === 'labels') {
if (tokens[i + 1].token !== '.') {
parsingContext.addError(`Expected "." to separate field path, got "${tokens[i + 1].token}" in ${context(i)}`);
searchContext.addError(`Expected "." to separate field path, got "${tokens[i + 1].token}" in ${context(i)}`);
return;
}
@@ -176,7 +169,7 @@ function getExpression(tokens, parsingContext, level = 0) {
if (tokens[i].token === 'relations') {
if (tokens[i + 1].token !== '.') {
parsingContext.addError(`Expected "." to separate field path, got "${tokens[i + 1].token}" in ${context(i)}`);
searchContext.addError(`Expected "." to separate field path, got "${tokens[i + 1].token}" in ${context(i)}`);
return;
}
@@ -187,13 +180,13 @@ function getExpression(tokens, parsingContext, level = 0) {
if (tokens[i].token === 'text') {
if (tokens[i + 1].token !== '*=*') {
parsingContext.addError(`Virtual attribute "note.text" supports only *=* operator, instead given "${tokens[i + 1].token}" in ${context(i)}`);
searchContext.addError(`Virtual attribute "note.text" supports only *=* operator, instead given "${tokens[i + 1].token}" in ${context(i)}`);
return;
}
i += 2;
return getFulltext([tokens[i]], parsingContext);
return getFulltext([tokens[i]], searchContext);
}
if (PropertyComparisonExp.isProperty(tokens[i].token)) {
@@ -203,7 +196,7 @@ function getExpression(tokens, parsingContext, level = 0) {
const comparator = buildComparator(operator, comparedValue);
if (!comparator) {
parsingContext.addError(`Can't find operator '${operator}' in ${context(i)}`);
searchContext.addError(`Can't find operator '${operator}' in ${context(i)}`);
return;
}
@@ -212,7 +205,7 @@ function getExpression(tokens, parsingContext, level = 0) {
return new PropertyComparisonExp(propertyName, comparator);
}
parsingContext.addError(`Unrecognized note property "${tokens[i].token}" in ${context(i)}`);
searchContext.addError(`Unrecognized note property "${tokens[i].token}" in ${context(i)}`);
}
function parseAttribute(name) {
@@ -232,7 +225,7 @@ function getExpression(tokens, parsingContext, level = 0) {
}
function parseLabel(labelName) {
parsingContext.highlightedTokens.push(labelName);
searchContext.highlightedTokens.push(labelName);
if (i < tokens.length - 2 && isOperator(tokens[i + 1].token)) {
let operator = tokens[i + 1].token;
@@ -245,33 +238,33 @@ function getExpression(tokens, parsingContext, level = 0) {
return;
}
parsingContext.highlightedTokens.push(comparedValue);
searchContext.highlightedTokens.push(comparedValue);
if (parsingContext.fuzzyAttributeSearch && operator === '=') {
if (searchContext.fuzzyAttributeSearch && operator === '=') {
operator = '*=*';
}
const comparator = buildComparator(operator, comparedValue);
if (!comparator) {
parsingContext.addError(`Can't find operator '${operator}' in ${context(i - 1)}`);
searchContext.addError(`Can't find operator '${operator}' in ${context(i - 1)}`);
} else {
return new LabelComparisonExp('label', labelName, comparator);
}
} else {
return new AttributeExistsExp('label', labelName, parsingContext.fuzzyAttributeSearch);
return new AttributeExistsExp('label', labelName, searchContext.fuzzyAttributeSearch);
}
}
function parseRelation(relationName) {
parsingContext.highlightedTokens.push(relationName);
searchContext.highlightedTokens.push(relationName);
if (i < tokens.length - 2 && tokens[i + 1].token === '.') {
i += 1;
return new RelationWhereExp(relationName, parseNoteProperty());
} else {
return new AttributeExistsExp('relation', relationName, parsingContext.fuzzyAttributeSearch);
return new AttributeExistsExp('relation', relationName, searchContext.fuzzyAttributeSearch);
}
}
@@ -300,7 +293,7 @@ function getExpression(tokens, parsingContext, level = 0) {
const valueExtractor = new ValueExtractor(propertyPath);
if (valueExtractor.validate()) {
parsingContext.addError(valueExtractor.validate());
searchContext.addError(valueExtractor.validate());
}
orderDefinitions.push({
@@ -328,7 +321,7 @@ function getExpression(tokens, parsingContext, level = 0) {
for (i = 0; i < tokens.length; i++) {
if (Array.isArray(tokens[i])) {
expressions.push(getExpression(tokens[i], parsingContext, level++));
expressions.push(getExpression(tokens[i], searchContext, level++));
continue;
}
@@ -343,7 +336,7 @@ function getExpression(tokens, parsingContext, level = 0) {
}
else if (['orderby', 'limit'].includes(token)) {
if (level !== 0) {
parsingContext.addError('orderBy can appear only on the top expression level');
searchContext.addError('orderBy can appear only on the top expression level');
continue;
}
@@ -361,11 +354,11 @@ function getExpression(tokens, parsingContext, level = 0) {
i += 1;
if (!Array.isArray(tokens[i])) {
parsingContext.addError(`not keyword should be followed by sub-expression in parenthesis, got ${tokens[i].token} instead`);
searchContext.addError(`not keyword should be followed by sub-expression in parenthesis, got ${tokens[i].token} instead`);
continue;
}
expressions.push(new NotExp(getExpression(tokens[i], parsingContext, level++)));
expressions.push(new NotExp(getExpression(tokens[i], searchContext, level++)));
}
else if (token === 'note') {
i++;
@@ -379,14 +372,14 @@ function getExpression(tokens, parsingContext, level = 0) {
op = token;
}
else if (op !== token) {
parsingContext.addError('Mixed usage of AND/OR - always use parenthesis to group AND/OR expressions.');
searchContext.addError('Mixed usage of AND/OR - always use parenthesis to group AND/OR expressions.');
}
}
else if (isOperator(token)) {
parsingContext.addError(`Misplaced or incomplete expression "${token}"`);
searchContext.addError(`Misplaced or incomplete expression "${token}"`);
}
else {
parsingContext.addError(`Unrecognized expression "${token}"`);
searchContext.addError(`Unrecognized expression "${token}"`);
}
if (!op && expressions.length > 1) {
@@ -397,10 +390,11 @@ function getExpression(tokens, parsingContext, level = 0) {
return getAggregateExpression();
}
function parse({fulltextTokens, expressionTokens, parsingContext}) {
function parse({fulltextTokens, expressionTokens, searchContext}) {
return AndExp.of([
getFulltext(fulltextTokens, parsingContext),
getExpression(expressionTokens, parsingContext)
searchContext.excludeArchived ? new PropertyComparisonExp("isarchived", buildComparator("=", "false")) : null,
getFulltext(fulltextTokens, searchContext),
getExpression(expressionTokens, searchContext)
]);
}
+18 -42
View File
@@ -5,7 +5,7 @@ const handleParens = require('./handle_parens.js');
const parse = require('./parse.js');
const NoteSet = require("../note_set.js");
const SearchResult = require("../search_result.js");
const ParsingContext = require("../parsing_context.js");
const SearchContext = require("../search_context.js");
const noteCache = require('../../note_cache/note_cache.js');
const noteCacheService = require('../../note_cache/note_cache_service.js');
const hoistedNoteService = require('../../hoisted_note.js');
@@ -50,14 +50,14 @@ function findNotesWithExpression(expression) {
return searchResults;
}
function parseQueryToExpression(query, parsingContext) {
function parseQueryToExpression(query, searchContext) {
const {fulltextTokens, expressionTokens} = lex(query);
const structuredExpressionTokens = handleParens(expressionTokens);
const expression = parse({
fulltextTokens,
expressionTokens: structuredExpressionTokens,
parsingContext,
searchContext,
originalQuery: query
});
@@ -66,12 +66,16 @@ function parseQueryToExpression(query, parsingContext) {
/**
* @param {string} query
* @param {ParsingContext} parsingContext
* @param {SearchContext} searchContext
* @return {SearchResult[]}
*/
function findNotesWithQuery(query, parsingContext) {
function findNotesWithQuery(query, searchContext) {
if (!query.trim().length) {
return [];
}
return utils.stopWatch(`Search with query "${query}"`, () => {
const expression = parseQueryToExpression(query, parsingContext);
const expression = parseQueryToExpression(query, searchContext);
if (!expression) {
return [];
@@ -81,24 +85,8 @@ function findNotesWithQuery(query, parsingContext) {
}, 20);
}
/**
* @return {SearchResult[]}
*/
function searchNotes(query) {
if (!query.trim().length) {
return [];
}
const parsingContext = new ParsingContext({
includeNoteContent: true,
fuzzyAttributeSearch: false
});
return findNotesWithQuery(query, parsingContext);
}
function searchTrimmedNotes(query) {
const allSearchResults = searchNotes(query);
function searchTrimmedNotes(query, searchContext) {
const allSearchResults = findNotesWithQuery(query, searchContext);
const trimmedSearchResults = allSearchResults.slice(0, 200);
return {
@@ -108,22 +96,17 @@ function searchTrimmedNotes(query) {
}
function searchNotesForAutocomplete(query) {
if (!query.trim().length) {
return [];
}
const parsingContext = new ParsingContext({
const searchContext = new SearchContext({
includeNoteContent: false,
excludeArchived: true,
fuzzyAttributeSearch: true
});
let searchResults = findNotesWithQuery(query, parsingContext);
const {results} = searchTrimmedNotes(query, searchContext);
searchResults = searchResults.slice(0, 200);
highlightSearchResults(results, searchContext.highlightedTokens);
highlightSearchResults(searchResults, parsingContext.highlightedTokens);
return searchResults.map(result => {
return results.map(result => {
return {
notePath: result.notePath,
noteTitle: noteCacheService.getNoteTitle(result.noteId),
@@ -196,15 +179,8 @@ function formatAttribute(attr) {
}
}
function searchNoteEntities(query) {
return searchNotes(query)
.map(res => repository.getNote(res.noteId));
}
module.exports = {
searchNotes,
searchTrimmedNotes,
searchNotesForAutocomplete,
findNotesWithQuery,
searchNoteEntities
findNotesWithQuery
};
+1 -1
View File
@@ -31,7 +31,7 @@ function isDbInitialized() {
async function initDbConnection() {
if (!isDbInitialized()) {
log.info(`DB not initialized, please visit setup page` + (utils.isElectron() ? '' : ` - http://[your-server-host]:${port} to see instructions on how to initialize Trilium.`));
log.info(`DB not initialized, please visit setup page` + (utils.isElectron() ? '' : ` - http://[your-server-host]:${await port} to see instructions on how to initialize Trilium.`));
return;
}
+2
View File
@@ -101,6 +101,8 @@
<div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div>
<%- include('dialogs/confirm.ejs') %>
<script type="text/javascript">
window.baseApiUrl = 'api/';
window.device = "mobile";
+1 -2
View File
@@ -14,7 +14,6 @@ process.on('SIGINT', function() {
});
const { app, sessionParser } = require('./app');
const debug = require('debug')('node:server');
const fs = require('fs');
const http = require('http');
const https = require('https');
@@ -100,7 +99,7 @@ async function startTrilium() {
}
)
httpServer.on('listening', () => debug('Listening on port' + httpServer.address().port));
httpServer.on('listening', () => log.info('Listening on port' + httpServer.address().port));
ws.init(httpServer, sessionParser);