shortcuts WIP

This commit is contained in:
zadam 2019-11-19 20:53:04 +01:00
parent 0ae9c8da17
commit 4bd7438fca
12 changed files with 160 additions and 63 deletions

6
package-lock.json generated
View File

@ -3112,9 +3112,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
}, },
"ejs": { "ejs": {
"version": "2.7.2", "version": "2.7.3",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.2.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.3.tgz",
"integrity": "sha512-rHGwtpl67oih3xAHbZlpw5rQAt+YV1mSCu2fUZ9XNrfaGEhom7E+AUiMci+ByP4aSfuAWx7hE0BPuJLMrpXwOw==" "integrity": "sha512-NtMNsdpaCF23gvHItgT37gzrpzckzs7KB7mg+YH1GMSG/5iZRq1BeWzAhEAJVagfM7nCQDnh/C51j/L2qjZmnA=="
}, },
"electron": { "electron": {
"version": "6.0.12", "version": "6.0.12",

View File

@ -29,7 +29,7 @@
"csurf": "1.10.0", "csurf": "1.10.0",
"dayjs": "1.8.17", "dayjs": "1.8.17",
"debug": "4.1.1", "debug": "4.1.1",
"ejs": "2.7.2", "ejs": "2.7.3",
"electron-debug": "3.0.1", "electron-debug": "3.0.1",
"electron-dl": "1.14.0", "electron-dl": "1.14.0",
"electron-find": "1.0.6", "electron-find": "1.0.6",

View File

@ -4,6 +4,7 @@ import zoomService from "./zoom.js";
import protectedSessionService from "./protected_session.js"; import protectedSessionService from "./protected_session.js";
import searchNotesService from "./search_notes.js"; import searchNotesService from "./search_notes.js";
import treeService from "./tree.js"; import treeService from "./tree.js";
import server from "./server.js";
const NOTE_REVISIONS = "../dialogs/note_revisions.js"; const NOTE_REVISIONS = "../dialogs/note_revisions.js";
const OPTIONS = "../dialogs/options.js"; const OPTIONS = "../dialogs/options.js";
@ -208,6 +209,43 @@ function registerEntrypoints() {
} }
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);
}
}
server.get('keyboard-actions').then(actions => {
for (const action of actions) {
}
});
export default { export default {
registerEntrypoints registerEntrypoints
} }

View File

@ -13,6 +13,10 @@ class Options {
return this.arr[key]; return this.arr[key];
} }
getNames() {
return Object.keys(this.arr);
}
getJson(key) { getJson(key) {
try { try {
return JSON.parse(this.arr[key]); return JSON.parse(this.arr[key]);

View File

@ -58,7 +58,7 @@ class Sidebar {
import("../widgets/note_revisions.js"), import("../widgets/note_revisions.js"),
import("../widgets/attributes.js"), import("../widgets/attributes.js"),
import("../widgets/what_links_here.js"), import("../widgets/what_links_here.js"),
import("../widgets/similar_notes.js"), import("../widgets/similar-notes.js"),
import("../widgets/edited_notes.js"), import("../widgets/edited_notes.js"),
import("../widgets/calendar.js") import("../widgets/calendar.js")
])).map(m => m.default); ])).map(m => m.default);

View File

