diff --git a/package-lock.json b/package-lock.json
index f2ded96c0..65ffd0b5b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2762,9 +2762,9 @@
}
},
"dayjs": {
- "version": "1.8.33",
- "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.33.tgz",
- "integrity": "sha512-881TDLZCdpJFKbraWRHcUG8zfMLLX400ENf9rFZDuWc5zYMss6xifo2PhlDX0ftOmR2NRmaIY47bAa4gKQfXqw=="
+ "version": "1.8.34",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.34.tgz",
+ "integrity": "sha512-Olb+E6EoMvdPmAMq2QoucuyZycKHjTlBXmRx8Ada+wGtq4SIXuDCdtoaX4KkK0yjf1fJLnwXQURr8gQKWKaybw=="
},
"debug": {
"version": "4.1.1",
diff --git a/package.json b/package.json
index 31ed07be5..52fa7e7ea 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
"commonmark": "0.29.1",
"cookie-parser": "1.4.5",
"csurf": "1.11.0",
- "dayjs": "1.8.33",
+ "dayjs": "1.8.34",
"debug": "4.1.1",
"ejs": "3.1.5",
"electron-debug": "3.1.0",
diff --git a/spec/search/lexer.spec.js b/spec/search/lexer.spec.js
index 180e77fcd..0b9d83a37 100644
--- a/spec/search/lexer.spec.js
+++ b/spec/search/lexer.spec.js
@@ -98,6 +98,19 @@ describe("Lexer expression", () => {
]);
});
+ it("note. prefix also separates fulltext from expression", () => {
+ expect(lex(`hello fulltext note.labels.capital = Prague`).expressionTokens.map(t => t.token))
+ .toEqual(["note", ".", "labels", ".", "capital", "=", "prague"]);
+ });
+
+ it("note. prefix in quotes will note start expression", () => {
+ expect(lex(`hello fulltext "note.txt"`).expressionTokens.map(t => t.token))
+ .toEqual([]);
+
+ expect(lex(`hello fulltext "note.txt"`).fulltextTokens.map(t => t.token))
+ .toEqual(["hello", "fulltext", "note.txt"]);
+ });
+
it("complex expressions with and, or and parenthesis", () => {
expect(lex(`# (#label=text OR #second=text) AND ~relation`).expressionTokens.map(t => t.token))
.toEqual(["#", "(", "#label", "=", "text", "or", "#second", "=", "text", ")", "and", "~relation"]);
diff --git a/src/app.js b/src/app.js
index f18324f54..3cd32aa7c 100644
--- a/src/app.js
+++ b/src/app.js
@@ -23,7 +23,8 @@ app.use(helmet({
hidePoweredBy: false, // deactivated because electron 4.0 crashes on this right after startup
contentSecurityPolicy: {
directives: {
- defaultSrc: ["*", "'unsafe-inline'", "'unsafe-eval'", "img-src 'self' data:"]
+ defaultSrc: ["*", "'unsafe-inline'", "'unsafe-eval'"],
+ imgSrc: ["'self' data:"]
}
}
}));
diff --git a/src/public/app/services/tree_cache.js b/src/public/app/services/tree_cache.js
index 4dba0792a..ad5e76161 100644
--- a/src/public/app/services/tree_cache.js
+++ b/src/public/app/services/tree_cache.js
@@ -165,23 +165,22 @@ class TreeCache {
for (const note of resp.notes) {
if (note.type === 'search') {
- const searchResults = await server.get('search-note/' + note.noteId);
+ const searchResultNoteIds = await server.get('search-note/' + note.noteId);
- if (!searchResults) {
+ if (!searchResultNoteIds) {
throw new Error(`Search note ${note.noteId} failed.`);
}
// force to load all the notes at once instead of one by one
- await this.getNotes(searchResults.map(res => res.noteId));
+ await this.getNotes(searchResultNoteIds);
const branches = resp.branches.filter(b => b.noteId === note.noteId || b.parentNoteId === note.noteId);
- searchResults.forEach((result, index) => branches.push({
+ searchResultNoteIds.forEach((resultNoteId, index) => branches.push({
// branchId should be repeatable since sometimes we reload some notes without rerendering the tree
- branchId: "virt" + result.noteId + '-' + note.noteId,
- noteId: result.noteId,
+ branchId: "virt" + resultNoteId + '-' + note.noteId,
+ noteId: resultNoteId,
parentNoteId: note.noteId,
- prefix: this.getBranch(result.branchId).prefix,
notePosition: (index + 1) * 10
}));
@@ -217,7 +216,7 @@ class TreeCache {
return noteIds.map(noteId => {
if (!this.notes[noteId] && !silentNotFoundError) {
- console.log(`Can't find note "${noteId}"`);
+ console.trace(`Can't find note "${noteId}"`);
return null;
}
diff --git a/src/public/app/widgets/collapsible_widgets/link_map.js b/src/public/app/widgets/collapsible_widgets/link_map.js
index fc1adcf6b..7d04d5279 100644
--- a/src/public/app/widgets/collapsible_widgets/link_map.js
+++ b/src/public/app/widgets/collapsible_widgets/link_map.js
@@ -1,4 +1,5 @@
import CollapsibleWidget from "../collapsible_widget.js";
+import treeCache from "../../services/tree_cache.js";
let linkMapContainerIdCtr = 1;
@@ -89,5 +90,19 @@ export default class LinkMapWidget extends CollapsibleWidget {
if (loadResults.getAttributes().find(attr => attr.type === 'relation' && (attr.noteId === this.noteId || attr.value === this.noteId))) {
this.noteSwitched();
}
+
+ const changedNoteIds = loadResults.getNoteIds();
+
+ if (changedNoteIds.length > 0) {
+ const $linkMapContainer = this.$body.find('.link-map-container');
+
+ for (const noteId of changedNoteIds) {
+ const note = treeCache.notes[noteId];
+
+ if (note) {
+ $linkMapContainer.find(`a[data-note-path="${noteId}"]`).text(note.title);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js
index ae2cf1da1..3006871e0 100644
--- a/src/public/app/widgets/note_tree.js
+++ b/src/public/app/widgets/note_tree.js
@@ -32,6 +32,7 @@ const TPL = `
.tree {
height: 100%;
overflow: auto;
+ padding-bottom: 20px;
}
.refresh-search-button {
@@ -588,7 +589,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
const note = await branch.getNote();
if (!note) {
- throw new Error(`Branch has no note ` + branch.noteId);
+ throw new Error(`Branch has no note "${branch.noteId}": ${JSON.stringify(note)}`);
}
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
diff --git a/src/public/app/widgets/search_box.js b/src/public/app/widgets/search_box.js
index cc9ce0344..cce532372 100644
--- a/src/public/app/widgets/search_box.js
+++ b/src/public/app/widgets/search_box.js
@@ -3,6 +3,7 @@ import toastService from "../services/toast.js";
import appContext from "../services/app_context.js";
import noteCreateService from "../services/note_create.js";
import utils from "../services/utils.js";
+import treeCache from "../services/tree_cache.js";
const TPL = `
@@ -105,14 +106,13 @@ export default class SearchBoxWidget extends BasicWidget {
return;
}
- // FIXME
- let activeNote = appContext.tabManager.getActiveTabNote();
+ let parent = appContext.tabManager.getActiveTabNote();
- if (activeNote.type === 'search') {
- activeNote = activeNote.getParentNotes()[0];
+ if (parent.type === 'search') {
+ parent = await treeCache.getNote('root');
}
- await noteCreateService.createNote(activeNote.noteId, {
+ await noteCreateService.createNote(parent.noteId, {
type: "search",
mime: "application/json",
title: searchString,
diff --git a/src/public/app/widgets/side_pane_toggles.js b/src/public/app/widgets/side_pane_toggles.js
index dc4db7714..0b04dd40a 100644
--- a/src/public/app/widgets/side_pane_toggles.js
+++ b/src/public/app/widgets/side_pane_toggles.js
@@ -20,8 +20,8 @@ const TPL = `
}
-
-
+
+
diff --git a/src/public/app/widgets/type_widgets/search.js b/src/public/app/widgets/type_widgets/search.js
index 81993b25b..7a2698c28 100644
--- a/src/public/app/widgets/type_widgets/search.js
+++ b/src/public/app/widgets/type_widgets/search.js
@@ -1,7 +1,7 @@
import TypeWidget from "./type_widget.js";
const TPL = `
-
+
Search string:
diff --git a/src/routes/api/search.js b/src/routes/api/search.js
index cb7070bc4..72cbda3e8 100644
--- a/src/routes/api/search.js
+++ b/src/routes/api/search.js
@@ -23,7 +23,7 @@ function searchNotes(req) {
}
}
-function searchFromNote(req) {
+async function searchFromNote(req) {
const note = repository.getNote(req.params.noteId);
if (!note) {
@@ -44,15 +44,16 @@ function searchFromNote(req) {
return [];
}
- let noteIds;
+ let searchResultNoteIds;
try {
if (json.searchString.startsWith('=')) {
const relationName = json.searchString.substr(1).trim();
- noteIds = searchFromRelation(note, relationName);
+ searchResultNoteIds = await searchFromRelation(note, relationName);
} else {
- noteIds = searchService.searchForNoteIds(json.searchString);
+ searchResultNoteIds = searchService.searchNotes(json.searchString)
+ .map(sr => sr.noteId);
}
}
catch (e) {
@@ -62,16 +63,17 @@ function searchFromNote(req) {
}
// we won't return search note's own noteId
- noteIds = noteIds.filter(noteId => noteId !== note.noteId);
+ // also don't allow root since that would force infinite cycle
+ searchResultNoteIds = searchResultNoteIds.filter(resultNoteId => !['root', note.noteId].includes(resultNoteId));
- if (noteIds.length > 200) {
- noteIds = noteIds.slice(0, 200);
+ if (searchResultNoteIds.length > 200) {
+ searchResultNoteIds = searchResultNoteIds.slice(0, 200);
}
- return noteIds.map(noteCacheService.getNotePath).filter(res => !!res);
+ return searchResultNoteIds;
}
-function searchFromRelation(note, relationName) {
+async function searchFromRelation(note, relationName) {
const scriptNote = note.getRelationTarget(relationName);
if (!scriptNote) {
@@ -92,7 +94,7 @@ function searchFromRelation(note, relationName) {
return [];
}
- const result = scriptService.executeNote(scriptNote, { originEntity: note });
+ const result = await scriptService.executeNote(scriptNote, { originEntity: note });
if (!Array.isArray(result)) {
log.info(`Result from ${scriptNote.noteId} is not an array.`);
diff --git a/src/services/image.js b/src/services/image.js
index b1894c6d2..9420245e3 100644
--- a/src/services/image.js
+++ b/src/services/image.js
@@ -20,7 +20,7 @@ async function processImage(uploadBuffer, originalName, shrinkImageSwitch) {
shrinkImageSwitch = false;
}
- const finalImageBuffer = shrinkImageSwitch ? await shrinkImage(uploadBuffer) : uploadBuffer;
+ const finalImageBuffer = shrinkImageSwitch ? await shrinkImage(uploadBuffer, originalName) : uploadBuffer;
const imageFormat = getImageType(finalImageBuffer);
@@ -104,11 +104,20 @@ function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSwitch)
};
}
-async function shrinkImage(buffer) {
+async function shrinkImage(buffer, originalName) {
const jpegQuality = optionService.getOptionInt('imageJpegQuality');
- let finalImageBuffer = await resize(buffer, jpegQuality);
- // if resizing & shrinking did not help with size then save the original
+ let finalImageBuffer;
+ try {
+ finalImageBuffer = await resize(buffer, jpegQuality);
+ }
+ catch (e) {
+ log.error("Failed to resize image '" + originalName + "'\nStack: " + e.stack);
+
+ finalImageBuffer = buffer;
+ }
+
+ // if resizing did not help with size then save the original
// (can happen when e.g. resizing PNG into JPEG)
if (finalImageBuffer.byteLength >= buffer.byteLength) {
finalImageBuffer = buffer;
diff --git a/src/services/notes.js b/src/services/notes.js
index 2f8d33b6f..cdd2aa1fc 100644
--- a/src/services/notes.js
+++ b/src/services/notes.js
@@ -92,7 +92,7 @@ function copyChildAttributes(parentNote, childNote) {
* - {integer} notePosition - default is last existing notePosition in a parent + 10
*
* @param params
- * @return {Promise<{note: Note, branch: Branch}>}
+ * @return {{note: Note, branch: Branch}}
*/
function createNewNote(params) {
const parentNote = repository.getNote(params.parentNoteId);
diff --git a/src/services/options.js b/src/services/options.js
index f2da61ab7..2118d4b9d 100644
--- a/src/services/options.js
+++ b/src/services/options.js
@@ -9,7 +9,7 @@ function getOption(name) {
}
/**
- * @return {Promise}
+ * @return {number}
*/
function getOptionInt(name) {
const val = getOption(name);
@@ -24,7 +24,7 @@ function getOptionInt(name) {
}
/**
- * @return {Promise}
+ * @return {boolean}
*/
function getOptionBool(name) {
const val = getOption(name);
diff --git a/src/services/search/services/lex.js b/src/services/search/services/lex.js
index 821a3e2fc..4f6ab8b97 100644
--- a/src/services/search/services/lex.js
+++ b/src/services/search/services/lex.js
@@ -83,6 +83,10 @@ function lex(str) {
continue;
}
else if (!quotes) {
+ if (!fulltextEnded && currentWord === 'note' && chr === '.') {
+ fulltextEnded = true;
+ }
+
if (chr === '#' || chr === '~') {
if (!fulltextEnded) {
fulltextEnded = true;
diff --git a/src/services/search/services/search.js b/src/services/search/services/search.js
index d3eeb1c3d..495e0b321 100644
--- a/src/services/search/services/search.js
+++ b/src/services/search/services/search.js
@@ -14,7 +14,7 @@ const utils = require('../../utils.js');
/**
* @param {Expression} expression
- * @return {Promise}
+ * @return {SearchResult[]}
*/
function findNotesWithExpression(expression) {
const hoistedNote = noteCache.notes[hoistedNoteService.getHoistedNoteId()];
@@ -67,7 +67,7 @@ function parseQueryToExpression(query, parsingContext) {
/**
* @param {string} query
* @param {ParsingContext} parsingContext
- * @return {Promise}
+ * @return {SearchResult[]}
*/
function findNotesWithQuery(query, parsingContext) {
const expression = parseQueryToExpression(query, parsingContext);
@@ -79,6 +79,9 @@ function findNotesWithQuery(query, parsingContext) {
return findNotesWithExpression(expression);
}
+/**
+ * @return {SearchResult[]}
+ */
function searchNotes(query) {
if (!query.trim().length) {
return [];