mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	shortcuts WIP
This commit is contained in:
		
							
								
								
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -3112,9 +3112,9 @@ | ||||
|       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" | ||||
|     }, | ||||
|     "ejs": { | ||||
|       "version": "2.7.2", | ||||
|       "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.2.tgz", | ||||
|       "integrity": "sha512-rHGwtpl67oih3xAHbZlpw5rQAt+YV1mSCu2fUZ9XNrfaGEhom7E+AUiMci+ByP4aSfuAWx7hE0BPuJLMrpXwOw==" | ||||
|       "version": "2.7.3", | ||||
|       "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.3.tgz", | ||||
|       "integrity": "sha512-NtMNsdpaCF23gvHItgT37gzrpzckzs7KB7mg+YH1GMSG/5iZRq1BeWzAhEAJVagfM7nCQDnh/C51j/L2qjZmnA==" | ||||
|     }, | ||||
|     "electron": { | ||||
|       "version": "6.0.12", | ||||
|   | ||||
| @@ -29,7 +29,7 @@ | ||||
|     "csurf": "1.10.0", | ||||
|     "dayjs": "1.8.17", | ||||
|     "debug": "4.1.1", | ||||
|     "ejs": "2.7.2", | ||||
|     "ejs": "2.7.3", | ||||
|     "electron-debug": "3.0.1", | ||||
|     "electron-dl": "1.14.0", | ||||
|     "electron-find": "1.0.6", | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import zoomService from "./zoom.js"; | ||||
| import protectedSessionService from "./protected_session.js"; | ||||
| import searchNotesService from "./search_notes.js"; | ||||
| import treeService from "./tree.js"; | ||||
| import server from "./server.js"; | ||||
|  | ||||
| const NOTE_REVISIONS = "../dialogs/note_revisions.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 { | ||||
|     registerEntrypoints | ||||
| } | ||||
| @@ -13,6 +13,10 @@ class Options { | ||||
|         return this.arr[key]; | ||||
|     } | ||||
|  | ||||
|     getNames() { | ||||
|         return Object.keys(this.arr); | ||||
|     } | ||||
|  | ||||
|     getJson(key) { | ||||
|         try { | ||||
|             return JSON.parse(this.arr[key]); | ||||
|   | ||||
| @@ -58,7 +58,7 @@ class Sidebar { | ||||
|             import("../widgets/note_revisions.js"), | ||||
|             import("../widgets/attributes.js"), | ||||
|             import("../widgets/what_links_here.js"), | ||||
|             import("../widgets/similar_notes.js"), | ||||
|             import("../widgets/similar-notes.js"), | ||||
|             import("../widgets/edited_notes.js"), | ||||
|             import("../widgets/calendar.js") | ||||
|         ])).map(m => m.default); | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class SimilarNotesWidget extends StandardWidget { | ||||
|         // remember which title was when we found the similar notes | ||||
|         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) { | ||||
|             this.$body.text("No similar notes found ..."); | ||||
|   | ||||
							
								
								
									
										11
									
								
								src/routes/api/keys.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/routes/api/keys.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const keyboardActions = require('../../services/keyboard_actions'); | ||||
|  | ||||
| async function getKeyboardActions() { | ||||
|     return await keyboardActions.getKeyboardActions(); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getKeyboardActions | ||||
| }; | ||||
| @@ -5,7 +5,7 @@ const log = require('../../services/log'); | ||||
| const attributes = require('../../services/attributes'); | ||||
|  | ||||
| // options allowed to be updated directly in options dialog | ||||
| const ALLOWED_OPTIONS = [ | ||||
| const ALLOWED_OPTIONS = new Set([ | ||||
|     'protectedSessionTimeout', | ||||
|     'noteRevisionSnapshotTimeInterval', | ||||
|     'zoomFactor', | ||||
| @@ -37,23 +37,32 @@ const ALLOWED_OPTIONS = [ | ||||
|     'spellCheckLanguageCode', | ||||
|     'imageMaxWidthHeight', | ||||
|     'imageJpegQuality' | ||||
| ]; | ||||
| ]); | ||||
|  | ||||
| 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) { | ||||
|     const {name, value} = req.params; | ||||
|  | ||||
|     if (!update(name, value)) { | ||||
|     if (!await update(name, value)) { | ||||
|         return [400, "not allowed option to change"]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function updateOptions(req) { | ||||
|     for (const optionName in req.body) { | ||||
|         if (!update(optionName, req.body[optionName])) { | ||||
|         if (!await update(optionName, req.body[optionName])) { | ||||
|             // this should be improved | ||||
|             // it should return 400 instead of current 500, but at least it now rollbacks transaction | ||||
|             throw new Error(`${optionName} is not allowed to change`); | ||||
| @@ -62,7 +71,7 @@ async function updateOptions(req) { | ||||
| } | ||||
|  | ||||
| async function update(name, value) { | ||||
|     if (!ALLOWED_OPTIONS.includes(name)) { | ||||
|     if (!isAllowed(name)) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| @@ -97,6 +106,10 @@ async function getUserThemes() { | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function isAllowed(name) { | ||||
|     return ALLOWED_OPTIONS.has(name) || name.startsWith("keyboardShortcuts"); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getOptions, | ||||
|     updateOption, | ||||
|   | ||||
| @@ -33,7 +33,8 @@ const searchRoute = require('./api/search'); | ||||
| const dateNotesRoute = require('./api/date_notes'); | ||||
| const linkMapRoute = require('./api/link_map'); | ||||
| 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 express = require('express'); | ||||
| @@ -242,7 +243,9 @@ function register(app) { | ||||
|     route(POST, '/api/clipper/notes', clipperMiddleware, clipperRoute.createNote, 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); | ||||
| } | ||||
|   | ||||
| @@ -1,157 +1,191 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const optionService = require('./options'); | ||||
| const log = require('./log'); | ||||
|  | ||||
| const ELECTRON = "electron"; | ||||
|  | ||||
| const KEYBOARD_ACTIONS = [ | ||||
| const DEFAULT_KEYBOARD_ACTIONS = [ | ||||
|     { | ||||
|         optionName: "JumpToNote", | ||||
|         actionName: "JumpToNote", | ||||
|         defaultShortcuts: ["mod+j"], | ||||
|         description: 'Open "Jump to note" dialog' | ||||
|     }, | ||||
|     { | ||||
|         optionName: "MarkdownToHTML", | ||||
|         actionName: "MarkdownToHTML", | ||||
|         defaultShortcuts: ["mod+return"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "NewTab", | ||||
|         actionName: "NewTab", | ||||
|         defaultShortcuts: ["mod+t"], | ||||
|         only: ELECTRON | ||||
|     }, | ||||
|     { | ||||
|         optionName: "CloseTab", | ||||
|         actionName: "CloseTab", | ||||
|         defaultShortcuts: ["mod+w"], | ||||
|         only: ELECTRON | ||||
|     }, | ||||
|     { | ||||
|         optionName: "NextTab", | ||||
|         actionName: "NextTab", | ||||
|         defaultShortcuts: ["mod+tab"], | ||||
|         only: ELECTRON | ||||
|     }, | ||||
|     { | ||||
|         optionName: "PreviousTab", | ||||
|         actionName: "PreviousTab", | ||||
|         defaultShortcuts: ["mod+shift+tab"], | ||||
|         only: ELECTRON | ||||
|     }, | ||||
|     { | ||||
|         optionName: "CreateNoteAfter", | ||||
|         actionName: "CreateNoteAfter", | ||||
|         defaultShortcuts: ["mod+o"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "CreateNoteInto", | ||||
|         actionName: "CreateNoteInto", | ||||
|         defaultShortcuts: ["mod+p"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ScrollToActiveNote", | ||||
|         actionName: "ScrollToActiveNote", | ||||
|         defaultShortcuts: ["mod+."] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "CollapseTree", | ||||
|         actionName: "CollapseTree", | ||||
|         defaultShortcuts: ["alt+c"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "RunSQL", | ||||
|         actionName: "RunSQL", | ||||
|         defaultShortcuts: ["mod+return"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "FocusNote", | ||||
|         actionName: "FocusNote", | ||||
|         defaultShortcuts: ["return"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "RunCurrentNote", | ||||
|         actionName: "RunCurrentNote", | ||||
|         defaultShortcuts: ["mod+return"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ClipboardCopy", | ||||
|         actionName: "ClipboardCopy", | ||||
|         defaultShortcuts: ["mod+c"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ClipboardPaste", | ||||
|         actionName: "ClipboardPaste", | ||||
|         defaultShortcuts: ["mod+v"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ClipboardCut", | ||||
|         actionName: "ClipboardCut", | ||||
|         defaultShortcuts: ["mod+x"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "SelectAllNotesInParent", | ||||
|         actionName: "SelectAllNotesInParent", | ||||
|         defaultShortcuts: ["mod+a"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "Undo", | ||||
|         actionName: "Undo", | ||||
|         defaultShortcuts: ["mod+z"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "Redo", | ||||
|         actionName: "Redo", | ||||
|         defaultShortcuts: ["mod+y"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "AddLinkToText", | ||||
|         actionName: "AddLinkToText", | ||||
|         defaultShortcuts: ["mod+l"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "CloneNotesTo", | ||||
|         actionName: "CloneNotesTo", | ||||
|         defaultShortcuts: ["mod+shift+c"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "MoveNotesTo", | ||||
|         actionName: "MoveNotesTo", | ||||
|         defaultShortcuts: ["mod+shift+c"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "SearchNotes", | ||||
|         actionName: "SearchNotes", | ||||
|         defaultShortcuts: ["mod+s"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ShowAttributes", | ||||
|         actionName: "ShowAttributes", | ||||
|         defaultShortcuts: ["alt+a"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ShowHelp", | ||||
|         actionName: "ShowHelp", | ||||
|         defaultShortcuts: ["f1"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "OpenSQLConsole", | ||||
|         actionName: "OpenSQLConsole", | ||||
|         defaultShortcuts: ["alt+o"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "BackInNoteHistory", | ||||
|         actionName: "BackInNoteHistory", | ||||
|         defaultShortcuts: ["alt+left"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ForwardInNoteHistory", | ||||
|         actionName: "ForwardInNoteHistory", | ||||
|         defaultShortcuts: ["alt+right"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ToggleZenMode", | ||||
|         actionName: "ToggleZenMode", | ||||
|         defaultShortcuts: ["alt+m"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "InsertDateTime", | ||||
|         actionName: "InsertDateTime", | ||||
|         defaultShortcuts: ["alt+t"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ReloadApp", | ||||
|         actionName: "ReloadApp", | ||||
|         defaultShortcuts: ["f5", "mod+r"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "OpenDevTools", | ||||
|         actionName: "OpenDevTools", | ||||
|         defaultShortcuts: ["mod+shift+i"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "FindInText", | ||||
|         actionName: "FindInText", | ||||
|         defaultShortcuts: ["mod+f"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ToggleFullscreen", | ||||
|         actionName: "ToggleFullscreen", | ||||
|         defaultShortcuts: ["f11"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ZoomOut", | ||||
|         actionName: "ZoomOut", | ||||
|         defaultShortcuts: ["mod+-"] | ||||
|     }, | ||||
|     { | ||||
|         optionName: "ZoomIn", | ||||
|         actionName: "ZoomIn", | ||||
|         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 = { | ||||
|     KEYBOARD_ACTIONS | ||||
|     DEFAULT_KEYBOARD_ACTIONS, | ||||
|     getKeyboardActions | ||||
| }; | ||||
| @@ -61,18 +61,12 @@ async function createOption(name, value, isSynced) { | ||||
|     }).save(); | ||||
| } | ||||
|  | ||||
| async function getOptions(allowedOptions) { | ||||
|     let options = await require('./repository').getEntities("SELECT * FROM options ORDER BY name"); | ||||
|  | ||||
|     if (allowedOptions) { | ||||
|         options = options.filter(opt => allowedOptions.includes(opt.name)); | ||||
| async function getOptions() { | ||||
|     return await require('./repository').getEntities("SELECT * FROM options ORDER BY name"); | ||||
| } | ||||
|  | ||||
|     return options; | ||||
| } | ||||
|  | ||||
| async function getOptionsMap(allowedOptions) { | ||||
|     const options = await getOptions(allowedOptions); | ||||
| async function getOptionsMap() { | ||||
|     const options = await getOptions(); | ||||
|  | ||||
|     return utils.toObject(options, opt => [opt.name, opt.value]); | ||||
| } | ||||
|   | ||||
| @@ -100,7 +100,7 @@ async function initStartupOptions() { | ||||
| } | ||||
|  | ||||
| function getKeyboardDefaultOptions() { | ||||
|     return keyboardActions.KEYBOARD_ACTIONS.map(ka => { | ||||
|     return keyboardActions.DEFAULT_KEYBOARD_ACTIONS.map(ka => { | ||||
|         return { | ||||
|             name: "keyboardShortcuts" + ka.optionName, | ||||
|             value: JSON.stringify(ka.defaultShortcuts), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user