From c1e8a4b3840b23945430affeaaa5c854f4023dc5 Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 6 Oct 2019 21:35:26 +0200 Subject: [PATCH] spell check support + small options tabs reorganization --- .../0146__add_spell_check_options.sql | 5 + docs/backend_api/BackendScriptApi.html | 22 +-- .../services_backend_script_api.js.html | 7 +- docs/frontend_api/FrontendScriptApi.html | 174 +++++++++++++---- docs/frontend_api/NoteShort.html | 73 ++++---- docs/frontend_api/entities_note_short.js.html | 3 + docs/frontend_api/global.html | 2 +- .../services_frontend_script_api.js.html | 7 +- electron.js | 39 ++-- package-lock.json | 175 ++++++++++++++++-- package.json | 1 + src/app.js | 5 +- src/public/javascripts/desktop.js | 4 + src/public/javascripts/dialogs/options.js | 3 +- .../dialogs/options/note_revisions.js | 20 -- .../javascripts/dialogs/options/other.js | 54 ++++++ .../dialogs/options/protected_session.js | 28 --- src/public/javascripts/services/options.js | 6 +- .../javascripts/services/spell_check.js | 43 +++++ src/routes/api/options.js | 4 +- src/services/app_info.js | 2 +- src/services/options_init.js | 3 + src/views/dialogs/options.ejs | 12 +- src/views/dialogs/options/note_revisions.ejs | 18 -- src/views/dialogs/options/other.ejs | 44 +++++ .../dialogs/options/protected_session.ejs | 19 -- 26 files changed, 560 insertions(+), 213 deletions(-) create mode 100644 db/migrations/0146__add_spell_check_options.sql delete mode 100644 src/public/javascripts/dialogs/options/note_revisions.js create mode 100644 src/public/javascripts/dialogs/options/other.js delete mode 100644 src/public/javascripts/dialogs/options/protected_session.js create mode 100644 src/public/javascripts/services/spell_check.js delete mode 100644 src/views/dialogs/options/note_revisions.ejs create mode 100644 src/views/dialogs/options/other.ejs delete mode 100644 src/views/dialogs/options/protected_session.ejs diff --git a/db/migrations/0146__add_spell_check_options.sql b/db/migrations/0146__add_spell_check_options.sql new file mode 100644 index 000000000..7f2378f39 --- /dev/null +++ b/db/migrations/0146__add_spell_check_options.sql @@ -0,0 +1,5 @@ +INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced) +VALUES ('spellCheckEnabled', 'true', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0); + +INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced) +VALUES ('spellCheckLanguageCode', 'en-US', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0); \ No newline at end of file diff --git a/docs/backend_api/BackendScriptApi.html b/docs/backend_api/BackendScriptApi.html index 803a7e61d..e4dc8c372 100644 --- a/docs/backend_api/BackendScriptApi.html +++ b/docs/backend_api/BackendScriptApi.html @@ -396,7 +396,7 @@ the backend.
Source:
@@ -1533,7 +1533,7 @@ the backend.
Source:
@@ -1997,7 +1997,7 @@ the backend.
Source:
@@ -2765,7 +2765,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3418,7 +3418,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3596,7 +3596,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3751,7 +3751,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3901,7 +3901,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -4427,7 +4427,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
Source:
@@ -4560,7 +4560,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
Source:
@@ -4935,7 +4935,7 @@ transactional by default.
Source:
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html index 9afc0aea4..52e7f3546 100644 --- a/docs/backend_api/services_backend_script_api.js.html +++ b/docs/backend_api/services_backend_script_api.js.html @@ -237,9 +237,11 @@ function BackendScriptApi(currentNote, apiParams) { * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch */ this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) { - await noteService.createNote(parentNoteId, title, content, extraOptions); + const ret = await noteService.createNote(parentNoteId, title, content, extraOptions); ws.refreshTree(); + + return ret; }; /** @@ -343,7 +345,8 @@ function BackendScriptApi(currentNote, apiParams) { this.getAppInfo = () => appInfo } -module.exports = BackendScriptApi; +module.exports = BackendScriptApi; + diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html index 58f9fcc86..8df157743 100644 --- a/docs/frontend_api/FrontendScriptApi.html +++ b/docs/frontend_api/FrontendScriptApi.html @@ -131,6 +131,116 @@ +

$container

+ + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
container + + +jQuery + + + + of all the rendered script content
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +

currentNote

@@ -223,7 +333,7 @@
Source:
@@ -336,7 +446,7 @@
Source:
@@ -442,7 +552,7 @@
Source:
@@ -552,7 +662,7 @@
Source:
@@ -661,7 +771,7 @@
Source:
@@ -790,7 +900,7 @@
Source:
@@ -945,7 +1055,7 @@
Source:
@@ -1100,7 +1210,7 @@
Source:
@@ -1280,7 +1390,7 @@
Source:
@@ -1413,7 +1523,7 @@
Source:
@@ -1519,7 +1629,7 @@
Source:
@@ -1625,7 +1735,7 @@
Source:
@@ -1784,7 +1894,7 @@
Source:
@@ -1891,7 +2001,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -2046,7 +2156,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -2202,7 +2312,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -2403,7 +2513,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2509,7 +2619,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2664,7 +2774,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -2773,7 +2883,7 @@ note.
Source:
@@ -2928,7 +3038,7 @@ note.
Source:
@@ -3061,7 +3171,7 @@ note.
Source:
@@ -3167,7 +3277,7 @@ note.
Source:
@@ -3255,7 +3365,7 @@ note.
Source:
@@ -3406,7 +3516,7 @@ note.
Source:
@@ -3539,7 +3649,7 @@ note.
Source:
@@ -3700,7 +3810,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -3860,7 +3970,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4016,7 +4126,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4167,7 +4277,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4304,7 +4414,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -4441,7 +4551,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
diff --git a/docs/frontend_api/NoteShort.html b/docs/frontend_api/NoteShort.html index 6514efb81..11675a2c8 100644 --- a/docs/frontend_api/NoteShort.html +++ b/docs/frontend_api/NoteShort.html @@ -30,7 +30,10 @@

NoteShort()

-
This note's representation is used in note tree and is kept in TreeCache.
+
FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache. +Attribute cache should be limited to "transaction". + +This note's representation is used in note tree and is kept in TreeCache.
@@ -93,7 +96,7 @@
Source:
@@ -183,7 +186,7 @@
Source:
@@ -241,7 +244,7 @@
Source:
@@ -299,7 +302,7 @@
Source:
@@ -357,7 +360,7 @@
Source:
@@ -415,7 +418,7 @@
Source:
@@ -473,7 +476,7 @@
Source:
@@ -531,7 +534,7 @@
Source:
@@ -679,7 +682,7 @@
Source:
@@ -846,7 +849,7 @@
Source:
@@ -1020,7 +1023,7 @@
Source:
@@ -1126,7 +1129,7 @@
Source:
@@ -1228,7 +1231,7 @@
Source:
@@ -1330,7 +1333,7 @@
Source:
@@ -1432,7 +1435,7 @@
Source:
@@ -1583,7 +1586,7 @@
Source:
@@ -1750,7 +1753,7 @@
Source:
@@ -1917,7 +1920,7 @@
Source:
@@ -2072,7 +2075,7 @@
Source:
@@ -2178,7 +2181,7 @@
Source:
@@ -2280,7 +2283,7 @@
Source:
@@ -2431,7 +2434,7 @@
Source:
@@ -2598,7 +2601,7 @@
Source:
@@ -2765,7 +2768,7 @@
Source:
@@ -2920,7 +2923,7 @@
Source:
@@ -3090,7 +3093,7 @@
Source:
@@ -3241,7 +3244,7 @@
Source:
@@ -3351,7 +3354,7 @@
Source:
@@ -3525,7 +3528,7 @@
Source:
@@ -3631,7 +3634,7 @@
Source:
@@ -3782,7 +3785,7 @@
Source:
@@ -3937,7 +3940,7 @@
Source:
@@ -4048,7 +4051,7 @@ Cache is note instance scoped.
Source:
@@ -4132,7 +4135,7 @@ Cache is note instance scoped.
Source:
diff --git a/docs/frontend_api/entities_note_short.js.html b/docs/frontend_api/entities_note_short.js.html index 7e0281d3b..a61030a29 100644 --- a/docs/frontend_api/entities_note_short.js.html +++ b/docs/frontend_api/entities_note_short.js.html @@ -35,6 +35,9 @@ const RELATION = 'relation'; const RELATION_DEFINITION = 'relation-definition'; /** + * FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache. + * Attribute cache should be limited to "transaction". + * * This note's representation is used in note tree and is kept in TreeCache. */ class NoteShort { diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html index 0a422518d..6e4c07a95 100644 --- a/docs/frontend_api/global.html +++ b/docs/frontend_api/global.html @@ -303,7 +303,7 @@
Source:
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html index 74d1bb234..203bd234e 100644 --- a/docs/frontend_api/services_frontend_script_api.js.html +++ b/docs/frontend_api/services_frontend_script_api.js.html @@ -44,9 +44,12 @@ import StandardWidget from '../widgets/standard_widget.js'; * @constructor * @hideconstructor */ -function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null) { +function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null, $container = null) { const $pluginButtons = $("#plugin-buttons"); + /** @property {jQuery} container of all the rendered script content */ + this.$container = $container; + /** @property {object} note where script started executing */ this.startNote = startNote; /** @property {object} note where script is currently executing */ @@ -322,7 +325,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte * @return {boolean} returns true if the original note is still loaded, false if user switched to another */ this.isNoteStillActive = () => { - return this.originEntity.noteId === tabContext.noteId; + return tabContext.note && this.originEntity.noteId === tabContext.note.noteId; }; /** diff --git a/electron.js b/electron.js index 693532e58..ab70a1e41 100644 --- a/electron.js +++ b/electron.js @@ -10,7 +10,6 @@ const port = require('./src/services/port'); const env = require('./src/services/env'); const appIconService = require('./src/services/app_icon'); const windowStateKeeper = require('electron-window-state'); -const contextMenu = require('electron-context-menu'); // Adds debug features like hotkeys for triggering dev tools and reload require('electron-debug')(); @@ -22,25 +21,25 @@ let mainWindow; require('electron-dl')({ saveAs: true }); -contextMenu({ - menu: (actions, params, browserWindow) => [ - actions.cut(), - actions.copy(), - actions.copyLink(), - actions.paste(), - { - label: 'Search DuckDuckGo for “{selection}”', - // Only show it when right-clicking text - visible: params.selectionText.trim().length > 0, - click: () => { - const {shell} = require('electron'); - - shell.openExternal(`https://duckduckgo.com?q=${encodeURIComponent(params.selectionText)}`); - } - }, - actions.inspect() - ] -}); +// contextMenu({ +// menu: (actions, params, browserWindow) => [ +// actions.cut(), +// actions.copy(), +// actions.copyLink(), +// actions.paste(), +// { +// label: 'Search DuckDuckGo for “{selection}”', +// // Only show it when right-clicking text +// visible: params.selectionText.trim().length > 0, +// click: () => { +// const {shell} = require('electron'); +// +// shell.openExternal(`https://duckduckgo.com?q=${encodeURIComponent(params.selectionText)}`); +// } +// }, +// actions.inspect() +// ] +// }); function onClosed() { // Dereference the window diff --git a/package-lock.json b/package-lock.json index abd302069..fe2405188 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,34 @@ "integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==", "dev": true }, + "@aabuhijleh/electron-remote": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@aabuhijleh/electron-remote/-/electron-remote-1.4.0.tgz", + "integrity": "sha512-EG4ZXxqbFY4lpX55vctwz14mFrEOcOHFCMLH5z5lOl6fiviTqscy86tSlKwEE3/o3ExtdPr2tECgCogYYL7d+g==", + "requires": { + "debug": "^2.5.1", + "hashids": "^1.1.1", + "lodash.get": "^4.4.2", + "pify": "^2.3.0", + "rxjs": "^5.0.0-beta.12", + "xmlhttprequest": "^1.8.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, "@babel/code-frame": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", @@ -233,6 +261,14 @@ } } }, + "@felixrieseberg/spellchecker": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@felixrieseberg/spellchecker/-/spellchecker-4.0.10.tgz", + "integrity": "sha512-b+BlHcBXjx+W7yGNAtoVpAv8dvmAQ8Tp2YhNjqxIgocb6Wq1nKLl4jfu9DG60UWC0hTNvvQ74ny9ojiUFNqGSA==", + "requires": { + "nan": "^2.13.2" + } + }, "@jimp/bmp": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.8.4.tgz", @@ -1202,6 +1238,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, + "bcp47": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", + "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=" + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -1955,6 +1996,54 @@ } } }, + "cld": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/cld/-/cld-2.5.1.tgz", + "integrity": "sha512-DwdvvcFVizwDdPCocoPPReFk3BwLEaTZ3RzFgJ4jLzsBzJKUC3cTna0ZmAZG4tFtMmQdl0ciso3+ijkH3OPZPA==", + "requires": { + "glob": "^5.0.10", + "nan": "^2.9.2", + "rimraf": "^2.4.0", + "underscore": "^1.6.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + } + } + }, "clean-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", @@ -4139,6 +4228,45 @@ } } }, + "electron-spellchecker": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/electron-spellchecker/-/electron-spellchecker-2.2.0.tgz", + "integrity": "sha512-QkOVgjmjx6bDkqNshRTfVzEz9ctjiKVPZw77YLS0sQReP320QNtTXAKyo+01TORWk58RFT/LdxPZ/aejLdPmOA==", + "requires": { + "@aabuhijleh/electron-remote": "^1.4.0", + "@felixrieseberg/spellchecker": "^4.0.10", + "bcp47": "^1.1.2", + "cld": "^2.5.1", + "debug": "^4.1.1", + "keyboard-layout": "^2.0.16", + "lru-cache": "^5.1.1", + "mkdirp": "^0.5.1", + "pify": "^4.0.1", + "rxjs": "^5.0.1", + "rxjs-serial-subscription": "^0.1.1", + "spawn-rx": "^2.0.7" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, "electron-window-state": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz", @@ -5089,6 +5217,11 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-kit": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.5.3.tgz", + "integrity": "sha512-b7Qi1JNzY4BfAYfnIRanLk0DOD1gdkWHT4GISIn8Q2tAf3LpU8SP2CMwWaq40imYoKWbtN4ZhbSRxvsnikooZQ==" + }, "exec-buffer": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", @@ -6721,6 +6854,11 @@ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, + "hashids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/hashids/-/hashids-1.2.2.tgz", + "integrity": "sha512-dEHCG2LraR6PNvSGxosZHIRgxF5sNLOIBFEHbj8lfP9WWmu/PWPMzsip1drdVSOFi51N2pU7gZavrgn7sbGFuw==" + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -7991,6 +8129,15 @@ "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==" }, + "keyboard-layout": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.16.tgz", + "integrity": "sha512-eGrxmlV6jbm/mbPEOpYGuH53XEC7wIUj9ZxKcT2z9QHJ/RwrT9iVkvxka9zRxqHZHwQzcffgsa5OxoVAKnhK9w==", + "requires": { + "event-kit": "^2.0.0", + "nan": "^2.13.2" + } + }, "keyboardevent-from-electron-accelerator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-1.1.0.tgz", @@ -8203,8 +8350,7 @@ "lodash.assign": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" }, "lodash.camelcase": { "version": "4.3.0", @@ -8229,8 +8375,7 @@ "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "lodash.isarguments": { "version": "3.1.0", @@ -11793,11 +11938,18 @@ "version": "5.5.12", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, "requires": { "symbol-observable": "1.0.1" } }, + "rxjs-serial-subscription": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/rxjs-serial-subscription/-/rxjs-serial-subscription-0.1.1.tgz", + "integrity": "sha1-pCsdsL8QlLCSMRkeJ3jKP8+e0Uc=", + "requires": { + "rxjs": "^5.0.0-beta.12" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12293,7 +12445,6 @@ "version": "2.0.12", "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.12.tgz", "integrity": "sha512-gOPXiQQFQ9lTOLuys0iMn3jfxxv9c7zzwhbYLOEbQGvEShHVJ5sSR1oD3Daj88os7jKArDYT7rbOKdvNhe7iEg==", - "dev": true, "requires": { "debug": "^2.5.1", "lodash.assign": "^4.2.0", @@ -12304,7 +12455,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -12667,8 +12817,7 @@ "symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" }, "symbol-tree": { "version": "3.2.2", @@ -13194,8 +13343,7 @@ "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", - "dev": true + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "unescape": { "version": "1.0.1", @@ -13893,6 +14041,11 @@ "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=", "dev": true }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, "xo": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/xo/-/xo-0.25.3.tgz", diff --git a/package.json b/package.json index fa148f97b..1fc2c5d1b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "electron-debug": "3.0.1", "electron-dl": "1.14.0", "electron-find": "1.0.6", + "electron-spellchecker": "^2.2.0", "electron-window-state": "5.0.3", "express": "4.17.1", "express-session": "1.16.2", diff --git a/src/app.js b/src/app.js index 26b5f439d..b25bf7dd2 100644 --- a/src/app.js +++ b/src/app.js @@ -88,7 +88,10 @@ app.use((req, res, next) => { // error handler app.use((err, req, res, next) => { - if (err && err.message && err.message.includes("Invalid package")) { + if (err && err.message && ( + err.message.includes("Invalid package") + || (err.message.includes("Router not found for request") && err.message.includes("node_modules")) + )) { // electron 6 outputs a lot of such errors which do not seem important } else { diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index 30ef34d6b..5f33fd985 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -177,3 +177,7 @@ entrypoints.registerEntrypoints(); noteTooltipService.setupGlobalTooltip(); noteAutocompleteService.init(); + +if (utils.isElectron()) { + import("./services/spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck()); +} \ No newline at end of file diff --git a/src/public/javascripts/dialogs/options.js b/src/public/javascripts/dialogs/options.js index 2e5b991fc..e2835a35c 100644 --- a/src/public/javascripts/dialogs/options.js +++ b/src/public/javascripts/dialogs/options.js @@ -19,8 +19,7 @@ export async function showDialog() { import('./options/appearance.js'), import('./options/code_notes.js'), import('./options/change_password.js'), - import('./options/note_revisions.js'), - import('./options/protected_session.js'), + import('./options/other.js'), import('./options/sidebar.js'), import('./options/sync.js'), ])) diff --git a/src/public/javascripts/dialogs/options/note_revisions.js b/src/public/javascripts/dialogs/options/note_revisions.js deleted file mode 100644 index 17d3ff010..000000000 --- a/src/public/javascripts/dialogs/options/note_revisions.js +++ /dev/null @@ -1,20 +0,0 @@ -import server from "../../services/server.js"; -import infoService from "../../services/info.js"; - -export default class NoteRevisionsOptions { - constructor() { - this.$form = $("#note-revision-snapshot-time-interval-form"); - this.$timeInterval = $("#note-revision-snapshot-time-interval-in-seconds"); - - this.$form.submit(() => { - const opts = { 'noteRevisionSnapshotTimeInterval': this.$timeInterval.val() }; - server.put('options', opts).then(() => infoService.showMessage("Options change have been saved.")); - - return false; - }); - } - - optionsLoaded(options) { - this.$timeInterval.val(options['noteRevisionSnapshotTimeInterval']); - } -} \ No newline at end of file diff --git a/src/public/javascripts/dialogs/options/other.js b/src/public/javascripts/dialogs/options/other.js new file mode 100644 index 000000000..98eaafd0a --- /dev/null +++ b/src/public/javascripts/dialogs/options/other.js @@ -0,0 +1,54 @@ +import optionsService from "../../services/options.js"; +import server from "../../services/server.js"; +import infoService from "../../services/info.js"; + +export default class ProtectedSessionOptions { + constructor() { + this.$spellCheckEnabled = $("#spell-check-enabled"); + this.$spellCheckLanguageCode = $("#spell-check-language-code"); + + this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds"); + this.$noteRevisionsTimeInterval = $("#note-revision-snapshot-time-interval-in-seconds"); + + this.$spellCheckEnabled.change(() => { + const opts = { 'spellCheckEnabled': this.$spellCheckEnabled.is(":checked") ? "true" : "false" }; + server.put('options', opts).then(() => infoService.showMessage("Options change have been saved.")); + + return false; + }); + + this.$spellCheckLanguageCode.change(() => { + const opts = { 'spellCheckLanguageCode': this.$spellCheckLanguageCode.val() }; + server.put('options', opts).then(() => infoService.showMessage("Options change have been saved.")); + + return false; + }); + + this.$protectedSessionTimeout.change(() => { + const protectedSessionTimeout = this.$protectedSessionTimeout.val(); + + server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => { + optionsService.reloadOptions(); + + infoService.showMessage("Options change have been saved."); + }); + + return false; + }); + + this.$noteRevisionsTimeInterval.change(() => { + const opts = { 'noteRevisionSnapshotTimeInterval': this.$noteRevisionsTimeInterval.val() }; + server.put('options', opts).then(() => infoService.showMessage("Options change have been saved.")); + + return false; + }); + } + + optionsLoaded(options) { + this.$spellCheckEnabled.prop("checked", options['spellCheckEnabled'] === 'true'); + this.$spellCheckLanguageCode.val(options['spellCheckLanguageCode']); + + this.$protectedSessionTimeout.val(options['protectedSessionTimeout']); + this.$noteRevisionsTimeInterval.val(options['noteRevisionSnapshotTimeInterval']); + } +} \ No newline at end of file diff --git a/src/public/javascripts/dialogs/options/protected_session.js b/src/public/javascripts/dialogs/options/protected_session.js deleted file mode 100644 index 5db0fe126..000000000 --- a/src/public/javascripts/dialogs/options/protected_session.js +++ /dev/null @@ -1,28 +0,0 @@ -import optionsService from "../../services/options.js"; -import server from "../../services/server.js"; -import infoService from "../../services/info.js"; - -export default class ProtectedSessionOptions { - constructor() { - this.$form = $("#protected-session-timeout-form"); - this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds"); - - this.$form.submit(() => this.save()); - } - - optionsLoaded(options) { - this.$protectedSessionTimeout.val(options['protectedSessionTimeout']); - } - - save() { - const protectedSessionTimeout = this.$protectedSessionTimeout.val(); - - server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => { - optionsService.reloadOptions(); - - infoService.showMessage("Options change have been saved."); - }); - - return false; - } -} \ No newline at end of file diff --git a/src/public/javascripts/services/options.js b/src/public/javascripts/services/options.js index 11e34efbf..c67190710 100644 --- a/src/public/javascripts/services/options.js +++ b/src/public/javascripts/services/options.js @@ -51,7 +51,11 @@ function reloadOptions() { return optionsReady; } -/** just waits for some options without triggering reload */ +/** + * just waits for some options without triggering reload + * + * @return {Options} + */ async function waitForOptions() { return await optionsReady; } diff --git a/src/public/javascripts/services/spell_check.js b/src/public/javascripts/services/spell_check.js new file mode 100644 index 000000000..db1546b3d --- /dev/null +++ b/src/public/javascripts/services/spell_check.js @@ -0,0 +1,43 @@ +import optionsService from "./options.js"; + +export async function initSpellCheck() { + const options = await optionsService.waitForOptions(); + + if (!options.is('spellCheckEnabled')) { + return; + } + + const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker'); + const {remote, shell} = require('electron'); + + const spellCheckHandler = new SpellCheckHandler(); + spellCheckHandler.attachToInput(); + + spellCheckHandler.switchLanguage(options.get('spellCheckLanguageCode')); + + spellCheckHandler.currentSpellcheckerChanged.subscribe(() => { + console.debug(`Detected language is ${spellCheckHandler.currentSpellcheckerLanguage}`); + }); + + const contextMenuBuilder = new ContextMenuBuilder(spellCheckHandler, null, true, (menu, menuInfo) => { + // There's no menu.remove(id) so this is a convoluted way of removing the 'Search with Google' menu item + const oldItems = menu.items; + menu.clear(); + oldItems.forEach(oldItem => { + if (!oldItem.label.includes('Google')) { + menu.append(oldItem); + } else { + menu.append(new remote.MenuItem({ + label: 'Search with DuckDuckGo', + click: () => { + shell.openExternal(`https://duckduckgo.com/?q=${encodeURIComponent(menuInfo.selectionText)}`); + } + })); + } + }); + }); + + new ContextMenuListener(async (info) => { + await contextMenuBuilder.showPopupMenu(info); + }); +} \ No newline at end of file diff --git a/src/routes/api/options.js b/src/routes/api/options.js index 5d956cd3e..3533ae9de 100644 --- a/src/routes/api/options.js +++ b/src/routes/api/options.js @@ -32,7 +32,9 @@ const ALLOWED_OPTIONS = [ 'similarNotesWidget', 'editedNotesWidget', 'calendarWidget', - 'codeNotesMimeTypes' + 'codeNotesMimeTypes', + 'spellCheckEnabled', + 'spellCheckLanguageCode' ]; async function getOptions() { diff --git a/src/services/app_info.js b/src/services/app_info.js index ee1db9289..477fb83c3 100644 --- a/src/services/app_info.js +++ b/src/services/app_info.js @@ -4,7 +4,7 @@ const build = require('./build'); const packageJson = require('../../package'); const {TRILIUM_DATA_DIR} = require('./data_dir'); -const APP_DB_VERSION = 145; +const APP_DB_VERSION = 146; const SYNC_VERSION = 10; const CLIPPER_PROTOCOL_VERSION = "1.0"; diff --git a/src/services/options_init.js b/src/services/options_init.js index d1dcf3e78..2ebb2b9de 100644 --- a/src/services/options_init.js +++ b/src/services/options_init.js @@ -79,6 +79,9 @@ async function initNotSyncedOptions(initialized, startNotePath = 'root', opts = await optionService.createOption('similarNotesWidget', '{"enabled":true,"expanded":true,"position":600}', false); await optionService.createOption('initialized', initialized ? 'true' : 'false', false); + + await optionService.createOption('spellCheckEnabled', 'true', false); + await optionService.createOption('spellCheckLanguageCode', 'en-US', false); } module.exports = { diff --git a/src/views/dialogs/options.ejs b/src/views/dialogs/options.ejs index 17e110f9c..b551975b2 100644 --- a/src/views/dialogs/options.ejs +++ b/src/views/dialogs/options.ejs @@ -22,15 +22,12 @@ - - + @@ -41,8 +38,7 @@ <% include options/sidebar.ejs %> <% include options/code_notes.ejs %> <% include options/change_password.ejs %> - <% include options/protected_session.ejs %> - <% include options/note_revisions.ejs %> + <% include options/other.ejs %> <% include options/sync.ejs %> <% include options/advanced.ejs %> diff --git a/src/views/dialogs/options/note_revisions.ejs b/src/views/dialogs/options/note_revisions.ejs deleted file mode 100644 index 376bdd085..000000000 --- a/src/views/dialogs/options/note_revisions.ejs +++ /dev/null @@ -1,18 +0,0 @@ -
-

Note revisions snapshot interval

- -

Note revision snapshot time interval is time in seconds after which new note revision will be created for the note.

- -
-
- - -
- -
- - - -
-
-
\ No newline at end of file diff --git a/src/views/dialogs/options/other.ejs b/src/views/dialogs/options/other.ejs new file mode 100644 index 000000000..6cb0b6b93 --- /dev/null +++ b/src/views/dialogs/options/other.ejs @@ -0,0 +1,44 @@ +
+
+

Spell check

+ +

These options apply only for desktop builds, browsers will use their own native spell check.

+ +
+ + +
+ +
+ +
+ + +
+ +

Changes to the spell check options will take effect after application restart.

+
+ +
+

Protected session timeout

+ +

Protected session timeout is a time period after which the protected session is wiped out from + browser's memory. This is measured from the last interaction with protected notes. See wiki for more info.

+ +
+ + +
+
+ +
+

Note revisions snapshot interval

+ +

Note revision snapshot time interval is time in seconds after which new note revision will be created for the note. See wiki for more info.

+ +
+ + +
+
+
\ No newline at end of file diff --git a/src/views/dialogs/options/protected_session.ejs b/src/views/dialogs/options/protected_session.ejs deleted file mode 100644 index da8c212ad..000000000 --- a/src/views/dialogs/options/protected_session.ejs +++ /dev/null @@ -1,19 +0,0 @@ -
-

Protected session timeout

- -

Protected session timeout is a time period after which the protected session is wiped out from - browser's memory. This is measured from the last interaction with protected notes.

- -
-
- - -
- -
- - - -
-
-
\ No newline at end of file