diff --git a/package.json b/package.json index 957ca7b29..51eedb719 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@excalidraw/excalidraw": "0.13.0", "archiver": "5.3.1", "async-mutex": "0.4.0", - "axios": "1.2.1", + "axios": "1.2.2", "better-sqlite3": "7.4.5", "chokidar": "3.5.3", "cls-hooked": "4.2.2", diff --git a/src/public/app/widgets/buttons/abstract_button.js b/src/public/app/widgets/buttons/abstract_button.js index 44be3946d..7e705d566 100644 --- a/src/public/app/widgets/buttons/abstract_button.js +++ b/src/public/app/widgets/buttons/abstract_button.js @@ -1,8 +1,8 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js"; -const TPL = ``; +const TPL = ``; export default class AbstractButtonWidget extends NoteContextAwareWidget { isEnabled() { @@ -22,7 +22,6 @@ export default class AbstractButtonWidget extends NoteContextAwareWidget { doRender() { this.$widget = $(TPL); - this.$iconSpan = this.$widget.find("span"); if (this.settings.onContextMenu) { this.$widget.on("contextmenu", e => { @@ -52,9 +51,9 @@ export default class AbstractButtonWidget extends NoteContextAwareWidget { } refreshIcon() { - for (const className of this.$iconSpan[0].classList) { + for (const className of this.$widget[0].classList) { if (className.startsWith("bx-")) { - this.$iconSpan.removeClass(className); + this.$widget.removeClass(className); } } @@ -62,7 +61,7 @@ export default class AbstractButtonWidget extends NoteContextAwareWidget { ? this.settings.icon() : this.settings.icon; - this.$iconSpan.addClass(icon); + this.$widget.addClass(icon); } initialRenderCompleteEvent() { diff --git a/src/public/app/widgets/buttons/edit_button.js b/src/public/app/widgets/buttons/edit_button.js index 3997b206a..dd9a7fc3b 100644 --- a/src/public/app/widgets/buttons/edit_button.js +++ b/src/public/app/widgets/buttons/edit_button.js @@ -39,12 +39,10 @@ export default class EditButton extends OnClickButtonWidget { // make the edit button stand out on the first display, otherwise // it's difficult to notice that the note is readonly if (this.isVisible() && !wasVisible) { - this.$iconSpan.addClass("bx-tada"); - this.$widget.css("transform", "scale(2)"); + this.$widget.addClass("bx-tada bx-lg"); setTimeout(() => { - this.$iconSpan.removeClass("bx-tada bx-lg"); - this.$widget.css("transform", "scale(1)"); + this.$widget.removeClass("bx-tada bx-lg"); }, 1700); } } diff --git a/src/public/app/widgets/buttons/global_menu.js b/src/public/app/widgets/buttons/global_menu.js index 7cf669a4e..ff881aa20 100644 --- a/src/public/app/widgets/buttons/global_menu.js +++ b/src/public/app/widgets/buttons/global_menu.js @@ -21,12 +21,12 @@ const TPL = ` background-position: 50% 45%; width: 100%; height: 100%; - position: relative; } .global-menu-button:hover { background-image: url("${window.glob.assetPath}/images/icon-color.png"); + border: 0; } .global-menu-button-update-available { diff --git a/src/services/html_sanitizer.js b/src/services/html_sanitizer.js index 9164ddfda..c675c428a 100644 --- a/src/services/html_sanitizer.js +++ b/src/services/html_sanitizer.js @@ -28,7 +28,8 @@ function sanitize(dirtyHtml) { 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'li', 'b', 'i', 'strong', 'em', 'strike', 's', 'del', 'abbr', 'code', 'hr', 'br', 'div', 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'section', 'img', - 'figure', 'figcaption', 'span', 'label', 'input' + 'figure', 'figcaption', 'span', 'label', 'input', + 'en-media' // for ENEX import ], allowedAttributes: { 'a': [ 'href', 'class', 'data-note-path' ], @@ -41,6 +42,7 @@ function sanitize(dirtyHtml) { 'code': [ 'class' ], 'ul': [ 'class' ], 'table': [ 'class' ], + 'en-media': [ 'hash' ] }, allowedSchemes: ['http', 'https', 'ftp', 'mailto', 'data', 'evernote'], transformTags, diff --git a/src/services/search/search_context.js b/src/services/search/search_context.js index c71addb17..6c427cf5f 100644 --- a/src/services/search/search_context.js +++ b/src/services/search/search_context.js @@ -24,6 +24,7 @@ class SearchContext { this.fuzzyAttributeSearch = !!params.fuzzyAttributeSearch; this.highlightedTokens = []; this.originalQuery = ""; + this.fulltextQuery = ""; // complete fulltext part // if true, becca does not have (up-to-date) information needed to process the query // and some extra data needs to be loaded before executing this.dbLoadNeeded = false; diff --git a/src/services/search/search_result.js b/src/services/search/search_result.js index 15b036e9a..4c678661d 100644 --- a/src/services/search/search_result.js +++ b/src/services/search/search_result.js @@ -17,18 +17,22 @@ class SearchResult { return this.notePathArray[this.notePathArray.length - 1]; } - computeScore(tokens) { + computeScore(fulltextQuery, tokens) { this.score = 0; + const note = becca.notes[this.noteId]; + + if (note.title.toLowerCase() === fulltextQuery) { + this.score += 100; // high reward for exact match #3470 + } + + // notes with matches on its own note title as opposed to ancestors or descendants + this.addScoreForStrings(tokens, note.title, 1.5); + // matches in attributes don't get extra points and thus are implicitly valued less than note path matches this.addScoreForStrings(tokens, this.notePathTitle, 1); - // add one more time for note title alone (already contained in the notePathTitle), - // thus preferring notes with matches on its own note title as opposed to ancestors or descendants - const note = becca.notes[this.noteId]; - this.addScoreForStrings(tokens, note.title, 1.5); - if (note.isInHiddenSubtree()) { this.score = this.score / 2; } diff --git a/src/services/search/services/lex.js b/src/services/search/services/lex.js index d7a3fb83e..c3c2273eb 100644 --- a/src/services/search/services/lex.js +++ b/src/services/search/services/lex.js @@ -1,6 +1,7 @@ function lex(str) { str = str.toLowerCase(); + let fulltextQuery = ""; const fulltextTokens = []; const expressionTokens = []; @@ -37,6 +38,8 @@ function lex(str) { expressionTokens.push(rec); } else { fulltextTokens.push(rec); + + fulltextQuery = str.substr(0, endIndex + 1); } currentWord = ''; @@ -129,7 +132,10 @@ function lex(str) { finishWord(str.length - 1); + fulltextQuery = fulltextQuery.trim(); + return { + fulltextQuery, fulltextTokens, expressionTokens } diff --git a/src/services/search/services/search.js b/src/services/search/services/search.js index 206ec63b4..3e2f5af40 100644 --- a/src/services/search/services/search.js +++ b/src/services/search/services/search.js @@ -170,7 +170,7 @@ function findResultsWithExpression(expression, searchContext) { .filter(note => !!note); for (const res of searchResults) { - res.computeScore(searchContext.highlightedTokens); + res.computeScore(searchContext.fulltextQuery, searchContext.highlightedTokens); } if (!noteSet.sorted) { @@ -195,7 +195,9 @@ function findResultsWithExpression(expression, searchContext) { } function parseQueryToExpression(query, searchContext) { - const {fulltextTokens, expressionTokens} = lex(query); + const {fulltextQuery, fulltextTokens, expressionTokens} = lex(query); + searchContext.fulltextQuery = fulltextQuery; + let structuredExpressionTokens; try { diff --git a/src/services/utils.js b/src/services/utils.js index 8164f48e4..f243e2833 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -291,6 +291,10 @@ function deferred() { } function removeDiacritic(str) { + if (!str) { + return ""; + } + return str.normalize("NFD").replace(/\p{Diacritic}/gu, ""); }