diff --git a/docs/backend_api/Note.html b/docs/backend_api/Note.html
index 292a16dbf..2c9c217f3 100644
--- a/docs/backend_api/Note.html
+++ b/docs/backend_api/Note.html
@@ -1226,7 +1226,7 @@ See addLabel, addRelation for more specific methods.
Source:
@@ -1513,7 +1513,7 @@ See addLabel, addRelation for more specific methods.
Source:
@@ -1692,7 +1692,7 @@ returned.
Source:
@@ -1927,7 +1927,7 @@ returned.
Source:
@@ -2127,7 +2127,7 @@ returned.
Source:
@@ -2389,7 +2389,7 @@ returned.
Source:
@@ -2495,7 +2495,7 @@ returned.
Source:
@@ -2669,7 +2669,7 @@ returned.
Source:
@@ -2847,7 +2847,7 @@ returned.
Source:
@@ -3674,7 +3674,7 @@ returned.
Source:
@@ -3931,7 +3931,7 @@ returned.
Source:
@@ -4089,7 +4089,7 @@ returned.
Source:
@@ -4259,7 +4259,7 @@ returned.
Source:
@@ -4426,7 +4426,7 @@ returned.
Source:
@@ -4532,7 +4532,7 @@ returned.
Source:
@@ -4712,7 +4712,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4977,7 +4977,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5132,7 +5132,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5290,7 +5290,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5460,7 +5460,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5627,7 +5627,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5782,7 +5782,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5940,7 +5940,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6110,7 +6110,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6558,7 +6558,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6716,7 +6716,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6886,7 +6886,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7101,7 +7101,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7309,7 +7309,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7411,7 +7411,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7513,7 +7513,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7615,7 +7615,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7811,7 +7811,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8037,7 +8037,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8333,7 +8333,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8562,7 +8562,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8760,7 +8760,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8958,7 +8958,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9156,7 +9156,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9306,7 +9306,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -10228,7 +10228,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -10408,7 +10408,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -10588,7 +10588,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -10783,7 +10783,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11015,7 +11015,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11195,7 +11195,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11355,7 +11355,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11597,7 +11597,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11808,7 +11808,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -12019,7 +12019,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
diff --git a/docs/backend_api/becca_entities_note.js.html b/docs/backend_api/becca_entities_note.js.html
index a7da74aec..bcca49db7 100644
--- a/docs/backend_api/becca_entities_note.js.html
+++ b/docs/backend_api/becca_entities_note.js.html
@@ -360,7 +360,7 @@ class Note extends AbstractEntity {
/** @returns {boolean} true if this note is JavaScript (code or attachment) */
isJavaScript() {
- return (this.type === "code" || this.type === "file")
+ return (this.type === "code" || this.type === "file" || this.type === 'launcher')
&& (this.mime.startsWith("application/javascript")
|| this.mime === "application/x-javascript"
|| this.mime === "text/javascript");
@@ -400,6 +400,8 @@ class Note extends AbstractEntity {
* @returns {Attribute[]} all note's attributes, including inherited ones
*/
getAttributes(type, name) {
+ this.__validateTypeName(type, name);
+
this.__getAttributes([]);
if (type && name) {
@@ -489,6 +491,19 @@ class Note extends AbstractEntity {
return this.inheritableAttributeCache;
}
+ __validateTypeName(type, name) {
+ if (type && type !== 'label' && type !== 'relation') {
+ throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`);
+ }
+
+ if (name) {
+ const firstLetter = name.charAt(0);
+ if (firstLetter === '#' || firstLetter === '~') {
+ throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`);
+ }
+ }
+ }
+
/**
* @param type
* @param name
@@ -693,10 +708,7 @@ class Note extends AbstractEntity {
* @returns {Attribute[]} note's "owned" attributes - excluding inherited ones
*/
getOwnedAttributes(type = null, name = null, value = null) {
- // it's a common mistake to include # or ~ into attribute name
- if (name && ["#", "~"].includes(name[0])) {
- name = name.substr(1);
- }
+ this.__validateTypeName(type, name);
if (type && name && value !== undefined && value !== null) {
return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name && attr.value === value);
diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html
index 0ccd21eac..4b296420c 100644
--- a/docs/frontend_api/FrontendScriptApi.html
+++ b/docs/frontend_api/FrontendScriptApi.html
@@ -1212,13 +1212,16 @@
-
+
+
+ Adds a new launcher to the launchbar. If the launcher (id) already exists, it will be updated.
+
@@ -1228,6 +1231,56 @@
+ Parameters:
+
+
+
+
+
+
@@ -1248,7 +1301,7 @@
- Deprecated:- this API has no effect anymore. Use bookmarks or launchpad shortcuts instead.
+ Deprecated:- you can now create/modify launchers in the
@@ -1262,7 +1315,7 @@
Source:
@@ -1399,7 +1452,7 @@
Source:
@@ -1538,7 +1591,7 @@
Source:
@@ -1746,7 +1799,7 @@
Source:
@@ -2110,7 +2163,7 @@
Source:
@@ -2243,7 +2296,7 @@
Source:
@@ -2353,7 +2406,7 @@
Source:
@@ -2459,7 +2512,7 @@
Source:
@@ -2565,7 +2618,7 @@
Source:
@@ -2675,7 +2728,7 @@
Source:
@@ -2786,7 +2839,7 @@ implementation of actual widget type.
Source:
@@ -2890,7 +2943,7 @@ implementation of actual widget type.
Source:
@@ -2998,7 +3051,7 @@ implementation of actual widget type.
Source:
@@ -3166,7 +3219,7 @@ implementation of actual widget type.
Source:
@@ -3303,7 +3356,7 @@ implementation of actual widget type.
Source:
@@ -3460,7 +3513,7 @@ implementation of actual widget type.
Source:
@@ -3615,7 +3668,7 @@ implementation of actual widget type.
Source:
@@ -3722,7 +3775,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3877,7 +3930,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -4033,7 +4086,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -4234,7 +4287,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4340,7 +4393,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4495,7 +4548,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4650,7 +4703,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4800,7 +4853,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5289,7 +5342,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5397,7 +5450,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5553,7 +5606,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5709,7 +5762,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5846,7 +5899,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6000,7 +6053,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6086,7 +6139,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6223,7 +6276,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6384,7 +6437,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6492,7 +6545,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6630,7 +6683,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6786,7 +6839,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6941,7 +6994,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7092,7 +7145,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7229,7 +7282,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7366,7 +7419,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7526,7 +7579,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7686,7 +7739,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7778,7 +7831,7 @@ Typical use case is when new note has been created, we should wait until it is s
Source:
diff --git a/docs/frontend_api/NoteShort.html b/docs/frontend_api/NoteShort.html
index a5a3f6060..6a35fdec0 100644
--- a/docs/frontend_api/NoteShort.html
+++ b/docs/frontend_api/NoteShort.html
@@ -1103,7 +1103,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -1281,7 +1281,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2150,7 +2150,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2305,7 +2305,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2472,7 +2472,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2582,7 +2582,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2756,7 +2756,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2934,7 +2934,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3289,7 +3289,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3444,7 +3444,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3611,7 +3611,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3766,7 +3766,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3921,7 +3921,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4088,7 +4088,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4651,7 +4651,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4806,7 +4806,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4976,7 +4976,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5127,7 +5127,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5294,7 +5294,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5400,7 +5400,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5513,7 +5513,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5619,7 +5619,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5721,7 +5721,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5895,7 +5895,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6152,7 +6152,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6330,7 +6330,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6485,7 +6485,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6640,7 +6640,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6795,7 +6795,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6903,7 +6903,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6987,7 +6987,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -7093,7 +7093,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
diff --git a/docs/frontend_api/entities_note_short.js.html b/docs/frontend_api/entities_note_short.js.html
index 85b0bad4d..d07ab3507 100644
--- a/docs/frontend_api/entities_note_short.js.html
+++ b/docs/frontend_api/entities_note_short.js.html
@@ -382,6 +382,8 @@ class NoteShort {
return a.isSearch ? 1 : -1;
} else if (a.isArchived !== b.isArchived) {
return a.isArchived ? 1 : -1;
+ } else if (a.isHidden !== b.isHidden) {
+ return a.isHidden ? 1 : -1;
} else {
return a.notePath.length - b.notePath.length;
}
@@ -391,6 +393,8 @@ class NoteShort {
}
__filterAttrs(attributes, type, name) {
+ this.__validateTypeName(type, name);
+
if (!type && !name) {
return attributes;
} else if (type && name) {
@@ -408,6 +412,19 @@ class NoteShort {
return attrs.filter(attr => attr.isInheritable);
}
+ __validateTypeName(type, name) {
+ if (type && type !== 'label' && type !== 'relation') {
+ throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`);
+ }
+
+ if (name) {
+ const firstLetter = name.charAt(0);
+ if (firstLetter === '#' || firstLetter === '~') {
+ throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`);
+ }
+ }
+ }
+
/**
* @param {string} [name] - label name to filter
* @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones
@@ -784,7 +801,7 @@ class NoteShort {
/** @returns {boolean} true if this note is JavaScript (code or attachment) */
isJavaScript() {
- return (this.type === "code" || this.type === "file")
+ return (this.type === "code" || this.type === "file" || this.type === 'launcher')
&& (this.mime.startsWith("application/javascript")
|| this.mime === "application/x-javascript"
|| this.mime === "text/javascript");
diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html
index d721331e5..ebe94b12e 100644
--- a/docs/frontend_api/global.html
+++ b/docs/frontend_api/global.html
@@ -194,7 +194,7 @@
-
+CreateOrUpdateLauncherOptions
@@ -241,6 +241,38 @@
+
+
+ id |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+ |
+
+
+
+
+ id of the button, used to identify the old instances of this button to be replaced
+ ID is optional because of BC, but not specifying it is deprecated. ID can be alphanumeric only. |
+
+
+
+
title |
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html
index 81a9e2905..2a2bf55af 100644
--- a/docs/frontend_api/services_frontend_script_api.js.html
+++ b/docs/frontend_api/services_frontend_script_api.js.html
@@ -134,7 +134,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
};
/**
- * @typedef {Object} ToolbarButtonOptions
+ * @typedef {Object} CreateOrUpdateLauncherOptions
+ * @property {string} [id] - id of the button, used to identify the old instances of this button to be replaced
+ * ID is optional because of BC, but not specifying it is deprecated. ID can be alphanumeric only.
* @property {string} title
* @property {string} [icon] - name of the boxicon to be used (e.g. "time" for "bx-time" icon)
* @property {function} action - callback handling the click on the button
@@ -142,9 +144,17 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*/
/**
- * @deprecated this API has no effect anymore. Use bookmarks or launchpad shortcuts instead.
+ * Adds a new launcher to the launchbar. If the launcher (id) already exists, it will be updated.
+ *
+ * @deprecated you can now create/modify launchers in the
+ * @param {CreateOrUpdateLauncherOptions} opts
*/
- this.addButtonToToolbar = () => console.warn("api.addButtonToToolbar() calls are deprecated and have no effect");
+ this.addButtonToToolbar = async opts => {
+ const {action, ...reqBody} = opts;
+ reqBody.action = action.toString();
+
+ await server.put('special-notes/api-script-launcher', reqBody);
+ };
function prepareParams(params) {
if (!params) {
diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js
index a07228378..c71607dd5 100644
--- a/src/becca/entities/note.js
+++ b/src/becca/entities/note.js
@@ -332,7 +332,7 @@ class Note extends AbstractEntity {
/** @returns {boolean} true if this note is JavaScript (code or attachment) */
isJavaScript() {
- return (this.type === "code" || this.type === "file")
+ return (this.type === "code" || this.type === "file" || this.type === 'launcher')
&& (this.mime.startsWith("application/javascript")
|| this.mime === "application/x-javascript"
|| this.mime === "text/javascript");
@@ -372,6 +372,8 @@ class Note extends AbstractEntity {
* @returns {Attribute[]} all note's attributes, including inherited ones
*/
getAttributes(type, name) {
+ this.__validateTypeName(type, name);
+
this.__getAttributes([]);
if (type && name) {
@@ -461,6 +463,19 @@ class Note extends AbstractEntity {
return this.inheritableAttributeCache;
}
+ __validateTypeName(type, name) {
+ if (type && type !== 'label' && type !== 'relation') {
+ throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`);
+ }
+
+ if (name) {
+ const firstLetter = name.charAt(0);
+ if (firstLetter === '#' || firstLetter === '~') {
+ throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`);
+ }
+ }
+ }
+
/**
* @param type
* @param name
@@ -665,10 +680,7 @@ class Note extends AbstractEntity {
* @returns {Attribute[]} note's "owned" attributes - excluding inherited ones
*/
getOwnedAttributes(type = null, name = null, value = null) {
- // it's a common mistake to include # or ~ into attribute name
- if (name && ["#", "~"].includes(name[0])) {
- name = name.substr(1);
- }
+ this.__validateTypeName(type, name);
if (type && name && value !== undefined && value !== null) {
return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name && attr.value === value);
diff --git a/src/public/app/entities/note_short.js b/src/public/app/entities/note_short.js
index 6635d3a3d..af08e045f 100644
--- a/src/public/app/entities/note_short.js
+++ b/src/public/app/entities/note_short.js
@@ -365,6 +365,8 @@ class NoteShort {
}
__filterAttrs(attributes, type, name) {
+ this.__validateTypeName(type, name);
+
if (!type && !name) {
return attributes;
} else if (type && name) {
@@ -382,6 +384,19 @@ class NoteShort {
return attrs.filter(attr => attr.isInheritable);
}
+ __validateTypeName(type, name) {
+ if (type && type !== 'label' && type !== 'relation') {
+ throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`);
+ }
+
+ if (name) {
+ const firstLetter = name.charAt(0);
+ if (firstLetter === '#' || firstLetter === '~') {
+ throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`);
+ }
+ }
+ }
+
/**
* @param {string} [name] - label name to filter
* @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones
@@ -758,7 +773,7 @@ class NoteShort {
/** @returns {boolean} true if this note is JavaScript (code or attachment) */
isJavaScript() {
- return (this.type === "code" || this.type === "file")
+ return (this.type === "code" || this.type === "file" || this.type === 'launcher')
&& (this.mime.startsWith("application/javascript")
|| this.mime === "application/x-javascript"
|| this.mime === "text/javascript");
diff --git a/src/public/app/services/frontend_script_api.js b/src/public/app/services/frontend_script_api.js
index fd0368e31..32f4af3b0 100644
--- a/src/public/app/services/frontend_script_api.js
+++ b/src/public/app/services/frontend_script_api.js
@@ -106,7 +106,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
};
/**
- * @typedef {Object} ToolbarButtonOptions
+ * @typedef {Object} CreateOrUpdateLauncherOptions
+ * @property {string} [id] - id of the button, used to identify the old instances of this button to be replaced
+ * ID is optional because of BC, but not specifying it is deprecated. ID can be alphanumeric only.
* @property {string} title
* @property {string} [icon] - name of the boxicon to be used (e.g. "time" for "bx-time" icon)
* @property {function} action - callback handling the click on the button
@@ -114,9 +116,19 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*/
/**
- * @deprecated this API has no effect anymore. Use bookmarks or launchpad shortcuts instead.
+ * Adds a new launcher to the launchbar. If the launcher (id) already exists, it will be updated.
+ *
+ * @deprecated you can now create/modify launchers in the top-left Menu -> Configure Launchbar
+ * @param {CreateOrUpdateLauncherOptions} opts
*/
- this.addButtonToToolbar = () => console.warn("api.addButtonToToolbar() calls are deprecated and have no effect");
+ this.addButtonToToolbar = async opts => {
+ console.warn("api.addButtonToToolbar() has been deprecated since v0.58 and may be removed in the future. Use Menu -> Configure Launchbar to create/update launchers instead.");
+
+ const {action, ...reqBody} = opts;
+ reqBody.action = action.toString();
+
+ await server.put('special-notes/api-script-launcher', reqBody);
+ };
function prepareParams(params) {
if (!params) {
diff --git a/src/public/app/widgets/buttons/launcher/script_launcher.js b/src/public/app/widgets/buttons/launcher/script_launcher.js
index de3d3c0fb..c8ee4c6b4 100644
--- a/src/public/app/widgets/buttons/launcher/script_launcher.js
+++ b/src/public/app/widgets/buttons/launcher/script_launcher.js
@@ -6,12 +6,16 @@ export default class ScriptLauncher extends AbstractLauncher {
this.title(this.launcherNote.title)
.icon(this.launcherNote.getIcon())
- .onClick(this.handler);
+ .onClick(() => this.launch());
}
async launch() {
- const script = await this.launcherNote.getRelationTarget('script');
+ if (this.launcherNote.hasLabel('scriptInLauncherContent')) {
+ await this.launcherNote.executeScript();
+ } else {
+ const script = await this.launcherNote.getRelationTarget('script');
- await script.executeScript();
+ await script.executeScript();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/routes/api/special_notes.js b/src/routes/api/special_notes.js
index fb9979e54..04e41e22b 100644
--- a/src/routes/api/special_notes.js
+++ b/src/routes/api/special_notes.js
@@ -74,6 +74,10 @@ function resetLauncher(req) {
return specialNotesService.resetLauncher(req.params.noteId);
}
+function createOrUpdateScriptLauncherFromApi(req) {
+ return specialNotesService.createOrUpdateScriptLauncherFromApi(req.body);
+}
+
module.exports = {
getInboxNote,
getDayNote,
@@ -86,5 +90,6 @@ module.exports = {
createSearchNote,
saveSearchNote,
createLauncher,
- resetLauncher
+ resetLauncher,
+ createOrUpdateScriptLauncherFromApi
};
diff --git a/src/routes/routes.js b/src/routes/routes.js
index ff1dde1fa..8c64c46a1 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -343,6 +343,7 @@ function register(app) {
apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote);
apiRoute(POST, '/api/special-notes/launchers/:noteId/reset', specialNotesRoute.resetLauncher);
apiRoute(POST, '/api/special-notes/launchers/:parentNoteId/:launcherType', specialNotesRoute.createLauncher);
+ apiRoute(PUT, '/api/special-notes/api-script-launcher', specialNotesRoute.createOrUpdateScriptLauncherFromApi);
// :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename
route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage);
diff --git a/src/services/special_notes.js b/src/services/special_notes.js
index 577f01535..d5c2f5dd2 100644
--- a/src/services/special_notes.js
+++ b/src/services/special_notes.js
@@ -143,6 +143,19 @@ function getHoistedNote() {
return becca.getNote(cls.getHoistedNoteId());
}
+function createScriptLauncher(parentNoteId, forceNoteId = null) {
+ const note = noteService.createNewNote({
+ noteId: forceNoteId,
+ title: "Script Launcher",
+ type: 'launcher',
+ content: '',
+ parentNoteId: parentNoteId
+ }).note;
+
+ note.addRelation('template', 'lbTplScriptLauncher');
+ return note;
+}
+
function createLauncher(parentNoteId, launcherType) {
let note;
@@ -156,14 +169,7 @@ function createLauncher(parentNoteId, launcherType) {
note.addRelation('template', 'lbTplNoteLauncher');
} else if (launcherType === 'script') {
- note = noteService.createNewNote({
- title: "Script Launcher",
- type: 'launcher',
- content: '',
- parentNoteId: parentNoteId
- }).note;
-
- note.addRelation('template', 'lbTplScriptLauncher');
+ note = createScriptLauncher(parentNoteId);
} else if (launcherType === 'customWidget') {
note = noteService.createNewNote({
title: "Widget Launcher",
@@ -215,6 +221,47 @@ function resetLauncher(noteId) {
hiddenSubtreeService.checkHiddenSubtree();
}
+/**
+ * This exists to ease transition into the new launchbar, but it's not meant to be a permanent functionality.
+ * Previously, the launchbar was fixed and the only way to add buttons was through this API, so a lot of buttons have been
+ * created just to fill this user hole.
+ *
+ * Another use case would be for script-packages (of which only few exists) which could this way register automatically
+ * into the launchbar. For such use cases this might be a usable replacement, but I'm not yet clear on that.
+ */
+function createOrUpdateScriptLauncherFromApi(opts) {
+ const launcherId = opts.id || ("tb" + opts.title.replace(/[^[a-z0-9]/gi, ""));
+
+ if (!opts.title) {
+ throw new Error("Title is mandatory property to create or update a launcher.");
+ } else if (!/^[a-z0-9]+$/i.test(launcherId)) {
+ throw new Error(`Launcher ID can be alphanumeric only, '${launcherId}' given`);
+ }
+
+ const launcherNote = becca.getNote(launcherId)
+ || createScriptLauncher('lbVisibleLaunchers', launcherId);
+
+ launcherNote.title = opts.title;
+ launcherNote.setContent("(" + opts.action + ")()");
+ launcherNote.setLabel('scriptInLauncherContent'); // there's no target note, the script is in the launcher's content
+ launcherNote.mime = 'application/javascript;env=frontend';
+ launcherNote.save();
+
+ if (opts.shortcut) {
+ launcherNote.setLabel('keyboardShortcut', opts.shortcut);
+ } else {
+ launcherNote.removeLabel('keyboardShortcut');
+ }
+
+ if (opts.icon) {
+ launcherNote.setLabel('iconClass', "bx bx-" + opts.icon);
+ } else {
+ launcherNote.removeLabel('iconClass');
+ }
+
+ return launcherNote;
+}
+
module.exports = {
getInboxNote,
createSqlConsole,
@@ -222,5 +269,6 @@ module.exports = {
createSearchNote,
saveSearchNote,
createLauncher,
- resetLauncher
+ resetLauncher,
+ createOrUpdateScriptLauncherFromApi
};