mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
tree keyboard shortcuts
This commit is contained in:
parent
0e5028acd3
commit
465c3b87a7
@ -52,13 +52,7 @@ function setActionHandler(actionName, handler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function triggerAction(actionName) {
|
async function triggerAction(actionName) {
|
||||||
await keyboardActionsLoaded;
|
const action = getAction(actionName);
|
||||||
|
|
||||||
const action = keyboardActionRepo[actionName];
|
|
||||||
|
|
||||||
if (!action) {
|
|
||||||
throw new Error(`Cannot find action ${actionName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!action.handler) {
|
if (!action.handler) {
|
||||||
throw new Error(`Action ${actionName} has no handler`);
|
throw new Error(`Action ${actionName} has no handler`);
|
||||||
@ -67,7 +61,20 @@ async function triggerAction(actionName) {
|
|||||||
await action.handler();
|
await action.handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getAction(actionName) {
|
||||||
|
await keyboardActionsLoaded;
|
||||||
|
|
||||||
|
const action = keyboardActionRepo[actionName];
|
||||||
|
|
||||||
|
if (!action) {
|
||||||
|
throw new Error(`Cannot find action ${actionName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setActionHandler,
|
setActionHandler,
|
||||||
triggerAction
|
triggerAction,
|
||||||
|
getAction
|
||||||
};
|
};
|
@ -9,9 +9,7 @@ import server from './server.js';
|
|||||||
import treeCache from './tree_cache.js';
|
import treeCache from './tree_cache.js';
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import treeBuilder from "./tree_builder.js";
|
import treeBuilder from "./tree_builder.js";
|
||||||
import treeKeyBindings from "./tree_keybindings.js";
|
import treeKeyBindingService from "./tree_keybindings.js";
|
||||||
import Branch from '../entities/branch.js';
|
|
||||||
import NoteShort from '../entities/note_short.js';
|
|
||||||
import hoistedNoteService from '../services/hoisted_note.js';
|
import hoistedNoteService from '../services/hoisted_note.js';
|
||||||
import optionsService from "../services/options.js";
|
import optionsService from "../services/options.js";
|
||||||
import TreeContextMenu from "./tree_context_menu.js";
|
import TreeContextMenu from "./tree_context_menu.js";
|
||||||
@ -430,7 +428,7 @@ async function treeInitialized() {
|
|||||||
setFrontendAsLoaded();
|
setFrontendAsLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFancyTree(tree) {
|
async function initFancyTree(tree) {
|
||||||
utils.assertArguments(tree);
|
utils.assertArguments(tree);
|
||||||
|
|
||||||
$tree.fancytree({
|
$tree.fancytree({
|
||||||
@ -473,7 +471,7 @@ function initFancyTree(tree) {
|
|||||||
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
|
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
|
||||||
init: (event, data) => treeInitialized(), // don't collapse to short form
|
init: (event, data) => treeInitialized(), // don't collapse to short form
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
keydown: treeKeyBindings
|
keydown: await treeKeyBindingService.getKeyboardBindings()
|
||||||
},
|
},
|
||||||
dnd5: dragAndDropSetup,
|
dnd5: dragAndDropSetup,
|
||||||
lazyLoad: function(event, data) {
|
lazyLoad: function(event, data) {
|
||||||
@ -754,7 +752,7 @@ async function sortAlphabetically(noteId) {
|
|||||||
async function showTree() {
|
async function showTree() {
|
||||||
const tree = await loadTree();
|
const tree = await loadTree();
|
||||||
|
|
||||||
initFancyTree(tree);
|
await initFancyTree(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.subscribeToMessages(message => {
|
ws.subscribeToMessages(message => {
|
||||||
|
@ -5,148 +5,9 @@ import hoistedNoteService from "./hoisted_note.js";
|
|||||||
import clipboard from "./clipboard.js";
|
import clipboard from "./clipboard.js";
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
import searchNoteService from "./search_notes.js";
|
import searchNoteService from "./search_notes.js";
|
||||||
|
import keyboardActionService from "./keyboard_actions.js";
|
||||||
|
|
||||||
const keyBindings = {
|
const fixedKeyBindings = {
|
||||||
"del": node => {
|
|
||||||
treeChangesService.deleteNodes(treeService.getSelectedOrActiveNodes(node));
|
|
||||||
},
|
|
||||||
"ctrl+up": node => {
|
|
||||||
const beforeNode = node.getPrevSibling();
|
|
||||||
|
|
||||||
if (beforeNode !== null) {
|
|
||||||
treeChangesService.moveBeforeNode([node], beforeNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+down": node => {
|
|
||||||
const afterNode = node.getNextSibling();
|
|
||||||
if (afterNode !== null) {
|
|
||||||
treeChangesService.moveAfterNode([node], afterNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+left": node => {
|
|
||||||
treeChangesService.moveNodeUpInHierarchy(node);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+right": node => {
|
|
||||||
const toNode = node.getPrevSibling();
|
|
||||||
|
|
||||||
if (toNode !== null) {
|
|
||||||
treeChangesService.moveToNode([node], toNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"shift+up": () => {
|
|
||||||
const node = treeService.getFocusedNode();
|
|
||||||
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.isActive()) {
|
|
||||||
node.setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.navigate($.ui.keyCode.UP, false).then(() => {
|
|
||||||
const currentNode = treeService.getFocusedNode();
|
|
||||||
|
|
||||||
if (currentNode.isSelected()) {
|
|
||||||
node.setSelected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentNode.setSelected(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"shift+down": () => {
|
|
||||||
const node = treeService.getFocusedNode();
|
|
||||||
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.isActive()) {
|
|
||||||
node.setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.navigate($.ui.keyCode.DOWN, false).then(() => {
|
|
||||||
const currentNode = treeService.getFocusedNode();
|
|
||||||
|
|
||||||
if (currentNode.isSelected()) {
|
|
||||||
node.setSelected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentNode.setSelected(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"f2": async node => {
|
|
||||||
const editBranchPrefixDialog = await import("../dialogs/branch_prefix.js");
|
|
||||||
editBranchPrefixDialog.showDialog(node);
|
|
||||||
},
|
|
||||||
"alt+-": node => {
|
|
||||||
treeService.collapseTree(node);
|
|
||||||
},
|
|
||||||
"alt+s": node => {
|
|
||||||
treeService.sortAlphabetically(node.data.noteId);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+a": node => {
|
|
||||||
for (const child of node.getParent().getChildren()) {
|
|
||||||
child.setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+c": node => {
|
|
||||||
clipboard.copy(treeService.getSelectedOrActiveNodes(node));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+x": node => {
|
|
||||||
clipboard.cut(treeService.getSelectedOrActiveNodes(node));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ctrl+v": node => {
|
|
||||||
clipboard.pasteInto(node);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"return": node => {
|
|
||||||
noteDetailService.focusOnTitle();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"backspace": async node => {
|
|
||||||
if (!await hoistedNoteService.isRootNode(node)) {
|
|
||||||
node.getParent().setActive().then(treeService.clearSelectedNodes);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ctrl+h": node => {
|
|
||||||
hoistedNoteService.getHoistedNoteId().then(async hoistedNoteId => {
|
|
||||||
if (node.data.noteId === hoistedNoteId) {
|
|
||||||
hoistedNoteService.unhoist();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const note = await treeCache.getNote(node.data.noteId);
|
|
||||||
|
|
||||||
if (note.type !== 'search') {
|
|
||||||
hoistedNoteService.setHoistedNoteId(node.data.noteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
|
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
|
||||||
// after opening context menu, standard shortcuts don't work, but they are detected here
|
// after opening context menu, standard shortcuts don't work, but they are detected here
|
||||||
// so we essentially takeover the standard handling with our implementation.
|
// so we essentially takeover the standard handling with our implementation.
|
||||||
@ -169,12 +30,173 @@ const keyBindings = {
|
|||||||
node.navigate($.ui.keyCode.DOWN, true).then(treeService.clearSelectedNodes);
|
node.navigate($.ui.keyCode.DOWN, true).then(treeService.clearSelectedNodes);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const templates = {
|
||||||
|
"DeleteNote": node => {
|
||||||
|
treeChangesService.deleteNodes(treeService.getSelectedOrActiveNodes(node));
|
||||||
},
|
},
|
||||||
"ctrl+shift+s": node => {
|
"MoveNoteUp": node => {
|
||||||
|
const beforeNode = node.getPrevSibling();
|
||||||
|
|
||||||
|
if (beforeNode !== null) {
|
||||||
|
treeChangesService.moveBeforeNode([node], beforeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"MoveNoteDown": node => {
|
||||||
|
const afterNode = node.getNextSibling();
|
||||||
|
if (afterNode !== null) {
|
||||||
|
treeChangesService.moveAfterNode([node], afterNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"MoveNoteUpInHierarchy": node => {
|
||||||
|
treeChangesService.moveNodeUpInHierarchy(node);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"MoveNoteDownInHierarchy": node => {
|
||||||
|
const toNode = node.getPrevSibling();
|
||||||
|
|
||||||
|
if (toNode !== null) {
|
||||||
|
treeChangesService.moveToNode([node], toNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"AddNoteAboveToSelection": () => {
|
||||||
|
const node = treeService.getFocusedNode();
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.isActive()) {
|
||||||
|
node.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.navigate($.ui.keyCode.UP, false).then(() => {
|
||||||
|
const currentNode = treeService.getFocusedNode();
|
||||||
|
|
||||||
|
if (currentNode.isSelected()) {
|
||||||
|
node.setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode.setSelected(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"AddNoteBelowToSelection": () => {
|
||||||
|
const node = treeService.getFocusedNode();
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.isActive()) {
|
||||||
|
node.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.navigate($.ui.keyCode.DOWN, false).then(() => {
|
||||||
|
const currentNode = treeService.getFocusedNode();
|
||||||
|
|
||||||
|
if (currentNode.isSelected()) {
|
||||||
|
node.setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode.setSelected(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
// TODO: this shouldn't be tree specific shortcut
|
||||||
|
"EditBranchPrefix": async node => {
|
||||||
|
const editBranchPrefixDialog = await import("../dialogs/branch_prefix.js");
|
||||||
|
editBranchPrefixDialog.showDialog(node);
|
||||||
|
},
|
||||||
|
"CollapseSubtree": node => {
|
||||||
|
treeService.collapseTree(node);
|
||||||
|
},
|
||||||
|
"SortChildNotes": node => {
|
||||||
|
treeService.sortAlphabetically(node.data.noteId);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"SelectAllNotesInParent": node => {
|
||||||
|
for (const child of node.getParent().getChildren()) {
|
||||||
|
child.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"CopyNotesToClipboard": node => {
|
||||||
|
clipboard.copy(treeService.getSelectedOrActiveNodes(node));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"CutNotesToClipboard": node => {
|
||||||
|
clipboard.cut(treeService.getSelectedOrActiveNodes(node));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"PasteNotesFromClipboard": node => {
|
||||||
|
clipboard.pasteInto(node);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"EditNoteTitle": node => {
|
||||||
|
noteDetailService.focusOnTitle();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"ActivateParentNote": async node => {
|
||||||
|
if (!await hoistedNoteService.isRootNode(node)) {
|
||||||
|
node.getParent().setActive().then(treeService.clearSelectedNodes);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO: this should probably be app-global shortcut
|
||||||
|
"ToggleNoteHoisting": node => {
|
||||||
|
hoistedNoteService.getHoistedNoteId().then(async hoistedNoteId => {
|
||||||
|
if (node.data.noteId === hoistedNoteId) {
|
||||||
|
hoistedNoteService.unhoist();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const note = await treeCache.getNote(node.data.noteId);
|
||||||
|
|
||||||
|
if (note.type !== 'search') {
|
||||||
|
hoistedNoteService.setHoistedNoteId(node.data.noteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"SearchInSubtree": node => {
|
||||||
searchNoteService.searchInSubtree(node.data.noteId);
|
searchNoteService.searchInSubtree(node.data.noteId);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default keyBindings;
|
async function getKeyboardBindings() {
|
||||||
|
const bindings = Object.assign({}, fixedKeyBindings);
|
||||||
|
|
||||||
|
for (const actionName in templates) {
|
||||||
|
const action = await keyboardActionService.getAction(actionName);
|
||||||
|
|
||||||
|
for (const shortcut of action.effectiveShortcuts || []) {
|
||||||
|
bindings[shortcut] = templates[actionName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getKeyboardBindings
|
||||||
|
};
|
@ -114,19 +114,89 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
|||||||
defaultShortcuts: ["CommandOrControl+return"]
|
defaultShortcuts: ["CommandOrControl+return"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "ClipboardCopy",
|
actionName: "DeleteNote",
|
||||||
|
defaultShortcuts: ["Delete"],
|
||||||
|
description: "Delete note"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "MoveNoteUp",
|
||||||
|
defaultShortcuts: ["Ctrl+Up"],
|
||||||
|
description: "Move note up"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "MoveNoteDown",
|
||||||
|
defaultShortcuts: ["Ctrl+Down"],
|
||||||
|
description: "Move note down"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "MoveNoteUpInHierarchy",
|
||||||
|
defaultShortcuts: ["Ctrl+Left"],
|
||||||
|
description: "Move note up in hierarchy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "MoveNoteDownInHierarchy",
|
||||||
|
defaultShortcuts: ["Ctrl+Right"],
|
||||||
|
description: "Move note down in hierarchy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "AddNoteAboveToSelection",
|
||||||
|
defaultShortcuts: ["Shift+Up"],
|
||||||
|
description: "Add note above to the selection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "AddNoteBelowToSelection",
|
||||||
|
defaultShortcuts: ["Shift+Down"],
|
||||||
|
description: "Add note above to the selection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "CopyNotesToClipboard",
|
||||||
defaultShortcuts: ["CommandOrControl+C"],
|
defaultShortcuts: ["CommandOrControl+C"],
|
||||||
description: "Copy selected notes to the clipboard"
|
description: "Copy selected notes to the clipboard"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "ClipboardPaste",
|
actionName: "PasteNotesFromClipboard",
|
||||||
defaultShortcuts: ["CommandOrControl+V"],
|
defaultShortcuts: ["CommandOrControl+V"],
|
||||||
description: "Paste notes from the clipboard into active note"
|
description: "Paste notes from the clipboard into active note"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "ClipboardCut",
|
actionName: "CutNotesToClipboard",
|
||||||
defaultShortcuts: ["CommandOrControl+X"],
|
defaultShortcuts: ["CommandOrControl+X"],
|
||||||
description: "Copy selected notes to the clipboard"
|
description: "Cut selected notes to the clipboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "EditBranchPrefix",
|
||||||
|
defaultShortcuts: ["F2"],
|
||||||
|
description: "Show Edit branch prefix dialog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "CollapseSubtree",
|
||||||
|
defaultShortcuts: ["Alt+-"],
|
||||||
|
description: "Collapses subtree of current note"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "SortChildNotes",
|
||||||
|
defaultShortcuts: ["Alt+s"],
|
||||||
|
description: "Sort child notes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "ActivateParentNote",
|
||||||
|
defaultShortcuts: ["Backspace"],
|
||||||
|
description: "Activates parent note of currently active note"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "ToggleNoteHoisting",
|
||||||
|
defaultShortcuts: ["Alt+h"],
|
||||||
|
description: "Toggles note hoisting of active note"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "SearchInSubtree",
|
||||||
|
defaultShortcuts: ["CommandOrControl+Shift+S"],
|
||||||
|
description: "Search for notes in the active note's subtree"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: "EditNoteTitle",
|
||||||
|
defaultShortcuts: ["return"],
|
||||||
|
description: "Edit active note title"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "SelectAllNotesInParent",
|
actionName: "SelectAllNotesInParent",
|
||||||
@ -136,16 +206,6 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
|||||||
{
|
{
|
||||||
separator: "Text note operations"
|
separator: "Text note operations"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
actionName: "Undo",
|
|
||||||
defaultShortcuts: ["CommandOrControl+Z"],
|
|
||||||
description: "Undo last text operation (applicable on MacOS only)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
actionName: "Redo",
|
|
||||||
defaultShortcuts: ["CommandOrControl+Y"],
|
|
||||||
description: "Undo last text operation (applicable on MacOS only)"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
actionName: "AddLinkToText",
|
actionName: "AddLinkToText",
|
||||||
defaultShortcuts: ["CommandOrControl+L"],
|
defaultShortcuts: ["CommandOrControl+L"],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user