@ -18,7 +18,7 @@ class SimilarNotesWidget extends StandardWidget {
// remember which title was when we found the similar notes // remember which title was when we found the similar notes
this.title = this.ctx.note.title; this.title = this.ctx.note.title;
const similarNotes = await server.get('similar_notes/' + this.ctx.note.noteId); const similarNotes = await server.get('similar-notes/' + this.ctx.note.noteId);
if (similarNotes.length === 0) { if (similarNotes.length === 0) {
this.$body.text("No similar notes found ..."); this.$body.text("No similar notes found ...");

11
src/routes/api/keys.js Normal file
View File

@ -0,0 +1,11 @@
"use strict";
const keyboardActions = require('../../services/keyboard_actions');
async function getKeyboardActions() {
return await keyboardActions.getKeyboardActions();
}
module.exports = {
getKeyboardActions
};

View File

@ -5,7 +5,7 @@ const log = require('../../services/log');
const attributes = require('../../services/attributes'); const attributes = require('../../services/attributes');
// options allowed to be updated directly in options dialog // options allowed to be updated directly in options dialog
const ALLOWED_OPTIONS = [ const ALLOWED_OPTIONS = new Set([
'protectedSessionTimeout', 'protectedSessionTimeout',
'noteRevisionSnapshotTimeInterval', 'noteRevisionSnapshotTimeInterval',
'zoomFactor', 'zoomFactor',
@ -37,23 +37,32 @@ const ALLOWED_OPTIONS = [
'spellCheckLanguageCode', 'spellCheckLanguageCode',
'imageMaxWidthHeight', 'imageMaxWidthHeight',
'imageJpegQuality' 'imageJpegQuality'
]; ]);
async function getOptions() { async function getOptions() {
return await optionService.getOptionsMap(ALLOWED_OPTIONS); const optionMap = await optionService.getOptionsMap();
const resultMap = {};
for (const optionName in optionMap) {
if (isAllowed(optionName)) {
resultMap[optionName] = optionMap[optionName];
}
}
return resultMap;
} }
async function updateOption(req) { async function updateOption(req) {
const {name, value} = req.params; const {name, value} = req.params;
if (!update(name, value)) { if (!await update(name, value)) {
return [400, "not allowed option to change"]; return [400, "not allowed option to change"];
} }
} }
async function updateOptions(req) { async function updateOptions(req) {
for (const optionName in req.body) { for (const optionName in req.body) {
if (!update(optionName, req.body[optionName])) { if (!await update(optionName, req.body[optionName])) {
// this should be improved // this should be improved
// it should return 400 instead of current 500, but at least it now rollbacks transaction // it should return 400 instead of current 500, but at least it now rollbacks transaction
throw new Error(`${optionName} is not allowed to change`); throw new Error(`${optionName} is not allowed to change`);
@ -62,7 +71,7 @@ async function updateOptions(req) {
} }
async function update(name, value) { async function update(name, value) {
if (!ALLOWED_OPTIONS.includes(name)) { if (!isAllowed(name)) {
return false; return false;
} }
@ -97,6 +106,10 @@ async function getUserThemes() {
return ret; return ret;
} }
function isAllowed(name) {
return ALLOWED_OPTIONS.has(name) || name.startsWith("keyboardShortcuts");
}
module.exports = { module.exports = {
getOptions, getOptions,
updateOption, updateOption,

View File

@ -33,7 +33,8 @@ const searchRoute = require('./api/search');
const dateNotesRoute = require('./api/date_notes'); const dateNotesRoute = require('./api/date_notes');
const linkMapRoute = require('./api/link_map'); const linkMapRoute = require('./api/link_map');
const clipperRoute = require('./api/clipper'); const clipperRoute = require('./api/clipper');
const similarNotesRoute = require('./api/similar_notes'); const similarNotesRoute = require('./api/similar-notes');
const keysRoute = require('./api/keys');
const log = require('../services/log'); const log = require('../services/log');
const express = require('express'); const express = require('express');
@ -242,7 +243,9 @@ function register(app) {
route(POST, '/api/clipper/notes', clipperMiddleware, clipperRoute.createNote, apiResultHandler); route(POST, '/api/clipper/notes', clipperMiddleware, clipperRoute.createNote, apiResultHandler);
route(POST, '/api/clipper/open/:noteId', clipperMiddleware, clipperRoute.openNote, apiResultHandler); route(POST, '/api/clipper/open/:noteId', clipperMiddleware, clipperRoute.openNote, apiResultHandler);
apiRoute(GET, '/api/similar_notes/:noteId', similarNotesRoute.getSimilarNotes); apiRoute(GET, '/api/similar-notes/:noteId', similarNotesRoute.getSimilarNotes);
apiRoute(GET, '/api/keyboard-actions', keysRoute.getKeyboardActions);
app.use('', router); app.use('', router);
} }

View File

@ -1,157 +1,191 @@
"use strict";
const optionService = require('./options');
const log = require('./log');
const ELECTRON = "electron"; const ELECTRON = "electron";
const KEYBOARD_ACTIONS = [ const DEFAULT_KEYBOARD_ACTIONS = [
{ {
optionName: "JumpToNote", actionName: "JumpToNote",
defaultShortcuts: ["mod+j"], defaultShortcuts: ["mod+j"],
description: 'Open "Jump to note" dialog' description: 'Open "Jump to note" dialog'
}, },
{ {
optionName: "MarkdownToHTML", actionName: "MarkdownToHTML",
defaultShortcuts: ["mod+return"] defaultShortcuts: ["mod+return"]
}, },
{ {
optionName: "NewTab", actionName: "NewTab",
defaultShortcuts: ["mod+t"], defaultShortcuts: ["mod+t"],
only: ELECTRON only: ELECTRON
}, },
{ {
optionName: "CloseTab", actionName: "CloseTab",
defaultShortcuts: ["mod+w"], defaultShortcuts: ["mod+w"],
only: ELECTRON only: ELECTRON
}, },
{ {
optionName: "NextTab", actionName: "NextTab",
defaultShortcuts: ["mod+tab"], defaultShortcuts: ["mod+tab"],
only: ELECTRON only: ELECTRON
}, },
{ {
optionName: "PreviousTab", actionName: "PreviousTab",
defaultShortcuts: ["mod+shift+tab"], defaultShortcuts: ["mod+shift+tab"],
only: ELECTRON only: ELECTRON
}, },
{ {
optionName: "CreateNoteAfter", actionName: "CreateNoteAfter",
defaultShortcuts: ["mod+o"] defaultShortcuts: ["mod+o"]
}, },
{ {
optionName: "CreateNoteInto", actionName: "CreateNoteInto",
defaultShortcuts: ["mod+p"] defaultShortcuts: ["mod+p"]
}, },
{ {
optionName: "ScrollToActiveNote", actionName: "ScrollToActiveNote",
defaultShortcuts: ["mod+."] defaultShortcuts: ["mod+."]
}, },
{ {
optionName: "CollapseTree", actionName: "CollapseTree",
defaultShortcuts: ["alt+c"] defaultShortcuts: ["alt+c"]
}, },
{ {
optionName: "RunSQL", actionName: "RunSQL",
defaultShortcuts: ["mod+return"] defaultShortcuts: ["mod+return"]
}, },
{ {
optionName: "FocusNote", actionName: "FocusNote",
defaultShortcuts: ["return"] defaultShortcuts: ["return"]
}, },
{ {
optionName: "RunCurrentNote", actionName: "RunCurrentNote",
defaultShortcuts: ["mod+return"] defaultShortcuts: ["mod+return"]
}, },
{ {
optionName: "ClipboardCopy", actionName: "ClipboardCopy",
defaultShortcuts: ["mod+c"] defaultShortcuts: ["mod+c"]
}, },
{ {
optionName: "ClipboardPaste", actionName: "ClipboardPaste",
defaultShortcuts: ["mod+v"] defaultShortcuts: ["mod+v"]
}, },
{ {
optionName: "ClipboardCut", actionName: "ClipboardCut",
defaultShortcuts: ["mod+x"] defaultShortcuts: ["mod+x"]
}, },
{ {
optionName: "SelectAllNotesInParent", actionName: "SelectAllNotesInParent",
defaultShortcuts: ["mod+a"] defaultShortcuts: ["mod+a"]
}, },
{ {
optionName: "Undo", actionName: "Undo",
defaultShortcuts: ["mod+z"] defaultShortcuts: ["mod+z"]
}, },
{ {
optionName: "Redo", actionName: "Redo",
defaultShortcuts: ["mod+y"] defaultShortcuts: ["mod+y"]
}, },
{ {
optionName: "AddLinkToText", actionName: "AddLinkToText",
defaultShortcuts: ["mod+l"] defaultShortcuts: ["mod+l"]
}, },
{ {
optionName: "CloneNotesTo", actionName: "CloneNotesTo",
defaultShortcuts: ["mod+shift+c"] defaultShortcuts: ["mod+shift+c"]
}, },
{ {
optionName: "MoveNotesTo", actionName: "MoveNotesTo",
defaultShortcuts: ["mod+shift+c"] defaultShortcuts: ["mod+shift+c"]
}, },
{ {
optionName: "SearchNotes", actionName: "SearchNotes",
defaultShortcuts: ["mod+s"] defaultShortcuts: ["mod+s"]
}, },
{ {
optionName: "ShowAttributes", actionName: "ShowAttributes",
defaultShortcuts: ["alt+a"] defaultShortcuts: ["alt+a"]
}, },
{ {
optionName: "ShowHelp", actionName: "ShowHelp",
defaultShortcuts: ["f1"] defaultShortcuts: ["f1"]
}, },
{ {
optionName: "OpenSQLConsole", actionName: "OpenSQLConsole",
defaultShortcuts: ["alt+o"] defaultShortcuts: ["alt+o"]
}, },
{ {
optionName: "BackInNoteHistory", actionName: "BackInNoteHistory",
defaultShortcuts: ["alt+left"] defaultShortcuts: ["alt+left"]
}, },
{ {
optionName: "ForwardInNoteHistory", actionName: "ForwardInNoteHistory",
defaultShortcuts: ["alt+right"] defaultShortcuts: ["alt+right"]
}, },
{ {
optionName: "ToggleZenMode", actionName: "ToggleZenMode",
defaultShortcuts: ["alt+m"] defaultShortcuts: ["alt+m"]
}, },
{ {
optionName: "InsertDateTime", actionName: "InsertDateTime",
defaultShortcuts: ["alt+t"] defaultShortcuts: ["alt+t"]
}, },
{ {
optionName: "ReloadApp", actionName: "ReloadApp",
defaultShortcuts: ["f5", "mod+r"] defaultShortcuts: ["f5", "mod+r"]
}, },
{ {
optionName: "OpenDevTools", actionName: "OpenDevTools",
defaultShortcuts: ["mod+shift+i"] defaultShortcuts: ["mod+shift+i"]
}, },
{ {
optionName: "FindInText", actionName: "FindInText",
defaultShortcuts: ["mod+f"] defaultShortcuts: ["mod+f"]
}, },
{ {
optionName: "ToggleFullscreen", actionName: "ToggleFullscreen",
defaultShortcuts: ["f11"] defaultShortcuts: ["f11"]
}, },
{ {
optionName: "ZoomOut", actionName: "ZoomOut",
defaultShortcuts: ["mod+-"] defaultShortcuts: ["mod+-"]
}, },
{ {
optionName: "ZoomIn", actionName: "ZoomIn",
defaultShortcuts: ["mod+="] defaultShortcuts: ["mod+="]
} }
]; ];
async function getKeyboardActions() {
const actions = JSON.parse(JSON.stringify(DEFAULT_KEYBOARD_ACTIONS));
for (const action of actions) {
action.effectiveShortcuts = action.defaultShortcuts.slice();
}
for (const option of await optionService.getOptions()) {
if (option.name.startsWith('keyboardShortcuts')) {
const actionName = option.name.substr(17);
const action = actions.find(ea => ea.actionName === actionName);
if (action) {
try {
action.effectiveShortcuts = JSON.parse(option.value);
}
catch (e) {
log.error(`Could not parse shortcuts for action ${actionName}`);
}
}
else {
log.info(`Keyboard action ${actionName} not found.`);
}
}
}
}
module.exports = { module.exports = {
KEYBOARD_ACTIONS DEFAULT_KEYBOARD_ACTIONS,
getKeyboardActions
}; };

View File

@ -61,18 +61,12 @@ async function createOption(name, value, isSynced) {
}).save(); }).save();
} }
async function getOptions(allowedOptions) { async function getOptions() {
let options = await require('./repository').getEntities("SELECT * FROM options ORDER BY name"); return await require('./repository').getEntities("SELECT * FROM options ORDER BY name");
if (allowedOptions) {
options = options.filter(opt => allowedOptions.includes(opt.name));
}
return options;
} }
async function getOptionsMap(allowedOptions) { async function getOptionsMap() {
const options = await getOptions(allowedOptions); const options = await getOptions();
return utils.toObject(options, opt => [opt.name, opt.value]); return utils.toObject(options, opt => [opt.name, opt.value]);
} }

View File

@ -100,7 +100,7 @@ async function initStartupOptions() {
} }
function getKeyboardDefaultOptions() { function getKeyboardDefaultOptions() {
return keyboardActions.KEYBOARD_ACTIONS.map(ka => { return keyboardActions.DEFAULT_KEYBOARD_ACTIONS.map(ka => {
return { return {
name: "keyboardShortcuts" + ka.optionName, name: "keyboardShortcuts" + ka.optionName,
value: JSON.stringify(ka.defaultShortcuts), value: JSON.stringify(ka.defaultShortcuts),