extraOptions |
@@ -693,7 +584,7 @@ the backend.
-CreateNoteExtraOptions
+CreateNoteParams
@@ -760,295 +651,7 @@ the backend.
Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Returns:
-
-
-
- object contains newly created entities note and branch
-
-
-
-
-
- -
- Type
-
- -
-
-Promise.<{note: Note, branch: Branch}>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- createNoteAndRefresh(parentNoteId, title, contentopt, extraOptionsopt) → {Promise.<{note: Note, branch: Branch}>}
-
-
-
-
-
-
-
- Creates new note according to given params and force all connected clients to refresh their tree.
-
-
-
-
-
-
-
-
-
-
- Parameters:
-
-
-
-
-
-
- Name |
-
-
- Type |
-
-
- Attributes |
-
-
-
- Default |
-
-
- Description |
-
-
-
-
-
-
-
-
- parentNoteId |
-
-
-
-
-
-string
-
-
-
- |
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
- |
-
-
- create new note under this parent |
-
-
-
-
-
-
- title |
-
-
-
-
-
-string
-
-
-
- |
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
- |
-
-
- |
-
-
-
-
-
-
- content |
-
-
-
-
-
-string
-
-
-
- |
-
-
-
-
- <optional>
-
-
-
-
-
- |
-
-
-
-
-
- ""
-
- |
-
-
- |
-
-
-
-
-
-
- extraOptions |
-
-
-
-
-
-CreateNoteExtraOptions
-
-
-
- |
-
-
-
-
- <optional>
-
-
-
-
-
- |
-
-
-
-
-
- {}
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Source:
-
@@ -1533,7 +1136,7 @@ the backend.
- Source:
@@ -1997,7 +1600,7 @@ the backend.
- Source:
@@ -2765,7 +2368,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -3418,7 +3021,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -3596,7 +3199,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -3751,7 +3354,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -3901,7 +3504,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -4404,7 +4007,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
- Source:
@@ -4537,7 +4140,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
- Source:
@@ -4912,7 +4515,7 @@ transactional by default.
- Source:
diff --git a/docs/backend_api/Note.html b/docs/backend_api/Note.html
index cfc3cb5e9..e9a278742 100644
--- a/docs/backend_api/Note.html
+++ b/docs/backend_api/Note.html
@@ -509,7 +509,7 @@
- (async) getAllNotePaths() → {Promise.<Array.<Array.<string>>>}
+ (async) addAttribute() → {Promise.<Attribute>}
@@ -557,7 +557,7 @@
- Source:
@@ -585,10 +585,6 @@
Returns:
-
- - array of notePaths (each represented by array of noteIds constituting the particular note path)
-
-
@@ -597,7 +593,7 @@
-
-Promise.<Array.<Array.<string>>>
+Promise.<Attribute>
@@ -735,7 +731,7 @@
- Source:
@@ -902,7 +898,7 @@
- Source:
@@ -1080,7 +1076,7 @@
- Source:
@@ -1186,7 +1182,7 @@
- Source:
@@ -1288,7 +1284,7 @@
- Source:
@@ -1394,7 +1390,7 @@
- Source:
@@ -1602,7 +1598,7 @@
- Source:
@@ -1835,7 +1831,7 @@
- Source:
@@ -2033,7 +2029,7 @@
- Source:
@@ -2231,7 +2227,7 @@
- Source:
@@ -2484,7 +2480,7 @@
- Source:
@@ -2651,7 +2647,7 @@
- Source:
@@ -2818,7 +2814,7 @@
- Source:
@@ -2973,7 +2969,7 @@
- Source:
@@ -3085,7 +3081,7 @@
- Source:
@@ -3187,7 +3183,7 @@
- Source:
@@ -3295,7 +3291,7 @@ This method can be significantly faster than the getAttribute()
- Source:
@@ -3403,7 +3399,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -3558,7 +3554,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -3725,7 +3721,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -3892,7 +3888,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4047,7 +4043,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4217,7 +4213,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4368,7 +4364,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4478,7 +4474,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4580,7 +4576,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4686,7 +4682,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4864,7 +4860,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -4970,7 +4966,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -5125,7 +5121,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -5280,7 +5276,7 @@ This method can be significantly faster than the getAttributes()
- Source:
@@ -5391,7 +5387,7 @@ Cache is note instance scoped.
- Source:
@@ -5475,7 +5471,7 @@ Cache is note instance scoped.
- Source:
@@ -5581,7 +5577,7 @@ Cache is note instance scoped.
- Source:
@@ -5687,7 +5683,7 @@ Cache is note instance scoped.
- Source:
@@ -5793,7 +5789,7 @@ Cache is note instance scoped.
- Source:
@@ -5899,7 +5895,7 @@ Cache is note instance scoped.
- Source:
@@ -6005,7 +6001,7 @@ Cache is note instance scoped.
- Source:
@@ -6234,7 +6230,7 @@ Cache is note instance scoped.
- Source:
@@ -6432,7 +6428,7 @@ Cache is note instance scoped.
- Source:
@@ -6630,7 +6626,7 @@ Cache is note instance scoped.
- Source:
@@ -6859,7 +6855,7 @@ Cache is note instance scoped.
- Source:
@@ -7063,7 +7059,7 @@ Cache is note instance scoped.
- Source:
@@ -7261,7 +7257,7 @@ Cache is note instance scoped.
- Source:
@@ -7459,7 +7455,7 @@ Cache is note instance scoped.
- Source:
@@ -7719,7 +7715,7 @@ Cache is note instance scoped.
- Source:
@@ -7948,7 +7944,7 @@ Cache is note instance scoped.
- Source:
@@ -8177,7 +8173,7 @@ Cache is note instance scoped.
- Source:
diff --git a/docs/backend_api/entities_attribute.js.html b/docs/backend_api/entities_attribute.js.html
index 1e19b196b..aa9220e39 100644
--- a/docs/backend_api/entities_attribute.js.html
+++ b/docs/backend_api/entities_attribute.js.html
@@ -107,6 +107,10 @@ class Attribute extends Entity {
async beforeSaving() {
if (!this.value) {
+ if (this.type === 'relation') {
+ throw new Error(`Cannot save relation ${this.name} since it does not target any note.`);
+ }
+
// null value isn't allowed
this.value = "";
}
diff --git a/docs/backend_api/entities_note.js.html b/docs/backend_api/entities_note.js.html
index a62d38017..30c1f1c3c 100644
--- a/docs/backend_api/entities_note.js.html
+++ b/docs/backend_api/entities_note.js.html
@@ -142,6 +142,10 @@ class Note extends Entity {
/** @returns {Promise} */
async setContent(content) {
+ if (content === null || content === undefined) {
+ throw new Error(`Cannot set null content to note ${this.noteId}`);
+ }
+
// force updating note itself so that dateModified is represented correctly even for the content
this.forcedChange = true;
this.contentLength = content.length;
@@ -500,6 +504,32 @@ class Note extends Entity {
}
}
+ /**
+ * @return {Promise<Attribute>}
+ */
+ async addAttribute(type, name, value = "") {
+ const attr = new Attribute({
+ noteId: this.noteId,
+ type: type,
+ name: name,
+ value: value
+ });
+
+ await attr.save();
+
+ this.invalidateAttributeCache();
+
+ return attr;
+ }
+
+ async addLabel(name, value = "") {
+ return await this.addAttribute(LABEL, name, value);
+ }
+
+ async addRelation(name, targetNoteId) {
+ return await this.addAttribute(RELATION, name, targetNoteId);
+ }
+
/**
* @param {string} name - label name
* @returns {Promise<boolean>} true if label exists (including inherited)
diff --git a/docs/backend_api/global.html b/docs/backend_api/global.html
index 0759abe47..542cf911b 100644
--- a/docs/backend_api/global.html
+++ b/docs/backend_api/global.html
@@ -290,7 +290,7 @@
-
+CreateNoteParams
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html
index eb68860b6..0fbb86466 100644
--- a/docs/backend_api/services_backend_script_api.js.html
+++ b/docs/backend_api/services_backend_script_api.js.html
@@ -206,7 +206,7 @@ function BackendScriptApi(currentNote, apiParams) {
*/
/**
- * @typedef {object} CreateNoteExtraOptions
+ * @typedef {object} CreateNoteParams
* @property {boolean} [json=false] - should the note be JSON
* @property {boolean} [isProtected=false] - should the note be protected
* @property {string} [type='text'] - note type
@@ -217,32 +217,10 @@ function BackendScriptApi(currentNote, apiParams) {
/**
* @method
*
- * @param {string} parentNoteId - create new note under this parent
- * @param {string} title
- * @param {string} [content=""]
- * @param {CreateNoteExtraOptions} [extraOptions={}]
+ * @param {CreateNoteParams} [extraOptions={}]
* @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
*/
- this.createNote = noteService.createNote;
-
- /**
- * Creates new note according to given params and force all connected clients to refresh their tree.
- *
- * @method
- *
- * @param {string} parentNoteId - create new note under this parent
- * @param {string} title
- * @param {string} [content=""]
- * @param {CreateNoteExtraOptions} [extraOptions={}]
- * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
- */
- this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) {
- const ret = await noteService.createNote(parentNoteId, title, content, extraOptions);
-
- ws.refreshTree();
-
- return ret;
- };
+ this.createNote = noteService.createNewNote;
/**
* Log given message to trilium logs.
diff --git a/docs/frontend_api/Branch.html b/docs/frontend_api/Branch.html
index cc6fc3b03..0b7013743 100644
--- a/docs/frontend_api/Branch.html
+++ b/docs/frontend_api/Branch.html
@@ -719,7 +719,7 @@
diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html
index dc8fc5a64..5ce01216e 100644
--- a/docs/frontend_api/FrontendScriptApi.html
+++ b/docs/frontend_api/FrontendScriptApi.html
@@ -81,7 +81,7 @@
- Source:
@@ -223,7 +223,7 @@
- Source:
@@ -333,7 +333,113 @@
- Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+keys
+
+
+
+
+
+
+
+
+
+
+ Properties:
+
+
+
+
+
+
+
+
+ Type |
+
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+
+
+
+
+KeyboardAction
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
@@ -446,7 +552,7 @@
- Source:
@@ -552,7 +658,7 @@
- Source:
@@ -662,7 +768,7 @@
- Source:
@@ -771,7 +877,7 @@
- Source:
@@ -900,7 +1006,7 @@
- Source:
@@ -1055,7 +1161,7 @@
- Source:
@@ -1210,7 +1316,7 @@
- Source:
@@ -1366,7 +1472,7 @@
- Source:
@@ -1546,7 +1652,7 @@
- Source:
@@ -1679,7 +1785,7 @@
- Source:
@@ -1785,7 +1891,7 @@
- Source:
@@ -1891,7 +1997,7 @@
- Source:
@@ -2050,7 +2156,7 @@
- Source:
@@ -2157,7 +2263,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -2312,7 +2418,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -2468,7 +2574,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -2669,7 +2775,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -2775,7 +2881,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -2930,7 +3036,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -3039,7 +3145,7 @@ note.
- Source:
@@ -3194,7 +3300,7 @@ note.
- Source:
@@ -3327,7 +3433,7 @@ note.
- Source:
@@ -3433,7 +3539,7 @@ note.
- Source:
@@ -3521,7 +3627,7 @@ note.
- Source:
@@ -3627,7 +3733,7 @@ note.
- Source:
@@ -3733,7 +3839,7 @@ note.
- Source:
@@ -3888,7 +3994,7 @@ note.
- Source:
@@ -4049,7 +4155,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4209,7 +4315,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4365,7 +4471,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4520,7 +4626,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4671,7 +4777,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4808,7 +4914,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4945,7 +5051,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -4991,7 +5097,7 @@ Internally this serializes the anonymous function into string and sends it to ba
diff --git a/docs/frontend_api/KeyboardAction.html b/docs/frontend_api/KeyboardAction.html
new file mode 100644
index 000000000..1877d3729
--- /dev/null
+++ b/docs/frontend_api/KeyboardAction.html
@@ -0,0 +1,2266 @@
+
+
+
+
+ JSDoc: Class: KeyboardAction
+
+
+
+
+
+
+
+
+
+
+
+
+ Class: KeyboardAction
+
+
+
+
+
+
+
+
+
+
+ KeyboardAction()
+
+ blaa vlaa
+
+
+
+
+
+
+
+
+
+
+ Constructor
+
+
+
+ new KeyboardAction()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Members
+
+
+
+(static) AddLinkToText
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) BackInNoteHistory
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ClipboardCopy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ClipboardCut
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ClipboardPaste
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) CloneNotesTo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) CloseTab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) CollapseTree
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) CreateNoteAfter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) CreateNoteInto
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) FindInText
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) FocusNote
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ForwardInNoteHistory
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) InsertDateTime
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) JumpToNote
+
+
+
+
+
+ Open "Jump to note" dialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) MarkdownToHTML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) MoveNotesTo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) NewTab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) NextTab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) OpenSQLConsole
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) PreviousTab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) Redo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ReloadApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) RunCurrentNote
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) RunSQL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) SearchNotes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) SelectAllNotesInParent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ShowAttributes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ShowHelp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ToggleFullscreen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ToggleZenMode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) Undo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ZoomIn
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(static) ZoomOut
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/frontend_api/KeyboardActions.html b/docs/frontend_api/KeyboardActions.html
new file mode 100644
index 000000000..0e5a93353
--- /dev/null
+++ b/docs/frontend_api/KeyboardActions.html
@@ -0,0 +1,280 @@
+
+
+
+
+ JSDoc: Class: KeyboardActions
+
+
+
+
+
+
+
+
+
+
+
+
+ Class: KeyboardActions
+
+
+
+
+
+
+
+
+
+
+ KeyboardActions()
+
+ blaa vlaa
+
+
+
+
+
+
+
+
+
+
+ Constructor
+
+
+
+ new KeyboardActions()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Members
+
+
+
+JUMP_TO
+
+
+
+
+
+
+
+
+
+
+ Properties:
+
+
+
+
+
+
+
+
+ Type |
+
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/frontend_api/NoteFull.html b/docs/frontend_api/NoteFull.html
index 890782f59..113ac1492 100644
--- a/docs/frontend_api/NoteFull.html
+++ b/docs/frontend_api/NoteFull.html
@@ -449,7 +449,7 @@
diff --git a/docs/frontend_api/NoteShort.html b/docs/frontend_api/NoteShort.html
index 6e3561286..110e0e051 100644
--- a/docs/frontend_api/NoteShort.html
+++ b/docs/frontend_api/NoteShort.html
@@ -346,7 +346,7 @@
- Source:
@@ -414,7 +414,7 @@
- Source:
@@ -548,64 +548,6 @@
-iconClass
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
isDeleted
@@ -888,7 +830,7 @@
- Source:
@@ -956,7 +898,7 @@
- Source:
@@ -1220,7 +1162,7 @@
- Source:
@@ -1387,7 +1329,7 @@
- Source:
@@ -1561,7 +1503,7 @@
- Source:
@@ -1667,7 +1609,7 @@
- Source:
@@ -1769,7 +1711,7 @@
- Source:
@@ -1871,7 +1813,7 @@
- Source:
@@ -1973,7 +1915,7 @@
- Source:
@@ -2124,7 +2066,7 @@
- Source:
@@ -2291,7 +2233,7 @@
- Source:
@@ -2458,7 +2400,7 @@
- Source:
@@ -2613,7 +2555,7 @@
- Source:
@@ -2719,7 +2661,7 @@
- Source:
@@ -2821,7 +2763,7 @@
- Source:
@@ -2972,7 +2914,7 @@
- Source:
@@ -3139,7 +3081,7 @@
- Source:
@@ -3306,7 +3248,7 @@
- Source:
@@ -3461,7 +3403,7 @@
- Source:
@@ -3631,7 +3573,7 @@
- Source:
@@ -3782,7 +3724,7 @@
- Source:
@@ -3892,7 +3834,7 @@
- Source:
@@ -4066,7 +4008,7 @@
- Source:
@@ -4172,7 +4114,7 @@
- Source:
@@ -4323,7 +4265,7 @@
- Source:
@@ -4478,7 +4420,7 @@
- Source:
@@ -4589,7 +4531,7 @@ Cache is note instance scoped.
- Source:
@@ -4673,7 +4615,7 @@ Cache is note instance scoped.
- Source:
@@ -4737,7 +4679,7 @@ Cache is note instance scoped.
diff --git a/docs/frontend_api/entities_attribute.js.html b/docs/frontend_api/entities_attribute.js.html
index 01956a8e7..2f85b38f8 100644
--- a/docs/frontend_api/entities_attribute.js.html
+++ b/docs/frontend_api/entities_attribute.js.html
@@ -71,7 +71,7 @@ export default Attribute;
diff --git a/docs/frontend_api/entities_branch.js.html b/docs/frontend_api/entities_branch.js.html
index bf5d06879..cc008e83d 100644
--- a/docs/frontend_api/entities_branch.js.html
+++ b/docs/frontend_api/entities_branch.js.html
@@ -70,7 +70,7 @@ export default Branch;
diff --git a/docs/frontend_api/entities_note_full.js.html b/docs/frontend_api/entities_note_full.js.html
index b66927b7b..9f141a2a7 100644
--- a/docs/frontend_api/entities_note_full.js.html
+++ b/docs/frontend_api/entities_note_full.js.html
@@ -68,7 +68,7 @@ export default NoteFull;
diff --git a/docs/frontend_api/entities_note_short.js.html b/docs/frontend_api/entities_note_short.js.html
index ed9603318..736bb9ad0 100644
--- a/docs/frontend_api/entities_note_short.js.html
+++ b/docs/frontend_api/entities_note_short.js.html
@@ -365,7 +365,7 @@ export default NoteShort;
diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html
index 7f4006e31..043fc4c5f 100644
--- a/docs/frontend_api/global.html
+++ b/docs/frontend_api/global.html
@@ -303,7 +303,7 @@
- Source:
@@ -333,7 +333,7 @@
diff --git a/docs/frontend_api/index.html b/docs/frontend_api/index.html
index 310dca343..cd881639d 100644
--- a/docs/frontend_api/index.html
+++ b/docs/frontend_api/index.html
@@ -50,7 +50,7 @@
diff --git a/docs/frontend_api/module.exports.html b/docs/frontend_api/module.exports.html
new file mode 100644
index 000000000..4050b4d16
--- /dev/null
+++ b/docs/frontend_api/module.exports.html
@@ -0,0 +1,280 @@
+
+
+
+
+ JSDoc: Class: exports
+
+
+
+
+
+
+
+
+
+
+
+
+ Class: exports
+
+
+
+
+
+
+
+
+
+
+ exports()
+
+ blaa vlaa
+
+
+
+
+
+
+
+
+
+
+ Constructor
+
+
+
+ new exports()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Members
+
+
+
+JUMP_TO
+
+
+
+
+
+
+
+
+
+
+ Properties:
+
+
+
+
+
+
+
+
+ Type |
+
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html
index e76325720..d03a54856 100644
--- a/docs/frontend_api/services_frontend_script_api.js.html
+++ b/docs/frontend_api/services_frontend_script_api.js.html
@@ -39,6 +39,7 @@ import dateNotesService from './date_notes.js';
import StandardWidget from '../widgets/standard_widget.js';
import ws from "./ws.js";
import hoistedNoteService from "./hoisted_note.js";
+import KeyboardAction from "./keyboard_action.js";
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -68,6 +69,11 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
/** @property {StandardWidget} */
this.StandardWidget = StandardWidget;
+ /** @property {KeyboardAction} */
+ this.keys = new KeyboardAction();
+
+ this.bind = keys.bind;
+
/**
* Activates note in the tree and in the note detail.
*
@@ -422,7 +428,7 @@ export default FrontendScriptApi;
diff --git a/docs/frontend_api/services_keyboard_action.js.html b/docs/frontend_api/services_keyboard_action.js.html
new file mode 100644
index 000000000..f477850d8
--- /dev/null
+++ b/docs/frontend_api/services_keyboard_action.js.html
@@ -0,0 +1,280 @@
+
+
+
+
+ JSDoc: Source: services/keyboard_action.js
+
+
+
+
+
+
+
+
+
+
+
+
+ Source: services/keyboard_action.js
+
+
+
+
+
+
+
+
+ /**
+ * blaa vlaa
+ */
+class KeyboardAction {
+ constructor(params) {
+ this.optionName = params.optionName;
+ }
+}
+
+/**
+ * Open "Jump to note" dialog
+ * @static
+ */
+KeyboardAction.JumpToNote = new KeyboardAction({
+ optionName: "JumpToNote",
+ defaultShortcuts: "mod+j",
+ description: 'Open "Jump to note" dialog'
+});
+
+/** @static */
+KeyboardAction.MarkdownToHTML = new KeyboardAction({
+ optionName: "MarkdownToHTML",
+ defaultShortcuts: "mod+return"
+});
+
+/** @static */
+KeyboardAction.NewTab = new KeyboardAction({
+ optionName: "NewTab",
+ defaultShortcuts: "mod+t"
+});
+
+/** @static */
+KeyboardAction.CloseTab = new KeyboardAction({
+ optionName: "CloseTab",
+ defaultShortcuts: "mod+w"
+});
+
+/** @static */
+KeyboardAction.NextTab = new KeyboardAction({
+ optionName: "NextTab",
+ defaultShortcuts: "mod+tab"
+});
+
+/** @static */
+KeyboardAction.PreviousTab = new KeyboardAction({
+ optionName: "PreviousTab",
+ defaultShortcuts: "mod+shift+tab"
+});
+
+/** @static */
+KeyboardAction.CreateNoteAfter = new KeyboardAction({
+ optionName: "CreateNoteAfter",
+ defaultShortcuts: "mod+o"
+});
+
+/** @static */
+KeyboardAction.CreateNoteInto = new KeyboardAction({
+ optionName: "CreateNoteInto",
+ defaultShortcuts: "mod+p"
+});
+
+/** @static */
+KeyboardAction.ScrollToActiveNote = new KeyboardAction({
+ optionName: "ScrollToActiveNote",
+ defaultShortcuts: "mod+."
+});
+
+/** @static */
+KeyboardAction.CollapseTree = new KeyboardAction({
+ optionName: "CollapseTree",
+ defaultShortcuts: "alt+c"
+});
+
+/** @static */
+KeyboardAction.RunSQL = new KeyboardAction({
+ optionName: "RunSQL",
+ defaultShortcuts: "mod+return"
+});
+
+/** @static */
+KeyboardAction.FocusNote = new KeyboardAction({
+ optionName: "FocusNote",
+ defaultShortcuts: "return"
+});
+
+/** @static */
+KeyboardAction.RunCurrentNote = new KeyboardAction({
+ optionName: "RunCurrentNote",
+ defaultShortcuts: "mod+return"
+});
+
+/** @static */
+KeyboardAction.ClipboardCopy = new KeyboardAction({
+ optionName: "ClipboardCopy",
+ defaultShortcuts: "mod+c"
+});
+
+/** @static */
+KeyboardAction.ClipboardPaste = new KeyboardAction({
+ optionName: "ClipboardPaste",
+ defaultShortcuts: "mod+v"
+});
+
+/** @static */
+KeyboardAction.ClipboardCut = new KeyboardAction({
+ optionName: "ClipboardCut",
+ defaultShortcuts: "mod+x"
+});
+
+/** @static */
+KeyboardAction.SelectAllNotesInParent = new KeyboardAction({
+ optionName: "SelectAllNotesInParent",
+ defaultShortcuts: "mod+a"
+});
+
+/** @static */
+KeyboardAction.Undo = new KeyboardAction({
+ optionName: "Undo",
+ defaultShortcuts: "mod+z"
+});
+
+/** @static */
+KeyboardAction.Redo = new KeyboardAction({
+ optionName: "Redo",
+ defaultShortcuts: "mod+y"
+});
+
+/** @static */
+KeyboardAction.AddLinkToText = new KeyboardAction({
+ optionName: "AddLinkToText",
+ defaultShortcuts: "mod+l"
+});
+
+/** @static */
+KeyboardAction.CloneNotesTo = new KeyboardAction({
+ optionName: "CloneNotesTo",
+ defaultShortcuts: "mod+shift+c"
+});
+
+/** @static */
+KeyboardAction.MoveNotesTo = new KeyboardAction({
+ optionName: "MoveNotesTo",
+ defaultShortcuts: "mod+shift+c"
+});
+
+/** @static */
+KeyboardAction.SearchNotes = new KeyboardAction({
+ optionName: "SearchNotes",
+ defaultShortcuts: "mod+s"
+});
+
+/** @static */
+KeyboardAction.ShowAttributes = new KeyboardAction({
+ optionName: "ShowAttributes",
+ defaultShortcuts: "alt+a"
+});
+
+/** @static */
+KeyboardAction.ShowHelp = new KeyboardAction({
+ optionName: "ShowHelp",
+ defaultShortcuts: "f1"
+});
+
+/** @static */
+KeyboardAction.OpenSQLConsole = new KeyboardAction({
+ optionName: "OpenSQLConsole",
+ defaultShortcuts: "alt+o"
+});
+
+/** @static */
+KeyboardAction.BackInNoteHistory = new KeyboardAction({
+ optionName: "BackInNoteHistory",
+ defaultShortcuts: "alt+left"
+});
+
+/** @static */
+KeyboardAction.ForwardInNoteHistory = new KeyboardAction({
+ optionName: "ForwardInNoteHistory",
+ defaultShortcuts: "alt+right"
+});
+
+/** @static */
+KeyboardAction.ToggleZenMode = new KeyboardAction({
+ optionName: "ToggleZenMode",
+ defaultShortcuts: "alt+m"
+});
+
+/** @static */
+KeyboardAction.InsertDateTime = new KeyboardAction({
+ optionName: "InsertDateTime",
+ defaultShortcuts: "alt+t"
+});
+
+/** @static */
+KeyboardAction.ReloadApp = new KeyboardAction({
+ optionName: "ReloadApp",
+ defaultShortcuts: ["f5", "mod+r"]
+});
+
+/** @static */
+KeyboardAction.OpenDevTools = new KeyboardAction({
+ optionName: "OpenDevTools",
+ defaultShortcuts: "mod+shift+i"
+});
+
+/** @static */
+KeyboardAction.FindInText = new KeyboardAction({
+ optionName: "FindInText",
+ defaultShortcuts: "mod+f"
+});
+
+/** @static */
+KeyboardAction.ToggleFullscreen = new KeyboardAction({
+ optionName: "ToggleFullscreen",
+ defaultShortcuts: "f11"
+});
+
+/** @static */
+KeyboardAction.ZoomOut = new KeyboardAction({
+ optionName: "ZoomOut",
+ defaultShortcuts: "mod+-"
+});
+
+/** @static */
+KeyboardAction.ZoomIn = new KeyboardAction({
+ optionName: "ZoomIn",
+ defaultShortcuts: "mod+="
+});
+
+export default KeyboardAction;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/frontend_api/services_keyboard_actions.js.html b/docs/frontend_api/services_keyboard_actions.js.html
new file mode 100644
index 000000000..6e24019bd
--- /dev/null
+++ b/docs/frontend_api/services_keyboard_actions.js.html
@@ -0,0 +1,61 @@
+
+
+
+
+ JSDoc: Source: services/keyboard_action.js
+
+
+
+
+
+
+
+
+
+
+
+
+ Source: services/keyboard_action.js
+
+
+
+
+
+
+
+
+ /**
+ * blaa vlaa
+ */
+class KeyboardActions {
+ constructor() {
+ /** @property {string} */
+ this.JUMP_TO = "";
+ }
+}
+
+export default KeyboardActions;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index 63c583348..506e6c846 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3112,9 +3112,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"ejs": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.1.tgz",
- "integrity": "sha512-kS/gEPzZs3Y1rRsbGX4UOSjtP/CeJP0CxSNZHYxGfVM/VgLcv0ZqM7C45YyTj2DI2g7+P9Dd24C+IMIg6D0nYQ=="
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.2.tgz",
+ "integrity": "sha512-rHGwtpl67oih3xAHbZlpw5rQAt+YV1mSCu2fUZ9XNrfaGEhom7E+AUiMci+ByP4aSfuAWx7hE0BPuJLMrpXwOw=="
},
"electron": {
"version": "6.0.12",
@@ -5990,9 +5990,9 @@
},
"dependencies": {
"glob": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
- "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -6546,12 +6546,13 @@
}
},
"imagemin": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.0.tgz",
- "integrity": "sha512-TXvCSSIYl4KQUASur9S0+E4olVECzvxvZABU9rNqsza7vzIrUQMRTjyczGf8OmtcgvZ9jOYyinXW3epOpd/04A==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz",
+ "integrity": "sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w==",
"requires": {
"file-type": "^12.0.0",
"globby": "^10.0.0",
+ "graceful-fs": "^4.2.2",
"junk": "^3.1.0",
"make-dir": "^3.0.0",
"p-pipe": "^3.0.0",
@@ -8130,11 +8131,18 @@
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
},
"mime-types": {
- "version": "2.1.24",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
- "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+ "version": "2.1.25",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
+ "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
"requires": {
- "mime-db": "1.40.0"
+ "mime-db": "1.42.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.42.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
+ "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ=="
+ }
}
},
"mimic-fn": {
@@ -9896,9 +9904,9 @@
"integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
},
"picomatch": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz",
- "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA=="
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz",
+ "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA=="
},
"pify": {
"version": "4.0.1",
diff --git a/package.json b/package.json
index 002203a60..33abff9ea 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"start-server": "TRILIUM_ENV=dev node ./src/www",
"start-electron": "TRILIUM_ENV=dev electron . --disable-gpu",
"build-backend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
- "build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js",
+ "build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/keyboard_action.js src/public/javascripts/services/frontend_script_api.js",
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs"
},
"dependencies": {
@@ -29,7 +29,7 @@
"csurf": "1.10.0",
"dayjs": "1.8.17",
"debug": "4.1.1",
- "ejs": "2.7.1",
+ "ejs": "2.7.2",
"electron-debug": "3.0.1",
"electron-dl": "1.14.0",
"electron-find": "1.0.6",
@@ -45,13 +45,13 @@
"http-proxy-agent": "2.1.0",
"https-proxy-agent": "3.0.1",
"image-type": "4.1.0",
- "imagemin": "7.0.0",
+ "imagemin": "7.0.1",
"imagemin-giflossy": "5.1.10",
"imagemin-mozjpeg": "8.0.0",
"imagemin-pngquant": "8.0.0",
"ini": "1.3.5",
"jimp": "0.8.5",
- "mime-types": "2.1.24",
+ "mime-types": "2.1.25",
"moment": "2.24.0",
"multer": "1.4.2",
"node-abi": "2.12.0",
diff --git a/src/entities/attribute.js b/src/entities/attribute.js
index 0bc68afa2..6755f113f 100644
--- a/src/entities/attribute.js
+++ b/src/entities/attribute.js
@@ -79,6 +79,10 @@ class Attribute extends Entity {
async beforeSaving() {
if (!this.value) {
+ if (this.type === 'relation') {
+ throw new Error(`Cannot save relation ${this.name} since it does not target any note.`);
+ }
+
// null value isn't allowed
this.value = "";
}
diff --git a/src/entities/note.js b/src/entities/note.js
index 7e0e544f1..0c8d18d25 100644
--- a/src/entities/note.js
+++ b/src/entities/note.js
@@ -114,6 +114,10 @@ class Note extends Entity {
/** @returns {Promise} */
async setContent(content) {
+ if (content === null || content === undefined) {
+ throw new Error(`Cannot set null content to note ${this.noteId}`);
+ }
+
// force updating note itself so that dateModified is represented correctly even for the content
this.forcedChange = true;
this.contentLength = content.length;
@@ -472,6 +476,32 @@ class Note extends Entity {
}
}
+ /**
+ * @return {Promise}
+ */
+ async addAttribute(type, name, value = "") {
+ const attr = new Attribute({
+ noteId: this.noteId,
+ type: type,
+ name: name,
+ value: value
+ });
+
+ await attr.save();
+
+ this.invalidateAttributeCache();
+
+ return attr;
+ }
+
+ async addLabel(name, value = "") {
+ return await this.addAttribute(LABEL, name, value);
+ }
+
+ async addRelation(name, targetNoteId) {
+ return await this.addAttribute(RELATION, name, targetNoteId);
+ }
+
/**
* @param {string} name - label name
* @returns {Promise} true if label exists (including inherited)
diff --git a/src/public/javascripts/services/entrypoints.js b/src/public/javascripts/services/entrypoints.js
index 8256ae96c..d0142e55d 100644
--- a/src/public/javascripts/services/entrypoints.js
+++ b/src/public/javascripts/services/entrypoints.js
@@ -92,12 +92,12 @@ function registerEntrypoints() {
}
}
+ // hide (toggle) everything except for the note content for zen mode
utils.bindGlobalShortcut('alt+m', e => {
- $(".hide-toggle").toggle();
- $("#container").toggleClass("distraction-free-mode");
+ $(".hide-in-zen-mode").toggle();
+ $("#container").toggleClass("zen-mode");
});
- // hide (toggle) everything except for the note content for distraction free writing
utils.bindGlobalShortcut('alt+t', e => {
const date = new Date();
const dateString = utils.formatDateTime(date);
diff --git a/src/public/javascripts/services/frontend_script_api.js b/src/public/javascripts/services/frontend_script_api.js
index ea6d2dd01..9d45a3677 100644
--- a/src/public/javascripts/services/frontend_script_api.js
+++ b/src/public/javascripts/services/frontend_script_api.js
@@ -11,6 +11,7 @@ import dateNotesService from './date_notes.js';
import StandardWidget from '../widgets/standard_widget.js';
import ws from "./ws.js";
import hoistedNoteService from "./hoisted_note.js";
+import KeyboardAction from "./keyboard_action.js";
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -40,6 +41,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
/** @property {StandardWidget} */
this.StandardWidget = StandardWidget;
+ /** @property {KeyboardAction} */
+ this.KeyboardAction = KeyboardAction;
+
/**
* Activates note in the tree and in the note detail.
*
diff --git a/src/public/javascripts/services/keyboard_action.js b/src/public/javascripts/services/keyboard_action.js
new file mode 100644
index 000000000..2d74036ec
--- /dev/null
+++ b/src/public/javascripts/services/keyboard_action.js
@@ -0,0 +1,261 @@
+/**
+ * blaa vlaa
+ */
+class KeyboardAction {
+ constructor(params) {
+ /** @property {string} */
+ this.optionName = params.optionName;
+ /** @property {string[]} */
+ this.defaultShortcuts = Array.isArray(params.defaultShortcuts) ? params.defaultShortcuts : [params.defaultShortcuts];
+ /** @property {string[]} */
+ this.activeShortcuts = this.defaultShortcuts.slice();
+ /** @property {string} */
+ this.description = params.description;
+ }
+
+ addShortcut(shortcut) {
+ this.activeShortcuts.push(shortcut);
+ }
+
+ /**
+ * @param {string|string[]} shortcuts
+ */
+ replaceShortcuts(shortcuts) {
+ this.activeShortcuts = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
+ }
+
+ /** @return {KeyboardAction[]} */
+ static get allActions() {
+ return Object.keys(KeyboardAction)
+ .map(key => KeyboardAction[key])
+ .filter(obj => obj instanceof KeyboardAction);
+ }
+}
+
+const ELECTRON = 1;
+
+/**
+ * Open "Jump to note" dialog
+ * @static
+ */
+KeyboardAction.JumpToNote = new KeyboardAction({
+ optionName: "JumpToNote",
+ defaultShortcuts: "mod+j",
+ description: 'Open "Jump to note" dialog'
+});
+
+/** @static */
+KeyboardAction.MarkdownToHTML = new KeyboardAction({
+ optionName: "MarkdownToHTML",
+ defaultShortcuts: "mod+return"
+});
+
+/** @static */
+KeyboardAction.NewTab = new KeyboardAction({
+ optionName: "NewTab",
+ defaultShortcuts: "mod+t",
+ only: ELECTRON
+});
+
+/** @static */
+KeyboardAction.CloseTab = new KeyboardAction({
+ optionName: "CloseTab",
+ defaultShortcuts: "mod+w",
+ only: ELECTRON
+});
+
+/** @static */
+KeyboardAction.NextTab = new KeyboardAction({
+ optionName: "NextTab",
+ defaultShortcuts: "mod+tab",
+ only: ELECTRON
+});
+
+/** @static */
+KeyboardAction.PreviousTab = new KeyboardAction({
+ optionName: "PreviousTab",
+ defaultShortcuts: "mod+shift+tab",
+ only: ELECTRON
+});
+
+/** @static */
+KeyboardAction.CreateNoteAfter = new KeyboardAction({
+ optionName: "CreateNoteAfter",
+ defaultShortcuts: "mod+o"
+});
+
+/** @static */
+KeyboardAction.CreateNoteInto = new KeyboardAction({
+ optionName: "CreateNoteInto",
+ defaultShortcuts: "mod+p"
+});
+
+/** @static */
+KeyboardAction.ScrollToActiveNote = new KeyboardAction({
+ optionName: "ScrollToActiveNote",
+ defaultShortcuts: "mod+."
+});
+
+/** @static */
+KeyboardAction.CollapseTree = new KeyboardAction({
+ optionName: "CollapseTree",
+ defaultShortcuts: "alt+c"
+});
+
+/** @static */
+KeyboardAction.RunSQL = new KeyboardAction({
+ optionName: "RunSQL",
+ defaultShortcuts: "mod+return"
+});
+
+/** @static */
+KeyboardAction.FocusNote = new KeyboardAction({
+ optionName: "FocusNote",
+ defaultShortcuts: "return"
+});
+
+/** @static */
+KeyboardAction.RunCurrentNote = new KeyboardAction({
+ optionName: "RunCurrentNote",
+ defaultShortcuts: "mod+return"
+});
+
+/** @static */
+KeyboardAction.ClipboardCopy = new KeyboardAction({
+ optionName: "ClipboardCopy",
+ defaultShortcuts: "mod+c"
+});
+
+/** @static */
+KeyboardAction.ClipboardPaste = new KeyboardAction({
+ optionName: "ClipboardPaste",
+ defaultShortcuts: "mod+v"
+});
+
+/** @static */
+KeyboardAction.ClipboardCut = new KeyboardAction({
+ optionName: "ClipboardCut",
+ defaultShortcuts: "mod+x"
+});
+
+/** @static */
+KeyboardAction.SelectAllNotesInParent = new KeyboardAction({
+ optionName: "SelectAllNotesInParent",
+ defaultShortcuts: "mod+a"
+});
+
+/** @static */
+KeyboardAction.Undo = new KeyboardAction({
+ optionName: "Undo",
+ defaultShortcuts: "mod+z"
+});
+
+/** @static */
+KeyboardAction.Redo = new KeyboardAction({
+ optionName: "Redo",
+ defaultShortcuts: "mod+y"
+});
+
+/** @static */
+KeyboardAction.AddLinkToText = new KeyboardAction({
+ optionName: "AddLinkToText",
+ defaultShortcuts: "mod+l"
+});
+
+/** @static */
+KeyboardAction.CloneNotesTo = new KeyboardAction({
+ optionName: "CloneNotesTo",
+ defaultShortcuts: "mod+shift+c"
+});
+
+/** @static */
+KeyboardAction.MoveNotesTo = new KeyboardAction({
+ optionName: "MoveNotesTo",
+ defaultShortcuts: "mod+shift+c"
+});
+
+/** @static */
+KeyboardAction.SearchNotes = new KeyboardAction({
+ optionName: "SearchNotes",
+ defaultShortcuts: "mod+s"
+});
+
+/** @static */
+KeyboardAction.ShowAttributes = new KeyboardAction({
+ optionName: "ShowAttributes",
+ defaultShortcuts: "alt+a"
+});
+
+/** @static */
+KeyboardAction.ShowHelp = new KeyboardAction({
+ optionName: "ShowHelp",
+ defaultShortcuts: "f1"
+});
+
+/** @static */
+KeyboardAction.OpenSQLConsole = new KeyboardAction({
+ optionName: "OpenSQLConsole",
+ defaultShortcuts: "alt+o"
+});
+
+/** @static */
+KeyboardAction.BackInNoteHistory = new KeyboardAction({
+ optionName: "BackInNoteHistory",
+ defaultShortcuts: "alt+left"
+});
+
+/** @static */
+KeyboardAction.ForwardInNoteHistory = new KeyboardAction({
+ optionName: "ForwardInNoteHistory",
+ defaultShortcuts: "alt+right"
+});
+
+/** @static */
+KeyboardAction.ToggleZenMode = new KeyboardAction({
+ optionName: "ToggleZenMode",
+ defaultShortcuts: "alt+m"
+});
+
+/** @static */
+KeyboardAction.InsertDateTime = new KeyboardAction({
+ optionName: "InsertDateTime",
+ defaultShortcuts: "alt+t"
+});
+
+/** @static */
+KeyboardAction.ReloadApp = new KeyboardAction({
+ optionName: "ReloadApp",
+ defaultShortcuts: ["f5", "mod+r"]
+});
+
+/** @static */
+KeyboardAction.OpenDevTools = new KeyboardAction({
+ optionName: "OpenDevTools",
+ defaultShortcuts: "mod+shift+i"
+});
+
+/** @static */
+KeyboardAction.FindInText = new KeyboardAction({
+ optionName: "FindInText",
+ defaultShortcuts: "mod+f"
+});
+
+/** @static */
+KeyboardAction.ToggleFullscreen = new KeyboardAction({
+ optionName: "ToggleFullscreen",
+ defaultShortcuts: "f11"
+});
+
+/** @static */
+KeyboardAction.ZoomOut = new KeyboardAction({
+ optionName: "ZoomOut",
+ defaultShortcuts: "mod+-"
+});
+
+/** @static */
+KeyboardAction.ZoomIn = new KeyboardAction({
+ optionName: "ZoomIn",
+ defaultShortcuts: "mod+="
+});
+
+export default KeyboardAction;
\ No newline at end of file
diff --git a/src/public/javascripts/services/keys.js b/src/public/javascripts/services/keys.js
new file mode 100644
index 000000000..64517f91b
--- /dev/null
+++ b/src/public/javascripts/services/keys.js
@@ -0,0 +1,16 @@
+class Actions {
+ constructor() {
+ this.JUMP_TO = "";
+ }
+}
+
+const actions = new Actions();
+
+function bind() {
+
+}
+
+export default {
+ actions,
+ bind
+};
\ No newline at end of file
diff --git a/src/public/javascripts/services/tab_row.js b/src/public/javascripts/services/tab_row.js
index 1d5b4048b..870da284a 100644
--- a/src/public/javascripts/services/tab_row.js
+++ b/src/public/javascripts/services/tab_row.js
@@ -29,6 +29,7 @@ const tabTemplate = `
`;
const newTabButtonTemplate = `+ `;
+const fillerTemplate = ``;
class TabRow {
constructor(el) {
@@ -40,9 +41,10 @@ class TabRow {
this.setupStyleEl();
this.setupEvents();
- this.layoutTabs();
this.setupDraggabilly();
this.setupNewButton();
+ this.setupFiller();
+ this.layoutTabs();
this.setVisibility();
}
@@ -109,12 +111,17 @@ class TabRow {
const widths = [];
let extraWidthRemaining = totalExtraWidthDueToFlooring;
+
for (let i = 0; i < numberOfTabs; i += 1) {
const extraWidth = flooredClampedTargetWidth < TAB_CONTENT_MAX_WIDTH && extraWidthRemaining > 0 ? 1 : 0;
widths.push(flooredClampedTargetWidth + extraWidth);
if (extraWidthRemaining > 0) extraWidthRemaining -= 1;
}
+ if (this.fillerEl) {
+ this.fillerEl.style.width = extraWidthRemaining + "px";
+ }
+
return widths;
}
@@ -129,8 +136,9 @@ class TabRow {
});
const newTabPosition = position;
+ const fillerPosition = position + 32;
- return {tabPositions, newTabPosition};
+ return {tabPositions, newTabPosition, fillerPosition};
}
layoutTabs() {
@@ -151,13 +159,14 @@ class TabRow {
let styleHTML = '';
- const {tabPositions, newTabPosition} = this.getTabPositions();
+ const {tabPositions, newTabPosition, fillerPosition} = this.getTabPositions();
tabPositions.forEach((position, i) => {
styleHTML += `.note-tab:nth-child(${ i + 1 }) { transform: translate3d(${ position }px, 0, 0)} `;
});
styleHTML += `.note-new-tab { transform: translate3d(${ newTabPosition }px, 0, 0) } `;
+ styleHTML += `.tab-row-filler { transform: translate3d(${ fillerPosition }px, 0, 0) } `;
this.styleEl.innerHTML = styleHTML;
}
@@ -387,11 +396,18 @@ class TabRow {
this.newTabEl = div.firstElementChild;
this.tabContentEl.appendChild(this.newTabEl);
- this.layoutTabs();
this.newTabEl.addEventListener('click', _ => this.emit('newTab'));
}
+ setupFiller() {
+ const div = document.createElement('div');
+ div.innerHTML = fillerTemplate;
+ this.fillerEl = div.firstElementChild;
+
+ this.tabContentEl.appendChild(this.fillerEl);
+ }
+
closest(value, array) {
let closest = Infinity;
let closestIndex = -1;
diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js
index dbbaa84be..d56f79108 100644
--- a/src/public/javascripts/services/tree.js
+++ b/src/public/javascripts/services/tree.js
@@ -368,7 +368,7 @@ async function treeInitialized() {
const notePath = location.hash.substr(1);
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
- if (await treeCache.noteExists(noteId)) {
+ if (noteId && await treeCache.noteExists(noteId)) {
for (const tab of openTabs) {
tab.active = false;
}
@@ -649,11 +649,9 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
const newNoteName = extraOptions.title || "new note";
- const {note, branch} = await server.post('notes/' + parentNoteId + '/children', {
+ const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${target}&targetBranchId=${node.data.branchId}`, {
title: newNoteName,
- content: extraOptions.content,
- target: target,
- target_branchId: node.data.branchId,
+ content: extraOptions.content || "",
isProtected: extraOptions.isProtected,
type: extraOptions.type
});
diff --git a/src/public/stylesheets/desktop.css b/src/public/stylesheets/desktop.css
index 9622e07da..206be72b1 100644
--- a/src/public/stylesheets/desktop.css
+++ b/src/public/stylesheets/desktop.css
@@ -18,7 +18,7 @@ body {
grid-gap: 0;
}
-#container.distraction-free-mode {
+#container.zen-mode {
grid-template-areas:
"tab-container" !important;
grid-template-rows: auto
@@ -59,7 +59,7 @@ body {
background-color: var(--header-background-color);
display: flex;
align-items: center;
- padding: 4px;
+ padding-top: 4px;
}
#header button {
@@ -68,18 +68,26 @@ body {
margin-bottom: 2px;
margin-top: 2px;
margin-right: 8px;
+ border-color: transparent !important;
+}
+
+#header button.btn-sm .bx {
+ position: relative;
+ top: 1px;
+}
+
+#header button:hover {
+ border-color: var(--button-border-color) !important;
}
#history-navigation {
margin: 0 15px 0 5px;
- position: relative;
- top: 2px;
}
#global-buttons {
display: flex;
justify-content: space-around;
- padding: 10px 0 10px 0;
+ padding: 3px 0 3px 0;
border: 1px solid var(--main-border-color);
border-radius: 7px;
margin: 5px 15px 5px 5px;
@@ -145,7 +153,7 @@ body {
::-webkit-scrollbar-thumb {
border-radius: 3px;
- border: 1px solid var(--main-border-color);
+ border: 1px solid var(--scrollbar-border-color);
}
::-webkit-scrollbar-corner {
@@ -165,6 +173,13 @@ body {
cursor: pointer;
position: relative;
top: -1px;
+ border: 1px solid transparent;
+ padding: 2px;
+ border-radius: 2px;
+}
+
+.refresh-search-button:hover {
+ border-color: var(--button-border-color);
}
.note-title-row {
@@ -215,7 +230,7 @@ body {
.note-new-tab {
position: absolute;
left: 0;
- height: 32px;
+ height: 33px;
width: 32px;
border: 0;
margin: 0;
@@ -223,6 +238,7 @@ body {
text-align: center;
font-size: 24px;
cursor: pointer;
+ border-bottom: 1px solid var(--button-border-color);
}
.note-new-tab:hover {
@@ -230,6 +246,14 @@ body {
border-radius: 5px;
}
+.tab-row-filler {
+ position: absolute;
+ left: 0;
+ background: linear-gradient(to right, var(--button-border-color), transparent);
+ height: 1px;
+ margin-top: 32px;
+}
+
.note-tab-row .note-tab[active] {
z-index: 5;
}
@@ -244,6 +268,7 @@ body {
top: 10px;
animation: note-tab-was-just-added 120ms forwards ease-in-out;
}
+
.note-tab-row .note-tab .note-tab-wrapper {
position: absolute;
display: flex;
@@ -256,11 +281,14 @@ body {
border-top-right-radius: 8px;
overflow: hidden;
pointer-events: all;
- background-image: linear-gradient(to bottom, var(--accented-background-color), var(--main-background-color));
+ background-color: var(--accented-background-color);
+ border-bottom: 1px solid var(--button-border-color);
}
.note-tab-row .note-tab[active] .note-tab-wrapper {
- background-image: linear-gradient(to bottom, var(--more-accented-background-color), var(--main-background-color));
+ background-color: var(--main-background-color);
+ border: 1px solid var(--button-border-color);
+ border-bottom: 0;
font-weight: bold;
}
@@ -268,6 +296,7 @@ body {
padding-left: 2px;
padding-right: 2px;
}
+
.note-tab-row .note-tab .note-tab-title {
flex: 1;
vertical-align: top;
@@ -275,12 +304,15 @@ body {
white-space: nowrap;
color: var(--muted-text-color);
}
+
.note-tab-row .note-tab[is-small] .note-tab-title {
margin-left: 0;
}
+
.note-tab-row .note-tab[active] .note-tab-title {
color: var(--main-text-color);
}
+
.note-tab-row .note-tab .note-tab-drag-handle {
position: absolute;
top: 0;
@@ -290,6 +322,7 @@ body {
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
+
.note-tab-row .note-tab .note-tab-close {
flex-grow: 0;
flex-shrink: 0;
@@ -348,6 +381,14 @@ body {
.hide-sidebar-button {
color: var(--main-text-color);
+ background: none;
+ border: 1px solid transparent;
+ padding: 2px 8px 2px 8px;
+ border-radius: 2px;
+}
+
+.hide-sidebar-button:hover {
+ border-color: var(--button-border-color);
}
.note-detail-sidebar {
@@ -380,6 +421,8 @@ body {
border: 0;
background: inherit;
font-weight: bold;
+ text-transform: uppercase;
+ color: var(--muted-text-color) !important;
}
.note-detail-sidebar .widget-header-action {
@@ -388,7 +431,9 @@ body {
}
.note-detail-sidebar .widget-help {
- color: var(--main-text-color);
+ color: var(--muted-text-color);
+ position: relative;
+ top: 2px;
}
.note-detail-sidebar .widget-help.no-link:hover {
diff --git a/src/public/stylesheets/mobile.css b/src/public/stylesheets/mobile.css
index ab1a78969..b97088011 100644
--- a/src/public/stylesheets/mobile.css
+++ b/src/public/stylesheets/mobile.css
@@ -19,7 +19,7 @@ html, body {
display: flex;
flex-shrink: 0;
justify-content: space-around;
- padding: 10px 0 10px 0;
+ padding: 3px 0 3px 0;
margin: 0 10px 0 16px;
}
diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css
index 97f0f6eac..00d4d2be6 100644
--- a/src/public/stylesheets/style.css
+++ b/src/public/stylesheets/style.css
@@ -144,9 +144,9 @@ ul.fancytree-container {
margin-top: 0;
}
-/** we disable shield background when in distraction free mode because I couldn't get it to stay static
+/** we disable shield background when in zen mode because I couldn't get it to stay static
(it kept growing with content) */
-#container:not(.distraction-free-mode) .note-tab-content.protected {
+#container:not(.zen-mode) .note-tab-content.protected {
/* DON'T COLLAPSE THE RULES INTO SINGLE ONE, BACKGROUND WON'T DISPLAY */
background: url('../images/shield.svg') no-repeat;
background-size: contain;
@@ -227,9 +227,13 @@ span.fancytree-node.archived {
.icon-action:hover {
text-decoration: none;
+ border-color: var(--button-border-color);
}
.icon-action {
+ border: 1px solid transparent;
+ border-radius: 3px;
+ padding: 5px;
cursor: pointer;
font-size: 1.5em;
}
diff --git a/src/public/stylesheets/themes.css b/src/public/stylesheets/themes.css
index b769d7362..032a38086 100644
--- a/src/public/stylesheets/themes.css
+++ b/src/public/stylesheets/themes.css
@@ -10,26 +10,27 @@
--main-text-color: black;
--main-border-color: #ccc;
--accented-background-color: #f5f5f5;
- --more-accented-background-color: #ccc;
- --header-background-color: #f8f8f8;
- --button-background-color: #eee;
- --button-disabled-background-color: #ccc;
+ --more-accented-background-color: #ddd;
+ --header-background-color: #fff;
+ --button-background-color: #fff;
+ --button-disabled-background-color: #ddd;
--button-border-color: #ddd;
--button-text-color: black;
--button-border-radius: 5px;
- --muted-text-color: #444;
+ --muted-text-color: #666;
--input-text-color: black;
--input-background-color: white;
--hover-item-text-color: black;
--hover-item-background-color: #eee;
--active-item-text-color: black;
- --active-item-background-color: #ccc;
+ --active-item-background-color: #ddd;
--menu-text-color: black;
--menu-background-color: white;
--tooltip-background-color: #f8f8f8;
--link-color: blue;
--modal-background-color: white;
--modal-backdrop-color: black;
+ --scrollbar-border-color: #ddd;
}
body.theme-black {
@@ -56,6 +57,7 @@ body.theme-black {
--link-color: lightskyblue;
--modal-background-color: black;
--modal-backdrop-color: #444;
+ --scrollbar-border-color: #888;
}
body.theme-black .CodeMirror {
@@ -65,7 +67,7 @@ body.theme-black .CodeMirror {
body.theme-dark {
--main-background-color: #333;
--main-text-color: white;
- --main-border-color: #ddd;
+ --main-border-color: #aaa;
--accented-background-color: #555;
--more-accented-background-color: #777;
--header-background-color: #333;
@@ -86,6 +88,7 @@ body.theme-dark {
--link-color: lightskyblue;
--modal-background-color: #333;
--modal-backdrop-color: #444;
+ --scrollbar-border-color: #888;
}
body.theme-dark .CodeMirror {
diff --git a/src/routes/api/clipper.js b/src/routes/api/clipper.js
index 9bf806950..0344037a5 100644
--- a/src/routes/api/clipper.js
+++ b/src/routes/api/clipper.js
@@ -31,7 +31,11 @@ async function addClipping(req) {
let clippingNote = await findClippingNote(todayNote, pageUrl);
if (!clippingNote) {
- clippingNote = (await noteService.createNote(todayNote.noteId, title, '')).note;
+ clippingNote = (await noteService.createNewNote({
+ parentNoteId: todayNote.noteId,
+ title: title,
+ content: ''
+ })).note;
await clippingNote.setLabel('clipType', 'clippings');
await clippingNote.setLabel('pageUrl', pageUrl);
@@ -51,7 +55,11 @@ async function createNote(req) {
const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
- const {note} = await noteService.createNote(todayNote.noteId, title, content);
+ const {note} = await noteService.createNewNote({
+ parentNoteId: todayNote.noteId,
+ title,
+ content
+ });
await note.setLabel('clipType', clipType);
diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js
index f0c7cb228..29b0b6849 100644
--- a/src/routes/api/notes.js
+++ b/src/routes/api/notes.js
@@ -53,10 +53,12 @@ async function getChildren(req) {
}
async function createNote(req) {
- const parentNoteId = req.params.parentNoteId;
- const newNote = req.body;
+ const params = Object.assign({}, req.body); // clone
+ params.parentNoteId = req.params.parentNoteId;
- const { note, branch } = await noteService.createNewNote(parentNoteId, newNote, req);
+ const { target, targetBranchId } = req.query;
+
+ const { note, branch } = await noteService.createNewNoteWithTarget(target, targetBranchId, params);
await treeService.setCssClassesToNotes([note]);
diff --git a/src/routes/api/sender.js b/src/routes/api/sender.js
index 06d87d7b4..c60cab4ac 100644
--- a/src/routes/api/sender.js
+++ b/src/routes/api/sender.js
@@ -26,10 +26,10 @@ async function uploadImage(req) {
async function saveNote(req) {
const parentNote = await dateNoteService.getDateNote(req.headers['x-local-date']);
- const {note, branch} = await noteService.createNewNote(parentNote.noteId, {
+ const {note, branch} = await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
title: req.body.title,
content: req.body.content,
- target: 'into',
isProtected: false,
type: 'text',
mime: 'text/html'
diff --git a/src/services/backend_script_api.js b/src/services/backend_script_api.js
index c430c9f88..2e1f639fe 100644
--- a/src/services/backend_script_api.js
+++ b/src/services/backend_script_api.js
@@ -178,7 +178,7 @@ function BackendScriptApi(currentNote, apiParams) {
*/
/**
- * @typedef {object} CreateNoteExtraOptions
+ * @typedef {object} CreateNoteParams
* @property {boolean} [json=false] - should the note be JSON
* @property {boolean} [isProtected=false] - should the note be protected
* @property {string} [type='text'] - note type
@@ -189,32 +189,10 @@ function BackendScriptApi(currentNote, apiParams) {
/**
* @method
*
- * @param {string} parentNoteId - create new note under this parent
- * @param {string} title
- * @param {string} [content=""]
- * @param {CreateNoteExtraOptions} [extraOptions={}]
+ * @param {CreateNoteParams} [extraOptions={}]
* @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
*/
- this.createNote = noteService.createNote;
-
- /**
- * Creates new note according to given params and force all connected clients to refresh their tree.
- *
- * @method
- *
- * @param {string} parentNoteId - create new note under this parent
- * @param {string} title
- * @param {string} [content=""]
- * @param {CreateNoteExtraOptions} [extraOptions={}]
- * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
- */
- this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) {
- const ret = await noteService.createNote(parentNoteId, title, content, extraOptions);
-
- ws.refreshTree();
-
- return ret;
- };
+ this.createNote = noteService.createNewNote;
/**
* Log given message to trilium logs.
diff --git a/src/services/date_notes.js b/src/services/date_notes.js
index 7887b4b40..270258576 100644
--- a/src/services/date_notes.js
+++ b/src/services/date_notes.js
@@ -14,10 +14,10 @@ const DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Satur
const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
async function createNote(parentNoteId, noteTitle, noteText) {
- return (await noteService.createNewNote(parentNoteId, {
+ return (await noteService.createNewNote({
+ parentNoteId: parentNoteId,
title: noteTitle,
content: noteText,
- target: 'into',
isProtected: false
})).note;
}
@@ -35,7 +35,8 @@ async function getRootCalendarNote() {
let rootNote = await attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
if (!rootNote) {
- rootNote = (await noteService.createNewNote('root', {
+ rootNote = (await noteService.createNewNote({
+ parentNoteId: 'root',
title: 'Calendar',
target: 'into',
isProtected: false
diff --git a/src/services/image.js b/src/services/image.js
index 34cdb6ec7..21a3c3a3b 100644
--- a/src/services/image.js
+++ b/src/services/image.js
@@ -57,14 +57,17 @@ async function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSw
const parentNote = await repository.getNote(parentNoteId);
- const {note} = await noteService.createNote(parentNoteId, fileName, buffer, {
- target: 'into',
+ const {note} = await noteService.createNewNote({
+ parentNoteId,
+ title: fileName,
+ content: buffer,
type: 'image',
- isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
mime: 'image/' + imageFormat.ext.toLowerCase(),
- attributes: [{ type: 'label', name: 'originalFileName', value: originalName }]
+ isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable()
});
+ await note.addLabel('originalFileName', originalName);
+
return {
fileName,
note,
diff --git a/src/services/import/enex.js b/src/services/import/enex.js
index e3e38c857..0bf1b56a2 100644
--- a/src/services/import/enex.js
+++ b/src/services/import/enex.js
@@ -27,7 +27,10 @@ async function importEnex(taskContext, file, parentNote) {
: file.originalname;
// root note is new note into all ENEX/notebook's notes will be imported
- const rootNote = (await noteService.createNote(parentNote.noteId, rootNoteTitle, "", {
+ const rootNote = (await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
+ title: rootNoteTitle,
+ content: "",
type: 'text',
mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
@@ -193,14 +196,20 @@ async function importEnex(taskContext, file, parentNote) {
content = extractContent(content);
- const noteEntity = (await noteService.createNote(rootNote.noteId, title, content, {
- attributes,
+ const noteEntity = (await noteService.createNewNote({
+ parentNoteId: rootNote.noteId,
+ title,
+ content,
utcDateCreated,
type: 'text',
mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note;
+ for (const attr of attributes) {
+ await noteEntity.addAttribute(attr.type, attr.name, attr.value);
+ }
+
taskContext.increaseProgressCount();
let noteContent = await noteEntity.getContent();
@@ -217,13 +226,19 @@ async function importEnex(taskContext, file, parentNote) {
}
const createFileNote = async () => {
- const resourceNote = (await noteService.createNote(noteEntity.noteId, resource.title, resource.content, {
- attributes: resource.attributes,
+ const resourceNote = (await noteService.createNewNote({
+ parentNoteId: noteEntity.noteId,
+ title: resource.title,
+ content: resource.content,
type: 'file',
mime: resource.mime,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note;
+ for (const attr of resource.attributes) {
+ await noteEntity.addAttribute(attr.type, attr.name, attr.value);
+ }
+
taskContext.increaseProgressCount();
const resourceLink = `${utils.escapeHtml(resource.title)}`;
diff --git a/src/services/import/opml.js b/src/services/import/opml.js
index 3b9e4f852..fc993dbf7 100644
--- a/src/services/import/opml.js
+++ b/src/services/import/opml.js
@@ -44,8 +44,12 @@ async function importOpml(taskContext, fileBuffer, parentNote) {
throw new Error("Unrecognized OPML version " + opmlVersion);
}
- const {note} = await noteService.createNote(parentNoteId, title, content, {
- isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
+ const {note} = await noteService.createNewNote({
+ parentNoteId,
+ title,
+ content,
+ type: 'text',
+ isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable()
});
taskContext.increaseProgressCount();
diff --git a/src/services/import/single.js b/src/services/import/single.js
index 18c871fd0..983cca355 100644
--- a/src/services/import/single.js
+++ b/src/services/import/single.js
@@ -41,16 +41,18 @@ async function importImage(file, parentNote, taskContext) {
async function importFile(taskContext, file, parentNote) {
const originalName = file.originalname;
- const size = file.size;
- const {note} = await noteService.createNote(parentNote.noteId, originalName, file.buffer, {
- target: 'into',
+ const {note} = await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
+ title: originalName,
+ content: file.buffer,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
type: 'file',
- mime: mimeService.getMime(originalName) || file.mimetype,
- attributes: [{ type: "label", name: "originalFileName", value: originalName }]
+ mime: mimeService.getMime(originalName) || file.mimetype
});
+ await note.addLabel("originalFileName", originalName);
+
taskContext.increaseProgressCount();
return note;
@@ -62,7 +64,10 @@ async function importCodeNote(taskContext, file, parentNote) {
const detectedMime = mimeService.getMime(file.originalname) || file.mimetype;
const mime = mimeService.normalizeMimeType(detectedMime);
- const {note} = await noteService.createNote(parentNote.noteId, title, content, {
+ const {note} = await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
+ title,
+ content,
type: 'code',
mime: mime,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable()
@@ -78,7 +83,10 @@ async function importPlainText(taskContext, file, parentNote) {
const plainTextContent = file.buffer.toString("UTF-8");
const htmlContent = convertTextToHtml(plainTextContent);
- const {note} = await noteService.createNote(parentNote.noteId, title, htmlContent, {
+ const {note} = await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
+ title,
+ content: htmlContent,
type: 'text',
mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
@@ -118,7 +126,10 @@ async function importMarkdown(taskContext, file, parentNote) {
const title = getFileNameWithoutExtension(file.originalname);
- const {note} = await noteService.createNote(parentNote.noteId, title, htmlContent, {
+ const {note} = await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
+ title,
+ content: htmlContent,
type: 'text',
mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
@@ -133,7 +144,10 @@ async function importHtml(taskContext, file, parentNote) {
const title = getFileNameWithoutExtension(file.originalname);
const content = file.buffer.toString("UTF-8");
- const {note} = await noteService.createNote(parentNote.noteId, title, content, {
+ const {note} = await noteService.createNewNote({
+ parentNoteId: parentNote.noteId,
+ title,
+ content,
type: 'text',
mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
diff --git a/src/services/import/tar.js b/src/services/import/tar.js
index b459d8c17..d71a6c55b 100644
--- a/src/services/import/tar.js
+++ b/src/services/import/tar.js
@@ -177,8 +177,11 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
return;
}
- ({note} = await noteService.createNote(parentNoteId, noteTitle, '', {
- noteId,
+ ({note} = await noteService.createNewNote({
+ parentNoteId: parentNoteId,
+ title: noteTitle,
+ content: '',
+ noteId: noteId,
type: noteMeta ? noteMeta.type : 'text',
mime: noteMeta ? noteMeta.mime : 'text/html',
prefix: noteMeta ? noteMeta.prefix : '',
@@ -324,7 +327,10 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
await note.setContent(content);
}
else {
- ({note} = await noteService.createNote(parentNoteId, noteTitle, content, {
+ ({note} = await noteService.createNewNote({
+ parentNoteId: parentNoteId,
+ title: noteTitle,
+ content: content,
noteId,
type,
mime,
diff --git a/src/services/keyboard_actions.js b/src/services/keyboard_actions.js
new file mode 100644
index 000000000..6a8d345b4
--- /dev/null
+++ b/src/services/keyboard_actions.js
@@ -0,0 +1,157 @@
+const ELECTRON = "electron";
+
+const KEYBOARD_ACTIONS = [
+ {
+ optionName: "JumpToNote",
+ defaultShortcuts: ["mod+j"],
+ description: 'Open "Jump to note" dialog'
+ },
+ {
+ optionName: "MarkdownToHTML",
+ defaultShortcuts: ["mod+return"]
+ },
+ {
+ optionName: "NewTab",
+ defaultShortcuts: ["mod+t"],
+ only: ELECTRON
+ },
+ {
+ optionName: "CloseTab",
+ defaultShortcuts: ["mod+w"],
+ only: ELECTRON
+ },
+ {
+ optionName: "NextTab",
+ defaultShortcuts: ["mod+tab"],
+ only: ELECTRON
+ },
+ {
+ optionName: "PreviousTab",
+ defaultShortcuts: ["mod+shift+tab"],
+ only: ELECTRON
+ },
+ {
+ optionName: "CreateNoteAfter",
+ defaultShortcuts: ["mod+o"]
+ },
+ {
+ optionName: "CreateNoteInto",
+ defaultShortcuts: ["mod+p"]
+ },
+ {
+ optionName: "ScrollToActiveNote",
+ defaultShortcuts: ["mod+."]
+ },
+ {
+ optionName: "CollapseTree",
+ defaultShortcuts: ["alt+c"]
+ },
+ {
+ optionName: "RunSQL",
+ defaultShortcuts: ["mod+return"]
+ },
+ {
+ optionName: "FocusNote",
+ defaultShortcuts: ["return"]
+ },
+ {
+ optionName: "RunCurrentNote",
+ defaultShortcuts: ["mod+return"]
+ },
+ {
+ optionName: "ClipboardCopy",
+ defaultShortcuts: ["mod+c"]
+ },
+ {
+ optionName: "ClipboardPaste",
+ defaultShortcuts: ["mod+v"]
+ },
+ {
+ optionName: "ClipboardCut",
+ defaultShortcuts: ["mod+x"]
+ },
+ {
+ optionName: "SelectAllNotesInParent",
+ defaultShortcuts: ["mod+a"]
+ },
+ {
+ optionName: "Undo",
+ defaultShortcuts: ["mod+z"]
+ },
+ {
+ optionName: "Redo",
+ defaultShortcuts: ["mod+y"]
+ },
+ {
+ optionName: "AddLinkToText",
+ defaultShortcuts: ["mod+l"]
+ },
+ {
+ optionName: "CloneNotesTo",
+ defaultShortcuts: ["mod+shift+c"]
+ },
+ {
+ optionName: "MoveNotesTo",
+ defaultShortcuts: ["mod+shift+c"]
+ },
+ {
+ optionName: "SearchNotes",
+ defaultShortcuts: ["mod+s"]
+ },
+ {
+ optionName: "ShowAttributes",
+ defaultShortcuts: ["alt+a"]
+ },
+ {
+ optionName: "ShowHelp",
+ defaultShortcuts: ["f1"]
+ },
+ {
+ optionName: "OpenSQLConsole",
+ defaultShortcuts: ["alt+o"]
+ },
+ {
+ optionName: "BackInNoteHistory",
+ defaultShortcuts: ["alt+left"]
+ },
+ {
+ optionName: "ForwardInNoteHistory",
+ defaultShortcuts: ["alt+right"]
+ },
+ {
+ optionName: "ToggleZenMode",
+ defaultShortcuts: ["alt+m"]
+ },
+ {
+ optionName: "InsertDateTime",
+ defaultShortcuts: ["alt+t"]
+ },
+ {
+ optionName: "ReloadApp",
+ defaultShortcuts: ["f5", "mod+r"]
+ },
+ {
+ optionName: "OpenDevTools",
+ defaultShortcuts: ["mod+shift+i"]
+ },
+ {
+ optionName: "FindInText",
+ defaultShortcuts: ["mod+f"]
+ },
+ {
+ optionName: "ToggleFullscreen",
+ defaultShortcuts: ["f11"]
+ },
+ {
+ optionName: "ZoomOut",
+ defaultShortcuts: ["mod+-"]
+ },
+ {
+ optionName: "ZoomIn",
+ defaultShortcuts: ["mod+="]
+ }
+];
+
+module.exports = {
+ KEYBOARD_ACTIONS
+};
\ No newline at end of file
diff --git a/src/services/notes.js b/src/services/notes.js
index cd516eaab..08d148291 100644
--- a/src/services/notes.js
+++ b/src/services/notes.js
@@ -16,29 +16,14 @@ const protectedSessionService = require('../services/protected_session');
const log = require('../services/log');
const noteRevisionService = require('../services/note_revisions');
-async function getNewNotePosition(parentNoteId, noteData) {
- let newNotePos = 0;
+async function getNewNotePosition(parentNoteId) {
+ const maxNotePos = await sql.getValue(`
+ SELECT MAX(notePosition)
+ FROM branches
+ WHERE parentNoteId = ?
+ AND isDeleted = 0`, [parentNoteId]);
- if (noteData.target === 'into') {
- const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]);
-
- newNotePos = maxNotePos === null ? 0 : maxNotePos + 10;
- }
- else if (noteData.target === 'after') {
- const afterNote = await sql.getRow('SELECT notePosition FROM branches WHERE branchId = ?', [noteData.target_branchId]);
-
- newNotePos = afterNote.notePosition + 10;
-
- // not updating utcDateModified to avoig having to sync whole rows
- await sql.execute('UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0',
- [parentNoteId, afterNote.notePosition]);
-
- await syncTableService.addNoteReorderingSync(parentNoteId);
- }
- else {
- throw new Error('Unknown target: ' + noteData.target);
- }
- return newNotePos;
+ return maxNotePos === null ? 0 : maxNotePos + 10;
}
async function triggerChildNoteCreated(childNote, parentNote) {
@@ -49,88 +34,90 @@ async function triggerNoteTitleChanged(note) {
await eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
}
-/**
- * FIXME: noteData has mandatory property "target", it might be better to add it as parameter to reflect this
- */
-async function createNewNote(parentNoteId, noteData) {
- let newNotePos;
-
- if (noteData.notePosition !== undefined) {
- newNotePos = noteData.notePosition;
- }
- else {
- newNotePos = await getNewNotePosition(parentNoteId, noteData);
+function deriveMime(type, mime) {
+ if (!type) {
+ throw new Error(`Note type is a required param`);
}
- const parentNote = await repository.getNote(parentNoteId);
-
- if (!parentNote) {
- throw new Error(`Parent note ${parentNoteId} not found.`);
+ if (mime) {
+ return mime;
}
- if (!noteData.type) {
- if (parentNote.type === 'text' || parentNote.type === 'code') {
- noteData.type = parentNote.type;
- noteData.mime = parentNote.mime;
- }
- else {
- // inheriting note type makes sense only for text and code
- noteData.type = 'text';
- noteData.mime = 'text/html';
- }
+ if (type === 'text') {
+ mime = 'text/html';
+ } else if (type === 'code') {
+ mime = 'text/plain';
+ } else if (['relation-map', 'search'].includes(type)) {
+ mime = 'application/json';
}
- if (!noteData.mime) {
- if (noteData.type === 'text') {
- noteData.mime = 'text/html';
- }
- else if (noteData.type === 'code') {
- noteData.mime = 'text/plain';
- }
- else if (noteData.type === 'relation-map' || noteData.type === 'search') {
- noteData.mime = 'application/json';
- }
- }
-
- noteData.type = noteData.type || parentNote.type;
- noteData.mime = noteData.mime || parentNote.mime;
-
- const note = await new Note({
- noteId: noteData.noteId, // optionally can force specific noteId
- title: noteData.title,
- isProtected: noteData.isProtected,
- type: noteData.type || 'text',
- mime: noteData.mime || 'text/html'
- }).save();
-
- if (note.isStringNote() || this.type === 'render') { // render to just make sure it's not null
- noteData.content = noteData.content || "";
- }
-
- await note.setContent(noteData.content);
-
- const branch = await new Branch({
- noteId: note.noteId,
- parentNoteId: parentNoteId,
- notePosition: newNotePos,
- prefix: noteData.prefix,
- isExpanded: !!noteData.isExpanded
- }).save();
+ return mime;
+}
+async function copyChildAttributes(parentNote, childNote) {
for (const attr of await parentNote.getAttributes()) {
if (attr.name.startsWith("child:")) {
await new Attribute({
- noteId: note.noteId,
- type: attr.type,
- name: attr.name.substr(6),
- value: attr.value,
- position: attr.position,
- isInheritable: attr.isInheritable
+ noteId: childNote.noteId,
+ type: attr.type,
+ name: attr.name.substr(6),
+ value: attr.value,
+ position: attr.position,
+ isInheritable: attr.isInheritable
}).save();
- note.invalidateAttributeCache();
+ childNote.invalidateAttributeCache();
}
}
+}
+
+/**
+ * Following object properties are mandatory:
+ * - {string} parentNoteId
+ * - {string} title
+ * - {*} content
+ * - {string} type - text, code, file, image, search, book, relation-map
+ *
+ * Following are optional (have defaults)
+ * - {string} mime - value is derived from default mimes for type
+ * - {boolean} isProtected - default is false
+ * - {boolean} isExpanded - default is false
+ * - {string} prefix - default is empty string
+ * - {integer} notePosition - default is last existing notePosition in a parent + 10
+ *
+ * @param params
+ * @return {Promise<{note: Note, branch: Branch}>}
+ */
+async function createNewNote(params) {
+ const parentNote = await repository.getNote(params.parentNoteId);
+
+ if (!parentNote) {
+ throw new Error(`Parent note ${params.parentNoteId} not found.`);
+ }
+
+ if (!params.title || params.title.trim().length === 0) {
+ throw new Error(`Note title must not be empty`);
+ }
+
+ const note = await new Note({
+ noteId: params.noteId, // optionally can force specific noteId
+ title: params.title,
+ isProtected: !!params.isProtected,
+ type: params.type,
+ mime: deriveMime(params.type, params.mime)
+ }).save();
+
+ await note.setContent(params.content);
+
+ const branch = await new Branch({
+ noteId: note.noteId,
+ parentNoteId: params.parentNoteId,
+ notePosition: params.notePosition !== undefined ? params.notePosition : await getNewNotePosition(params.parentNoteId),
+ prefix: params.prefix,
+ isExpanded: !!params.isExpanded
+ }).save();
+
+ await copyChildAttributes(parentNote, note);
await triggerNoteTitleChanged(note);
await triggerChildNoteCreated(note, parentNote);
@@ -141,41 +128,59 @@ async function createNewNote(parentNoteId, noteData) {
};
}
-async function createNote(parentNoteId, title, content = "", extraOptions = {}) {
- if (!parentNoteId) throw new Error("Empty parentNoteId");
- if (!title) throw new Error("Empty title");
+async function createNewNoteWithTarget(target, targetBranchId, params) {
+ if (!params.type) {
+ const parentNote = await repository.getNote(params.parentNoteId);
- const noteData = {
- title: title,
- content: extraOptions.json ? JSON.stringify(content, null, '\t') : content,
- target: 'into',
- noteId: extraOptions.noteId,
- isProtected: !!extraOptions.isProtected,
- type: extraOptions.type,
- mime: extraOptions.mime,
- dateCreated: extraOptions.dateCreated,
- isExpanded: extraOptions.isExpanded,
- notePosition: extraOptions.notePosition
- };
-
- if (extraOptions.json && !noteData.type) {
- noteData.type = "code";
- noteData.mime = "application/json";
+ // code note type can be inherited, otherwise text is default
+ params.type = parentNote.type === 'code' ? 'code' : 'text';
+ params.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html';
}
- const {note, branch} = await createNewNote(parentNoteId, noteData);
-
- for (const attr of extraOptions.attributes || []) {
- await attributeService.createAttribute({
- noteId: note.noteId,
- type: attr.type,
- name: attr.name,
- value: attr.value,
- isInheritable: !!attr.isInheritable
- });
+ if (target === 'into') {
+ return await createNewNote(params);
}
+ else if (target === 'after') {
+ const afterNote = await sql.getRow('SELECT notePosition FROM branches WHERE branchId = ?', [noteData.target_branchId]);
- return {note, branch};
+ // not updating utcDateModified to avoig having to sync whole rows
+ await sql.execute('UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0',
+ [params.parentNoteId, afterNote.notePosition]);
+
+ params.notePosition = afterNote.notePosition + 10;
+
+ await createNewNote(params);
+
+ await syncTableService.addNoteReorderingSync(params.parentNoteId);
+ }
+ else {
+ throw new Error(`Unknown target ${target}`);
+ }
+}
+
+// methods below should be probably just backend API methods
+async function createJsonNote(parentNoteId, title, content = {}, params = {}) {
+ params.parentNoteId = parentNoteId;
+ params.title = title;
+
+ params.type = "code";
+ params.mime = "application/json";
+
+ params.content = JSON.stringify(content, null, '\t');
+
+ return await createNewNote(params);
+}
+
+async function createTextNote(parentNoteId, title, content = "", params = {}) {
+ params.parentNoteId = parentNoteId;
+ params.title = title;
+
+ params.type = "text";
+ params.mime = "text/html";
+
+ params.content = content;
+
+ return await createNewNote(params);
}
async function protectNoteRecursively(note, protect, taskContext) {
@@ -537,7 +542,7 @@ sqlInit.dbReady.then(() => {
module.exports = {
createNewNote,
- createNote,
+ createNewNoteWithTarget,
updateNote,
deleteBranch,
protectNoteRecursively,
diff --git a/src/services/options_init.js b/src/services/options_init.js
index a67063cc0..26efc4ca1 100644
--- a/src/services/options_init.js
+++ b/src/services/options_init.js
@@ -5,6 +5,7 @@ const appInfo = require('./app_info');
const utils = require('./utils');
const log = require('./log');
const dateUtils = require('./date_utils');
+const keyboardActions = require('./keyboard_actions');
async function initDocumentOptions() {
await optionService.createOption('documentId', utils.randomSecureToken(16), false);
@@ -87,7 +88,9 @@ const defaultOptions = [
async function initStartupOptions() {
const optionsMap = await optionService.getOptionsMap();
- for (const {name, value, isSynced} of defaultOptions) {
+ const allDefaultOptions = defaultOptions.concat(getKeyboardDefaultOptions());
+
+ for (const {name, value, isSynced} of allDefaultOptions) {
if (!(name in optionsMap)) {
await optionService.createOption(name, value, isSynced);
@@ -96,6 +99,16 @@ async function initStartupOptions() {
}
}
+function getKeyboardDefaultOptions() {
+ return keyboardActions.KEYBOARD_ACTIONS.map(ka => {
+ return {
+ name: "keyboardShortcuts" + ka.optionName,
+ value: JSON.stringify(ka.defaultShortcuts),
+ isSynced: false
+ };
+ });
+}
+
module.exports = {
initDocumentOptions,
initSyncedOptions,
diff --git a/src/tools/generate_document.js b/src/tools/generate_document.js
index 7c2fb6b1e..bf250ca77 100644
--- a/src/tools/generate_document.js
+++ b/src/tools/generate_document.js
@@ -50,7 +50,12 @@ async function start() {
const content = loremIpsum({ count: paragraphCount, units: 'paragraphs', sentenceLowerBound: 1, sentenceUpperBound: 15,
paragraphLowerBound: 3, paragraphUpperBound: 10, format: 'html' });
- const {note} = await noteService.createNote(getRandomParentNoteId(), title, content);
+ const {note} = await noteService.createNewNote({
+ parentNoteId: getRandomParentNoteId(),
+ title,
+ content,
+ type: 'text'
+ });
console.log(`Created note ${i}: ${title}`);
diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs
index e6a078476..09b1527dd 100644
--- a/src/views/desktop.ejs
+++ b/src/views/desktop.ejs
@@ -10,12 +10,10 @@
|