Merge remote-tracking branch 'origin/next58' into next58

This commit is contained in:
zadam 2022-12-02 15:27:30 +01:00
commit 0d50cdb0f5
86 changed files with 630 additions and 518 deletions

View File

@ -1,24 +1,26 @@
import froca from "./froca.js"; import froca from "../services/froca.js";
import bundleService from "./bundle.js"; import bundleService from "../services/bundle.js";
import RootCommandExecutor from "./root_command_executor.js"; import RootCommandExecutor from "./root_command_executor.js";
import Entrypoints from "./entrypoints.js"; import Entrypoints from "./entrypoints.js";
import options from "./options.js"; import options from "../services/options.js";
import utils from "./utils.js"; import utils from "../services/utils.js";
import zoomService from "./zoom.js"; import zoomComponent from "./zoom.js";
import TabManager from "./tab_manager.js"; import TabManager from "./tab_manager.js";
import treeService from "./tree.js"; import treeService from "../services/tree.js";
import Component from "../widgets/component.js"; import Component from "./component.js";
import keyboardActionsService from "./keyboard_actions.js"; import keyboardActionsService from "../services/keyboard_actions.js";
import MobileScreenSwitcherExecutor from "../widgets/mobile_widgets/mobile_screen_switcher.js"; import MobileScreenSwitcherExecutor from "./mobile_screen_switcher.js";
import MainTreeExecutors from "./main_tree_executors.js"; import MainTreeExecutors from "./main_tree_executors.js";
import toast from "./toast.js"; import toast from "../services/toast.js";
import ShortcutComponent from "./shortcut_component.js";
class AppContext extends Component { class AppContext extends Component {
constructor(isMainWindow) { constructor(isMainWindow) {
super(); super();
this.isMainWindow = isMainWindow; this.isMainWindow = isMainWindow;
this.executors = []; // non-widget/layout components needed for the application
this.components = [];
this.beforeUnloadListeners = []; this.beforeUnloadListeners = [];
} }
@ -27,7 +29,9 @@ class AppContext extends Component {
} }
async start() { async start() {
this.showWidgets(); this.initComponents();
this.renderWidgets();
await Promise.all([froca.initializedPromise, options.initializedPromise]); await Promise.all([froca.initializedPromise, options.initializedPromise]);
@ -36,7 +40,31 @@ class AppContext extends Component {
setTimeout(() => bundleService.executeStartupBundles(), 2000); setTimeout(() => bundleService.executeStartupBundles(), 2000);
} }
showWidgets() { initComponents() {
this.tabManager = new TabManager();
this.components = [
this.tabManager,
new RootCommandExecutor(),
new Entrypoints(),
new MainTreeExecutors(),
new ShortcutComponent()
];
if (utils.isMobile()) {
this.components.push(new MobileScreenSwitcherExecutor());
}
for (const component of this.components) {
this.child(component);
}
if (utils.isElectron()) {
this.child(zoomComponent);
}
}
renderWidgets() {
const rootWidget = this.layout.getRootWidget(this); const rootWidget = this.layout.getRootWidget(this);
const $renderedWidget = rootWidget.render(); const $renderedWidget = rootWidget.render();
@ -52,29 +80,8 @@ class AppContext extends Component {
component.triggerCommand(commandName, {$el: $(this)}); component.triggerCommand(commandName, {$el: $(this)});
}); });
this.tabManager = new TabManager();
this.executors = [
this.tabManager,
new RootCommandExecutor(),
new Entrypoints(),
new MainTreeExecutors()
];
if (utils.isMobile()) {
this.executors.push(new MobileScreenSwitcherExecutor());
}
this.child(rootWidget); this.child(rootWidget);
for (const executor of this.executors) {
this.child(executor);
}
if (utils.isElectron()) {
this.child(zoomService);
}
this.triggerEvent('initialRenderComplete'); this.triggerEvent('initialRenderComplete');
} }
@ -85,7 +92,7 @@ class AppContext extends Component {
/** @returns {Promise} */ /** @returns {Promise} */
triggerCommand(name, data = {}) { triggerCommand(name, data = {}) {
for (const executor of this.executors) { for (const executor of this.components) {
const fun = executor[name + "Command"]; const fun = executor[name + "Command"];
if (fun) { if (fun) {

View File

@ -1,13 +1,13 @@
import utils from "./utils.js"; import utils from "../services/utils.js";
import dateNoteService from "./date_notes.js"; import dateNoteService from "../services/date_notes.js";
import protectedSessionHolder from './protected_session_holder.js'; import protectedSessionHolder from '../services/protected_session_holder.js';
import server from "./server.js"; import server from "../services/server.js";
import appContext from "./app_context.js"; import appContext from "./app_context.js";
import Component from "../widgets/component.js"; import Component from "./component.js";
import toastService from "./toast.js"; import toastService from "../services/toast.js";
import ws from "./ws.js"; import ws from "../services/ws.js";
import bundleService from "./bundle.js"; import bundleService from "../services/bundle.js";
import froca from "./froca.js"; import froca from "../services/froca.js";
export default class Entrypoints extends Component { export default class Entrypoints extends Component {
constructor() { constructor() {

View File

@ -1,8 +1,8 @@
import appContext from "./app_context.js"; import appContext from "./app_context.js";
import noteCreateService from "./note_create.js"; import noteCreateService from "../services/note_create.js";
import treeService from "./tree.js"; import treeService from "../services/tree.js";
import hoistedNoteService from "./hoisted_note.js"; import hoistedNoteService from "../services/hoisted_note.js";
import Component from "../widgets/component.js"; import Component from "./component.js";
/** /**
* This class contains command executors which logically belong to the NoteTree widget, but for better user experience * This class contains command executors which logically belong to the NoteTree widget, but for better user experience

View File

@ -1,4 +1,4 @@
import Component from "../component.js"; import Component from "./component.js";
export default class MobileScreenSwitcherExecutor extends Component { export default class MobileScreenSwitcherExecutor extends Component {
setActiveScreenCommand({screen}) { setActiveScreenCommand({screen}) {

View File

@ -1,12 +1,12 @@
import protectedSessionHolder from "./protected_session_holder.js"; import protectedSessionHolder from "../services/protected_session_holder.js";
import server from "./server.js"; import server from "../services/server.js";
import utils from "./utils.js"; import utils from "../services/utils.js";
import appContext from "./app_context.js"; import appContext from "./app_context.js";
import treeService from "./tree.js"; import treeService from "../services/tree.js";
import Component from "../widgets/component.js"; import Component from "./component.js";
import froca from "./froca.js"; import froca from "../services/froca.js";
import hoistedNoteService from "./hoisted_note.js"; import hoistedNoteService from "../services/hoisted_note.js";
import options from "./options.js"; import options from "../services/options.js";
class NoteContext extends Component { class NoteContext extends Component {
/** /**

View File

@ -1,11 +1,11 @@
import Component from "../widgets/component.js"; import Component from "./component.js";
import appContext from "./app_context.js"; import appContext from "./app_context.js";
import dateNoteService from "../services/date_notes.js"; import dateNoteService from "../services/date_notes.js";
import treeService from "../services/tree.js"; import treeService from "../services/tree.js";
import openService from "./open.js"; import openService from "../services/open.js";
import protectedSessionService from "./protected_session.js"; import protectedSessionService from "../services/protected_session.js";
import options from "./options.js"; import options from "../services/options.js";
import froca from "./froca.js"; import froca from "../services/froca.js";
export default class RootCommandExecutor extends Component { export default class RootCommandExecutor extends Component {
editReadOnlyNoteCommand() { editReadOnlyNoteCommand() {
@ -72,7 +72,7 @@ export default class RootCommandExecutor extends Component {
options.toggle('leftPaneVisible'); options.toggle('leftPaneVisible');
} }
async showLaunchBarShortcutsCommand() { async showLaunchBarSubtreeCommand() {
await appContext.tabManager.openContextWithNote('lb_root', true, null, 'lb_root'); await appContext.tabManager.openContextWithNote('lb_root', true, null, 'lb_root');
} }

View File

@ -0,0 +1,40 @@
import appContext from "./app_context.js";
import shortcutService from "../services/shortcuts.js";
import server from "../services/server.js";
import Component from "./component.js";
import froca from "../services/froca.js";
export default class ShortcutComponent extends Component {
constructor() {
super();
server.get('keyboard-shortcuts-for-notes').then(shortcutAttributes => {
for (const attr of shortcutAttributes) {
this.bindNoteShortcutHandler(attr);
}
});
}
bindNoteShortcutHandler(label) {
const handler = () => appContext.tabManager.getActiveContext().setNote(label.noteId);
const namespace = label.attributeId;
if (label.isDeleted) {
shortcutService.removeGlobalShortcut(namespace);
} else {
shortcutService.bindGlobalShortcut(label.value, handler, namespace);
}
}
async entitiesReloadedEvent({loadResults}) {
for (const attr of loadResults.getAttributes()) {
if (attr.type === 'label' && attr.name === 'keyboardShortcut') {
const note = await froca.getNote(attr.noteId);
// launcher shortcuts are handled specifically
if (note && note.type !== 'launcher') {
this.bindNoteShortcutHandler(attr);
}
}
}
}
}

View File

@ -1,10 +1,10 @@
import Component from "../widgets/component.js"; import Component from "./component.js";
import SpacedUpdate from "./spaced_update.js"; import SpacedUpdate from "../services/spaced_update.js";
import server from "./server.js"; import server from "../services/server.js";
import options from "./options.js"; import options from "../services/options.js";
import froca from "./froca.js"; import froca from "../services/froca.js";
import treeService from "./tree.js"; import treeService from "../services/tree.js";
import utils from "./utils.js"; import utils from "../services/utils.js";
import NoteContext from "./note_context.js"; import NoteContext from "./note_context.js";
import appContext from "./app_context.js"; import appContext from "./app_context.js";
import Mutex from "../utils/mutex.js"; import Mutex from "../utils/mutex.js";

View File

@ -1,11 +1,11 @@
import options from "./options.js"; import options from "../services/options.js";
import Component from "../widgets/component.js"; import Component from "./component.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
const MIN_ZOOM = 0.5; const MIN_ZOOM = 0.5;
const MAX_ZOOM = 2.0; const MAX_ZOOM = 2.0;
class ZoomService extends Component { class ZoomComponent extends Component {
constructor() { constructor() {
super(); super();
@ -59,6 +59,6 @@ class ZoomService extends Component {
} }
} }
const zoomService = new ZoomService(); const zoomService = new ZoomComponent();
export default zoomService; export default zoomService;

View File

@ -1,4 +1,4 @@
import appContext from "./services/app_context.js"; import appContext from "./components/app_context.js";
import utils from './services/utils.js'; import utils from './services/utils.js';
import noteTooltipService from './services/note_tooltip.js'; import noteTooltipService from './services/note_tooltip.js';
import bundleService from "./services/bundle.js"; import bundleService from "./services/bundle.js";
@ -7,7 +7,7 @@ import macInit from './services/mac_init.js';
import contextMenu from "./menus/context_menu.js"; import contextMenu from "./menus/context_menu.js";
import DesktopLayout from "./layouts/desktop_layout.js"; import DesktopLayout from "./layouts/desktop_layout.js";
import glob from "./services/glob.js"; import glob from "./services/glob.js";
import zoomService from './services/zoom.js'; import zoomService from './components/zoom.js';
bundleService.getWidgetBundlesByParent().then(widgetBundles => { bundleService.getWidgetBundlesByParent().then(widgetBundles => {
appContext.setLayout(new DesktopLayout(widgetBundles)); appContext.setLayout(new DesktopLayout(widgetBundles));

View File

@ -0,0 +1 @@
<p>Keyboard shortcut for this launcher action can be configured in Options -> Shortcuts.</p>

View File

@ -21,7 +21,7 @@ const NOTE_TYPE_ICONS = {
"mermaid": "bx bx-selection", "mermaid": "bx bx-selection",
"canvas": "bx bx-pen", "canvas": "bx bx-pen",
"web-view": "bx bx-globe-alt", "web-view": "bx bx-globe-alt",
"shortcut": "bx bx-link", "launcher": "bx bx-link",
"doc": "bx bxs-file-doc" "doc": "bx bxs-file-doc"
}; };
@ -827,7 +827,7 @@ class NoteShort {
} }
isLaunchBarConfig() { isLaunchBarConfig() {
return this.type === 'shortcut' || this.noteId.startsWith("lb_"); return this.type === 'launcher' || this.noteId.startsWith("lb_");
} }
} }

View File

@ -72,7 +72,7 @@ import OptionsDialog from "../widgets/dialogs/options.js";
import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js"; import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js"; import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import MermaidExportButton from "../widgets/floating_buttons/mermaid_export_button.js"; import MermaidExportButton from "../widgets/floating_buttons/mermaid_export_button.js";
import ShortcutContainer from "../widgets/containers/shortcut_container.js"; import LauncherContainer from "../widgets/containers/launcher_container.js";
import NoteRevisionsButton from "../widgets/buttons/note_revisions_button.js"; import NoteRevisionsButton from "../widgets/buttons/note_revisions_button.js";
import EditableCodeButtonsWidget from "../widgets/type_widgets/editable_code_buttons.js"; import EditableCodeButtonsWidget from "../widgets/type_widgets/editable_code_buttons.js";
import ApiLogWidget from "../widgets/api_log.js"; import ApiLogWidget from "../widgets/api_log.js";
@ -104,7 +104,7 @@ export default class DesktopLayout {
.id("launcher-pane") .id("launcher-pane")
.css("width", "53px") .css("width", "53px")
.child(new GlobalMenuWidget()) .child(new GlobalMenuWidget())
.child(new ShortcutContainer()) .child(new LauncherContainer())
.child(new LeftPaneToggleWidget()) .child(new LeftPaneToggleWidget())
) )
.child(new LeftPaneContainer() .child(new LeftPaneContainer()

View File

@ -4,7 +4,7 @@ import contextMenu from "./context_menu.js";
import dialogService from "../services/dialog.js"; import dialogService from "../services/dialog.js";
import server from "../services/server.js"; import server from "../services/server.js";
export default class ShortcutContextMenu { export default class LauncherContextMenu {
/** /**
* @param {NoteTreeWidget} treeWidget * @param {NoteTreeWidget} treeWidget
* @param {FancytreeNode} node * @param {FancytreeNode} node
@ -27,38 +27,38 @@ export default class ShortcutContextMenu {
const note = await froca.getNote(this.node.data.noteId); const note = await froca.getNote(this.node.data.noteId);
const parentNoteId = this.node.getParent().data.noteId; const parentNoteId = this.node.getParent().data.noteId;
const isVisibleRoot = note.noteId === 'lb_visibleshortcuts'; const isVisibleRoot = note.noteId === 'lb_visiblelaunchers';
const isAvailableRoot = note.noteId === 'lb_availableshortcuts'; const isAvailableRoot = note.noteId === 'lb_availablelaunchers';
const isVisibleItem = parentNoteId === 'lb_visibleshortcuts'; const isVisibleItem = parentNoteId === 'lb_visiblelaunchers';
const isAvailableItem = parentNoteId === 'lb_availableshortcuts'; const isAvailableItem = parentNoteId === 'lb_availablelaunchers';
const isItem = isVisibleItem || isAvailableItem; const isItem = isVisibleItem || isAvailableItem;
const canBeDeleted = !note.noteId.startsWith("lb_"); const canBeDeleted = !note.noteId.startsWith("lb_");
const canBeReset = note.noteId.startsWith("lb_"); const canBeReset = note.noteId.startsWith("lb_");
return [ return [
(isVisibleRoot || isAvailableRoot) ? { title: 'Add note shortcut', command: 'addNoteShortcut', uiIcon: "bx bx-plus" } : null, (isVisibleRoot || isAvailableRoot) ? { title: 'Add a note launcher', command: 'addNoteLauncher', uiIcon: "bx bx-plus" } : null,
(isVisibleRoot || isAvailableRoot) ? { title: 'Add script shortcut', command: 'addScriptShortcut', uiIcon: "bx bx-plus" } : null, (isVisibleRoot || isAvailableRoot) ? { title: 'Add a script launcher', command: 'addScriptLauncher', uiIcon: "bx bx-plus" } : null,
(isVisibleRoot || isAvailableRoot) ? { title: 'Add widget shortcut', command: 'addWidgetShortcut', uiIcon: "bx bx-plus" } : null, (isVisibleRoot || isAvailableRoot) ? { title: 'Add a custom widget', command: 'addWidgetLauncher', uiIcon: "bx bx-plus" } : null,
(isVisibleRoot || isAvailableRoot) ? { title: 'Add spacer', command: 'addSpacerShortcut', uiIcon: "bx bx-plus" } : null, (isVisibleRoot || isAvailableRoot) ? { title: 'Add spacer', command: 'addSpacerLauncher', uiIcon: "bx bx-plus" } : null,
(isVisibleRoot || isAvailableRoot) ? { title: "----" } : null, (isVisibleRoot || isAvailableRoot) ? { title: "----" } : null,
{ title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash", enabled: canBeDeleted }, { title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash", enabled: canBeDeleted },
{ title: 'Reset', command: "resetShortcut", uiIcon: "bx bx-empty", enabled: canBeReset}, { title: 'Reset', command: "resetLauncher", uiIcon: "bx bx-empty", enabled: canBeReset},
{ title: "----" }, { title: "----" },
isAvailableItem ? { title: 'Move to visible shortcuts', command: "moveShortcutToVisible", uiIcon: "bx bx-show", enabled: true } : null, isAvailableItem ? { title: 'Move to visible launchers', command: "moveLauncherToVisible", uiIcon: "bx bx-show", enabled: true } : null,
isVisibleItem ? { title: 'Move to available shortcuts', command: "moveShortcutToAvailable", uiIcon: "bx bx-hide", enabled: true } : null, isVisibleItem ? { title: 'Move to available launchers', command: "moveLauncherToAvailable", uiIcon: "bx bx-hide", enabled: true } : null,
{ title: `Duplicate shortcut <kbd data-command="duplicateSubtree">`, command: "duplicateSubtree", uiIcon: "bx bx-empty", { title: `Duplicate launcher <kbd data-command="duplicateSubtree">`, command: "duplicateSubtree", uiIcon: "bx bx-empty",
enabled: isItem } enabled: isItem }
].filter(row => row !== null); ].filter(row => row !== null);
} }
async selectMenuItemHandler({command}) { async selectMenuItemHandler({command}) {
if (command === 'resetShortcut') { if (command === 'resetLauncher') {
const confirmed = await dialogService.confirm(`Do you really want to reset "${this.node.title}"? const confirmed = await dialogService.confirm(`Do you really want to reset "${this.node.title}"?
All data / settings in this shortcut (and its children) will be lost All data / settings in this note (and its children) will be lost
and the shortcut will be returned to its original location.`); and the launcher will be returned to its original location.`);
if (confirmed) { if (confirmed) {
await server.post(`special-notes/shortcuts/${this.node.data.noteId}/reset`); await server.post(`special-notes/launchers/${this.node.data.noteId}/reset`);
} }
return; return;

View File

@ -1,5 +1,5 @@
import contextMenu from "./context_menu.js"; import contextMenu from "./context_menu.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
function openContextMenu(notePath, e) { function openContextMenu(notePath, e) {
contextMenu.show({ contextMenu.show({

View File

@ -3,7 +3,7 @@ import froca from "../services/froca.js";
import clipboard from '../services/clipboard.js'; import clipboard from '../services/clipboard.js';
import noteCreateService from "../services/note_create.js"; import noteCreateService from "../services/note_create.js";
import contextMenu from "./context_menu.js"; import contextMenu from "./context_menu.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import noteTypesService from "../services/note_types.js"; import noteTypesService from "../services/note_types.js";
export default class TreeContextMenu { export default class TreeContextMenu {

View File

@ -1,4 +1,4 @@
import appContext from "./services/app_context.js"; import appContext from "./components/app_context.js";
import MobileLayout from "./layouts/mobile_layout.js"; import MobileLayout from "./layouts/mobile_layout.js";
import glob from "./services/glob.js"; import glob from "./services/glob.js";

View File

@ -4,13 +4,13 @@ import toastService from "./toast.js";
import froca from "./froca.js"; import froca from "./froca.js";
import hoistedNoteService from "./hoisted_note.js"; import hoistedNoteService from "./hoisted_note.js";
import ws from "./ws.js"; import ws from "./ws.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
async function moveBeforeBranch(branchIdsToMove, beforeBranchId) { async function moveBeforeBranch(branchIdsToMove, beforeBranchId) {
branchIdsToMove = filterRootNote(branchIdsToMove); branchIdsToMove = filterRootNote(branchIdsToMove);
branchIdsToMove = filterSearchBranches(branchIdsToMove); branchIdsToMove = filterSearchBranches(branchIdsToMove);
if (['root', 'lb_root', 'lb_availableshortcuts', 'lb_visibleshortcuts'].includes(beforeBranchId)) { if (['root', 'lb_root', 'lb_availablelaunchers', 'lb_visiblelaunchers'].includes(beforeBranchId)) {
toastService.showError('Cannot move notes here.'); toastService.showError('Cannot move notes here.');
return; return;
} }
@ -35,8 +35,8 @@ async function moveAfterBranch(branchIdsToMove, afterBranchId) {
'root', 'root',
hoistedNoteService.getHoistedNoteId(), hoistedNoteService.getHoistedNoteId(),
'lb_root', 'lb_root',
'lb_availableshortcuts', 'lb_availablelaunchers',
'lb_visibleshortcuts' 'lb_visiblelaunchers'
]; ];
if (forbiddenNoteIds.includes(afterNote.noteId)) { if (forbiddenNoteIds.includes(afterNote.noteId)) {

View File

@ -1,4 +1,4 @@
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
async function info(message) { async function info(message) {
return new Promise(res => return new Promise(res =>

View File

@ -1,5 +1,5 @@
import ws from "./ws.js"; import ws from "./ws.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
const fileModificationStatus = {}; const fileModificationStatus = {};

View File

@ -2,7 +2,7 @@ import Branch from "../entities/branch.js";
import NoteShort from "../entities/note_short.js"; import NoteShort from "../entities/note_short.js";
import Attribute from "../entities/attribute.js"; import Attribute from "../entities/attribute.js";
import server from "./server.js"; import server from "./server.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import NoteComplement from "../entities/note_complement.js"; import NoteComplement from "../entities/note_complement.js";
/** /**

View File

@ -76,7 +76,7 @@ async function processEntityChanges(entityChanges) {
noteAttributeCache.invalidate(); noteAttributeCache.invalidate();
} }
const appContext = (await import("./app_context.js")).default; const appContext = (await import("../components/app_context.js")).default;
await appContext.triggerEvent('entitiesReloaded', {loadResults}); await appContext.triggerEvent('entitiesReloaded', {loadResults});
} }
} }

View File

@ -9,10 +9,11 @@ import dateNotesService from './date_notes.js';
import searchService from './search.js'; import searchService from './search.js';
import CollapsibleWidget from '../widgets/collapsible_widget.js'; import CollapsibleWidget from '../widgets/collapsible_widget.js';
import ws from "./ws.js"; import ws from "./ws.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js"; import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
import BasicWidget from "../widgets/basic_widget.js"; import BasicWidget from "../widgets/basic_widget.js";
import SpacedUpdate from "./spaced_update.js"; import SpacedUpdate from "./spaced_update.js";
import shortcutService from "./shortcuts.js";
/** /**
* This is the main frontend API interface for scripts. It's published in the local "api" object. * This is the main frontend API interface for scripts. It's published in the local "api" object.
@ -508,8 +509,10 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
* @method * @method
* @param {string} keyboardShortcut - e.g. "ctrl+shift+a" * @param {string} keyboardShortcut - e.g. "ctrl+shift+a"
* @param {function} handler * @param {function} handler
* @param {string} [namespace] - specify namespace of the handler for the cases where call for bind may be repeated.
* If a handler with this ID exists, it's replaced by the new handler.
*/ */
this.bindGlobalShortcut = utils.bindGlobalShortcut; this.bindGlobalShortcut = shortcutService.bindGlobalShortcut;
/** /**
* Trilium runs in backend and frontend process, when something is changed on the backend from script, * Trilium runs in backend and frontend process, when something is changed on the backend from script,

View File

@ -1,5 +1,5 @@
import utils from "./utils.js"; import utils from "./utils.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import server from "./server.js"; import server from "./server.js";
import libraryLoader from "./library_loader.js"; import libraryLoader from "./library_loader.js";
import ws from "./ws.js"; import ws from "./ws.js";

View File

@ -1,4 +1,4 @@
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import treeService from "./tree.js"; import treeService from "./tree.js";
import dialogService from "./dialog.js"; import dialogService from "./dialog.js";
import froca from "./froca.js"; import froca from "./froca.js";

View File

@ -2,7 +2,7 @@ import toastService from "./toast.js";
import server from "./server.js"; import server from "./server.js";
import ws from "./ws.js"; import ws from "./ws.js";
import utils from "./utils.js"; import utils from "./utils.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
export async function uploadFiles(parentNoteId, files, options) { export async function uploadFiles(parentNoteId, files, options) {
if (files.length === 0) { if (files.length === 0) {

View File

@ -1,6 +1,6 @@
import server from "./server.js"; import server from "./server.js";
import utils from "./utils.js"; import appContext from "../components/app_context.js";
import appContext from "./app_context.js"; import shortcutService from "./shortcuts.js";
const keyboardActionRepo = {}; const keyboardActionRepo = {};
@ -31,7 +31,7 @@ async function setupActionsForElement(scope, $el, component) {
for (const action of actions) { for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) { for (const shortcut of action.effectiveShortcuts) {
utils.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId})); shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
} }
} }
} }
@ -39,37 +39,11 @@ async function setupActionsForElement(scope, $el, component) {
getActionsForScope("window").then(actions => { getActionsForScope("window").then(actions => {
for (const action of actions) { for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) { for (const shortcut of action.effectiveShortcuts) {
utils.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId})); shortcutService.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
} }
} }
}); });
server.get('keyboard-shortcuts-for-notes').then(shortcutForNotes => {
for (const shortcut in shortcutForNotes) {
utils.bindGlobalShortcut(shortcut, async () => {
appContext.tabManager.getActiveContext().setNote(shortcutForNotes[shortcut]);
});
}
});
function setElementActionHandler($el, actionName, handler) {
keyboardActionsLoaded.then(() => {
const action = keyboardActionRepo[actionName];
if (!action) {
throw new Error(`Cannot find keyboard action '${actionName}'`);
}
// not setting action.handler since this is not global
for (const shortcut of action.effectiveShortcuts) {
if (shortcut) {
utils.bindElShortcut($el, shortcut, handler);
}
}
});
}
async function getAction(actionName, silent = false) { async function getAction(actionName, silent = false) {
await keyboardActionsLoaded; await keyboardActionsLoaded;
@ -116,10 +90,8 @@ function updateDisplayedShortcuts($container) {
} }
export default { export default {
setElementActionHandler,
updateDisplayedShortcuts, updateDisplayedShortcuts,
setupActionsForElement, setupActionsForElement,
getActions, getActions,
getActionsForScope, getActionsForScope
getAction
}; };

View File

@ -1,6 +1,6 @@
import treeService from './tree.js'; import treeService from './tree.js';
import linkContextMenuService from "../menus/link_context_menu.js"; import linkContextMenuService from "../menus/link_context_menu.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import froca from "./froca.js"; import froca from "./froca.js";
import utils from "./utils.js"; import utils from "./utils.js";

View File

@ -2,15 +2,16 @@
* Mac specific initialization * Mac specific initialization
*/ */
import utils from "./utils.js"; import utils from "./utils.js";
import shortcutService from "./shortcuts.js";
function init() { function init() {
if (utils.isElectron() && utils.isMac()) { if (utils.isElectron() && utils.isMac()) {
utils.bindGlobalShortcut('meta+c', () => exec("copy")); shortcutService.bindGlobalShortcut('meta+c', () => exec("copy"));
utils.bindGlobalShortcut('meta+v', () => exec('paste')); shortcutService.bindGlobalShortcut('meta+v', () => exec('paste'));
utils.bindGlobalShortcut('meta+x', () => exec('cut')); shortcutService.bindGlobalShortcut('meta+x', () => exec('cut'));
utils.bindGlobalShortcut('meta+a', () => exec('selectAll')); shortcutService.bindGlobalShortcut('meta+a', () => exec('selectAll'));
utils.bindGlobalShortcut('meta+z', () => exec('undo')); shortcutService.bindGlobalShortcut('meta+z', () => exec('undo'));
utils.bindGlobalShortcut('meta+y', () => exec('redo')); shortcutService.bindGlobalShortcut('meta+y', () => exec('redo'));
} }
} }

View File

@ -1,5 +1,5 @@
import server from "./server.js"; import server from "./server.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import utils from './utils.js'; import utils from './utils.js';
import noteCreateService from './note_create.js'; import noteCreateService from './note_create.js';
import treeService from './tree.js'; import treeService from './tree.js';

View File

@ -1,4 +1,4 @@
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import utils from "./utils.js"; import utils from "./utils.js";
import protectedSessionHolder from "./protected_session_holder.js"; import protectedSessionHolder from "./protected_session_holder.js";
import server from "./server.js"; import server from "./server.js";

View File

@ -2,7 +2,7 @@ import server from './server.js';
import protectedSessionHolder from './protected_session_holder.js'; import protectedSessionHolder from './protected_session_holder.js';
import toastService from "./toast.js"; import toastService from "./toast.js";
import ws from "./ws.js"; import ws from "./ws.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
import froca from "./froca.js"; import froca from "./froca.js";
import utils from "./utils.js"; import utils from "./utils.js";
import options from "./options.js"; import options from "./options.js";

View File

@ -3,7 +3,7 @@ import utils from './utils.js';
const REQUEST_LOGGING_ENABLED = false; const REQUEST_LOGGING_ENABLED = false;
async function getHeaders(headers) { async function getHeaders(headers) {
const appContext = (await import('./app_context.js')).default; const appContext = (await import('../components/app_context.js')).default;
const activeNoteContext = appContext.tabManager ? appContext.tabManager.getActiveContext() : null; const activeNoteContext = appContext.tabManager ? appContext.tabManager.getActiveContext() : null;
// headers need to be lowercase because node.js automatically converts them to lower case // headers need to be lowercase because node.js automatically converts them to lower case

View File

@ -0,0 +1,57 @@
import utils from "./utils.js";
function removeGlobalShortcut(namespace) {
bindGlobalShortcut('', null, namespace);
}
function bindGlobalShortcut(keyboardShortcut, handler, namespace = null) {
bindElShortcut($(document), keyboardShortcut, handler, namespace);
}
function bindElShortcut($el, keyboardShortcut, handler, namespace = null) {
if (utils.isDesktop()) {
keyboardShortcut = normalizeShortcut(keyboardShortcut);
let eventName = 'keydown';
if (namespace) {
eventName += "." + namespace;
// if there's a namespace then we replace the existing event handler with the new one
$el.off(eventName);
}
// method can be called to remove the shortcut (e.g. when keyboardShortcut label is deleted)
if (keyboardShortcut) {
$el.bind(eventName, keyboardShortcut, e => {
handler(e);
e.preventDefault();
e.stopPropagation();
});
}
}
}
/**
* Normalize to the form expected by the jquery.hotkeys.js
*/
function normalizeShortcut(shortcut) {
if (!shortcut) {
return shortcut;
}
return shortcut
.toLowerCase()
.replace("enter", "return")
.replace("delete", "del")
.replace("ctrl+alt", "alt+ctrl")
.replace("meta+alt", "alt+meta"); // alt needs to be first;
}
export default {
bindGlobalShortcut,
bindElShortcut,
removeGlobalShortcut,
normalizeShortcut
}

View File

@ -3,7 +3,7 @@ import utils from './utils.js';
import server from './server.js'; import server from './server.js';
import froca from './froca.js'; import froca from './froca.js';
import hoistedNoteService from '../services/hoisted_note.js'; import hoistedNoteService from '../services/hoisted_note.js';
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
/** /**
* @return {string|null} * @return {string|null}

View File

@ -132,35 +132,6 @@ function randomString(len) {
return text; return text;
} }
function bindGlobalShortcut(keyboardShortcut, handler) {
bindElShortcut($(document), keyboardShortcut, handler);
}
function bindElShortcut($el, keyboardShortcut, handler) {
if (isDesktop()) {
keyboardShortcut = normalizeShortcut(keyboardShortcut);
$el.bind('keydown', keyboardShortcut, e => {
handler(e);
e.preventDefault();
e.stopPropagation();
});
}
}
/**
* Normalize to the form expected by the jquery.hotkeys.js
*/
function normalizeShortcut(shortcut) {
return shortcut
.toLowerCase()
.replace("enter", "return")
.replace("delete", "del")
.replace("ctrl+alt", "alt+ctrl")
.replace("meta+alt", "alt+meta"); // alt needs to be first;
}
function isMobile() { function isMobile() {
return window.device === "mobile" return window.device === "mobile"
// window.device is not available in setup // window.device is not available in setup
@ -387,8 +358,6 @@ export default {
formatLabel, formatLabel,
toObject, toObject,
randomString, randomString,
bindGlobalShortcut,
bindElShortcut,
isMobile, isMobile,
isDesktop, isDesktop,
setCookie, setCookie,
@ -402,7 +371,6 @@ export default {
focusSavedElement, focusSavedElement,
isHtmlEmpty, isHtmlEmpty,
clearBrowserCache, clearBrowserCache,
normalizeShortcut,
copySelectionToClipboard, copySelectionToClipboard,
dynamicRequire, dynamicRequire,
timeLimit, timeLimit,

View File

@ -3,7 +3,7 @@ import toastService from "./toast.js";
import server from "./server.js"; import server from "./server.js";
import options from "./options.js"; import options from "./options.js";
import frocaUpdater from "./froca_updater.js"; import frocaUpdater from "./froca_updater.js";
import appContext from "./app_context.js"; import appContext from "../components/app_context.js";
const messageHandlers = []; const messageHandlers = [];

View File

@ -8,6 +8,7 @@ import promotedAttributeDefinitionParser from '../../services/promoted_attribute
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import SpacedUpdate from "../../services/spaced_update.js"; import SpacedUpdate from "../../services/spaced_update.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = ` const TPL = `
<div class="attr-detail"> <div class="attr-detail">
@ -271,8 +272,8 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.$widget = $(TPL); this.$widget = $(TPL);
utils.bindElShortcut(this.$widget, 'ctrl+return', () => this.saveAndClose()); shortcutService.bindElShortcut(this.$widget, 'ctrl+return', () => this.saveAndClose());
utils.bindElShortcut(this.$widget, 'esc', () => this.cancelAndClose()); shortcutService.bindElShortcut(this.$widget, 'esc', () => this.cancelAndClose());
this.$title = this.$widget.find('.attr-detail-title'); this.$title = this.$widget.find('.attr-detail-title');

View File

@ -1,4 +1,4 @@
import Component from "./component.js"; import Component from "../components/component.js";
class BasicWidget extends Component { class BasicWidget extends Component {
constructor() { constructor() {

View File

@ -2,7 +2,7 @@ import libraryLoader from "../../services/library_loader.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import dateNoteService from "../../services/date_notes.js"; import dateNoteService from "../../services/date_notes.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import RightDropdownButtonWidget from "./right_dropdown_button.js"; import RightDropdownButtonWidget from "./right_dropdown_button.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";

View File

@ -1,5 +1,5 @@
import ButtonWidget from "./button_widget.js"; import ButtonWidget from "./button_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import attributeService from "../../services/attributes.js"; import attributeService from "../../services/attributes.js";
import protectedSessionHolder from "../../services/protected_session_holder.js"; import protectedSessionHolder from "../../services/protected_session_holder.js";

View File

@ -124,9 +124,9 @@ const TPL = `
<kbd ></kbd> <kbd ></kbd>
</li> </li>
<li class="dropdown-item" data-trigger-command="showLaunchBarShortcuts"> <li class="dropdown-item" data-trigger-command="showLaunchBarSubtree">
<span class="bx bx-sidebar"></span> <span class="bx bx-sidebar"></span>
Configure launchbar shortcuts Configure launchbar
</li> </li>
<li class="dropdown-item" data-trigger-command="showShareSubtree"> <li class="dropdown-item" data-trigger-command="showShareSubtree">

View File

@ -11,6 +11,6 @@ export default class NoteRevisionsButton extends ButtonWidget {
} }
isEnabled() { isEnabled() {
return super.isEnabled() && !['shortcut', 'doc'].includes(this.note?.type); return super.isEnabled() && !['launcher', 'doc'].includes(this.note?.type);
} }
} }

View File

@ -1,5 +1,5 @@
import ButtonWidget from "./button_widget.js"; import ButtonWidget from "./button_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
// FIXME: this widget might not be useful anymore // FIXME: this widget might not be useful anymore

View File

@ -0,0 +1,149 @@
import ButtonWidget from "../buttons/button_widget.js";
import dialogService from "../../services/dialog.js";
import appContext from "../../components/app_context.js";
import CalendarWidget from "../buttons/calendar.js";
import SpacerWidget from "../spacer.js";
import BookmarkButtons from "../bookmark_buttons.js";
import ProtectedSessionStatusWidget from "../buttons/protected_session_status.js";
import SyncStatusWidget from "../sync_status.js";
import BackInHistoryButtonWidget from "../buttons/history/history_back.js";
import ForwardInHistoryButtonWidget from "../buttons/history/history_forward.js";
import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
export default class LauncherWidget extends BasicWidget {
constructor(launcherNote) {
super();
if (launcherNote.type !== 'launcher') {
throw new Error(`Note '${this.note.noteId}' '${this.note.title}' is not a launcher even though it's in the launcher subtree`);
}
this.note = launcherNote;
this.innerWidget = null;
this.handler = null;
}
isEnabled() {
return this.innerWidget.isEnabled();
}
doRender() {
this.$widget = this.innerWidget.render();
}
async initLauncher() {
const launcherType = this.note.getLabelValue("launcherType");
if (launcherType === 'command') {
this.handler = () => this.triggerCommand(this.note.getLabelValue("command"));
this.innerWidget = new ButtonWidget()
.title(this.note.title)
.icon(this.note.getIcon())
.onClick(this.handler);
} else if (launcherType === 'note') {
// we're intentionally displaying the launcher title and icon instead of the target
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only (but major) downside is more work in setting up the typical case where you actually want to have both title and icon in sync.
this.handler = () => {
const targetNoteId = this.note.getRelationValue('targetNote');
if (!targetNoteId) {
dialogService.info("This launcher doesn't define target note.");
return;
}
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId, true)
};
this.innerWidget = new ButtonWidget()
.title(this.note.title)
.icon(this.note.getIcon())
.onClick(this.handler);
} else if (launcherType === 'script') {
this.handler = async () => {
const script = await this.note.getRelationTarget('script');
await script.executeScript();
};
this.innerWidget = new ButtonWidget()
.title(this.note.title)
.icon(this.note.getIcon())
.onClick(this.handler);
} else if (launcherType === 'customWidget') {
const widget = await this.note.getRelationTarget('widget');
if (widget) {
this.innerWidget = await widget.executeScript();
} else {
throw new Error(`Could not initiate custom widget of launcher '${this.note.noteId}' '${this.note.title}`);
}
} else if (launcherType === 'builtinWidget') {
const builtinWidget = this.note.getLabelValue("builtinWidget");
if (builtinWidget) {
if (builtinWidget === 'calendar') {
this.innerWidget = new CalendarWidget(this.note.title, this.note.getIcon());
} else if (builtinWidget === 'spacer') {
// || has to be inside since 0 is a valid value
const baseSize = parseInt(this.note.getLabelValue("baseSize") || "40");
const growthFactor = parseInt(this.note.getLabelValue("growthFactor") || "100");
this.innerWidget = new SpacerWidget(baseSize, growthFactor);
} else if (builtinWidget === 'bookmarks') {
this.innerWidget = new BookmarkButtons();
} else if (builtinWidget === 'protectedSession') {
this.innerWidget = new ProtectedSessionStatusWidget();
} else if (builtinWidget === 'syncStatus') {
this.innerWidget = new SyncStatusWidget();
} else if (builtinWidget === 'backInHistoryButton') {
this.innerWidget = new BackInHistoryButtonWidget();
} else if (builtinWidget === 'forwardInHistoryButton') {
this.innerWidget = new ForwardInHistoryButtonWidget();
} else {
throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${this.note.noteId} "${this.note.title}"`);
}
}
} else {
throw new Error(`Unrecognized launcher type '${launcherType}' for launcher '${this.note.noteId}' title ${this.note.title}`);
}
if (!this.innerWidget) {
throw new Error(`Unknown initialization error for note '${this.note.noteId}', title '${this.note.title}'`);
}
for (const label of this.note.getLabels('keyboardShortcut')) {
this.bindNoteShortcutHandler(label);
}
this.child(this.innerWidget);
}
bindNoteShortcutHandler(label) {
if (!this.handler) {
return;
}
const namespace = label.attributeId;
if (label.isDeleted) {
shortcutService.removeGlobalShortcut(namespace);
} else {
shortcutService.bindGlobalShortcut(label.value, this.handler, namespace);
}
}
entitiesReloadedEvent({loadResults}) {
for (const attr of loadResults.getAttributes()) {
if (attr.noteId === this.note.noteId && attr.type === 'label' && attr.name === 'keyboardShortcut') {
this.bindNoteShortcutHandler(attr);
}
}
}
}

View File

@ -0,0 +1,70 @@
import FlexContainer from "./flex_container.js";
import froca from "../../services/froca.js";
import appContext from "../../components/app_context.js";
import LauncherWidget from "./launcher.js";
export default class LauncherContainer extends FlexContainer {
constructor() {
super('column');
this.id('launcher-container');
this.css('height', '100%');
this.filling();
this.load();
}
async load() {
this.children = [];
const visibleLaunchersRoot = await froca.getNote('lb_visiblelaunchers', true);
if (!visibleLaunchersRoot) {
console.log("Visible launchers root note doesn't exist.");
return;
}
await Promise.allSettled(
(await visibleLaunchersRoot.getChildNotes())
.map(async launcherNote => {
try {
const launcherWidget = new LauncherWidget(launcherNote);
await launcherWidget.initLauncher();
this.child(launcherWidget);
}
catch (e) {
console.error(e.message);
}
})
);
this.$widget.empty();
this.renderChildren();
await this.handleEventInChildren('initialRenderComplete');
const activeContext = appContext.tabManager.getActiveContext();
if (activeContext) {
await this.handleEvent('setNoteContext', {
noteContext: activeContext
});
if (activeContext.notePath) {
await this.handleEvent('noteSwitched', {
noteContext: activeContext,
notePath: activeContext.notePath
});
}
}
}
entitiesReloadedEvent({loadResults}) {
if (loadResults.getNoteIds().find(noteId => froca.notes[noteId]?.isLaunchBarConfig())
|| loadResults.getBranches().find(branch => branch.parentNoteId.startsWith("lb_"))
|| loadResults.getAttributes().find(attr => froca.notes[attr.noteId]?.isLaunchBarConfig())) {
this.load();
}
}
}

View File

@ -1,156 +0,0 @@
import FlexContainer from "./flex_container.js";
import froca from "../../services/froca.js";
import ButtonWidget from "../buttons/button_widget.js";
import CalendarWidget from "../buttons/calendar.js";
import appContext from "../../services/app_context.js";
import SpacerWidget from "../spacer.js";
import BookmarkButtons from "../bookmark_buttons.js";
import ProtectedSessionStatusWidget from "../buttons/protected_session_status.js";
import SyncStatusWidget from "../sync_status.js";
import BackInHistoryButtonWidget from "../buttons/history/history_back.js";
import ForwardInHistoryButtonWidget from "../buttons/history/history_forward.js";
import dialogService from "../../services/dialog.js";
export default class ShortcutContainer extends FlexContainer {
constructor() {
super('column');
this.id('shortcut-container');
this.css('height', '100%');
this.filling();
this.load();
}
async load() {
this.children = [];
const visibleShortcutsRoot = await froca.getNote('lb_visibleshortcuts', true);
if (!visibleShortcutsRoot) {
console.log("Visible shortcuts root note doesn't exist.");
return;
}
await Promise.allSettled(
(await visibleShortcutsRoot.getChildNotes())
.map(shortcut => this.initShortcut(shortcut))
);
this.$widget.empty();
this.renderChildren();
await this.handleEventInChildren('initialRenderComplete');
const activeContext = appContext.tabManager.getActiveContext();
if (activeContext) {
await this.handleEvent('setNoteContext', {
noteContext: activeContext
});
if (activeContext.notePath) {
await this.handleEvent('noteSwitched', {
noteContext: activeContext,
notePath: activeContext.notePath
});
}
}
}
async initShortcut(shortcut) {
try {
if (shortcut.type !== 'shortcut') {
console.warn(`Note ${shortcut.noteId} is not a shortcut even though it's in shortcut subtree`);
return;
}
const shortcutType = shortcut.getLabelValue("shortcutType");
if (shortcutType === 'command') {
this.child(new ButtonWidget()
.title(shortcut.title)
.icon(shortcut.getIcon())
.command(shortcut.getLabelValue("command")));
} else if (shortcutType === 'note') {
// we're intentionally displaying the shortcut title and icon instead of the target
// e.g. you want to make shortcuts to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only (but major) downside is more work in setting up the typical case where you actually want to have both title and icon in sync.
this.child(new ButtonWidget()
.title(shortcut.title)
.icon(shortcut.getIcon())
.onClick(() => {
const targetNoteId = shortcut.getRelationValue('targetNote');
if (!targetNoteId) {
dialogService.info("This shortcut doesn't define target note.");
return;
}
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId, true)
}));
} else if (shortcutType === 'script') {
this.child(new ButtonWidget()
.title(shortcut.title)
.icon(shortcut.getIcon())
.onClick(async () => {
const script = await shortcut.getRelationTarget('script');
await script.executeScript();
}));
} else if (shortcutType === 'customWidget') {
const widget = await shortcut.getRelationTarget('widget');
if (widget) {
const res = await widget.executeScript();
this.child(res);
}
} else if (shortcutType === 'builtinWidget') {
const builtinWidget = shortcut.getLabelValue("builtinWidget");
if (builtinWidget) {
if (builtinWidget === 'calendar') {
this.child(new CalendarWidget(shortcut.title, shortcut.getIcon()));
} else if (builtinWidget === 'spacer') {
// || has to be inside since 0 is a valid value
const baseSize = parseInt(shortcut.getLabelValue("baseSize") || "40");
const growthFactor = parseInt(shortcut.getLabelValue("growthFactor") || "100");
this.child(new SpacerWidget(baseSize, growthFactor));
} else if (builtinWidget === 'bookmarks') {
this.child(new BookmarkButtons());
} else if (builtinWidget === 'protectedSession') {
this.child(new ProtectedSessionStatusWidget());
} else if (builtinWidget === 'syncStatus') {
this.child(new SyncStatusWidget());
} else if (builtinWidget === 'backInHistoryButton') {
this.child(new BackInHistoryButtonWidget());
} else if (builtinWidget === 'forwardInHistoryButton') {
this.child(new ForwardInHistoryButtonWidget());
} else {
console.warn(`Unrecognized builtin widget ${builtinWidget} for shortcut ${shortcut.noteId} "${shortcut.title}"`);
}
}
} else {
console.warn(`Unrecognized shortcut type ${shortcutType} for shortcut '${shortcut.noteId}' title ${shortcut.title}`);
}
}
catch (e) {
console.error(`Initialization of shortcut '${shortcut.noteId}' with title '${shortcut.title}' failed with error: ${e.message} ${e.stack}`);
}
}
entitiesReloadedEvent({loadResults}) {
if (loadResults.getNoteIds().find(noteId => froca.notes[noteId]?.isLaunchBarConfig())
|| loadResults.getBranches().find(branch => branch.parentNoteId.startsWith("lb_"))
|| loadResults.getAttributes().find(attr => froca.notes[attr.noteId]?.isLaunchBarConfig())) {
this.load();
}
}
}

View File

@ -1,5 +1,5 @@
import FlexContainer from "./flex_container.js"; import FlexContainer from "./flex_container.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
export default class SplitNoteContainer extends FlexContainer { export default class SplitNoteContainer extends FlexContainer {
constructor(widgetFactory) { constructor(widgetFactory) {

View File

@ -4,7 +4,7 @@ import froca from "../../services/froca.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
let branchId; let branchId;

View File

@ -4,7 +4,7 @@ import treeService from "../../services/tree.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
import branchService from "../../services/branches.js"; import branchService from "../../services/branches.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
const TPL = ` const TPL = `

View File

@ -1,7 +1,8 @@
import noteAutocompleteService from '../../services/note_autocomplete.js'; import noteAutocompleteService from '../../services/note_autocomplete.js';
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = `<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog"> const TPL = `<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document"> <div class="modal-dialog modal-lg" role="document">
@ -42,7 +43,7 @@ export default class JumpToNoteDialog extends BasicWidget {
this.$showInFullTextButton = this.$widget.find(".show-in-full-text-button"); this.$showInFullTextButton = this.$widget.find(".show-in-full-text-button");
this.$showInFullTextButton.on('click', e => this.showInFullText(e)); this.$showInFullTextButton.on('click', e => this.showInFullText(e));
utils.bindElShortcut(this.$widget, 'ctrl+return', e => this.showInFullText(e)); shortcutService.bindElShortcut(this.$widget, 'ctrl+return', e => this.showInFullText(e));
} }
async jumpToNoteEvent() { async jumpToNoteEvent() {

View File

@ -1,8 +1,9 @@
import libraryLoader from "../../services/library_loader.js"; import libraryLoader from "../../services/library_loader.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = ` const TPL = `
<div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog"> <div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -42,7 +43,7 @@ export default class MarkdownImportDialog extends BasicWidget {
this.$widget.on('shown.bs.modal', () => this.$importTextarea.trigger('focus')); this.$widget.on('shown.bs.modal', () => this.$importTextarea.trigger('focus'));
utils.bindElShortcut(this.$widget, 'ctrl+return', () => this.sendForm()); shortcutService.bindElShortcut(this.$widget, 'ctrl+return', () => this.sendForm());
} }
async convertMarkdownToHtml(text) { async convertMarkdownToHtml(text) {

View File

@ -1,7 +1,7 @@
import utils from '../../services/utils.js'; import utils from '../../services/utils.js';
import server from '../../services/server.js'; import server from '../../services/server.js';
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import libraryLoader from "../../services/library_loader.js"; import libraryLoader from "../../services/library_loader.js";
import openService from "../../services/open.js"; import openService from "../../services/open.js";
import protectedSessionHolder from "../../services/protected_session_holder.js"; import protectedSessionHolder from "../../services/protected_session_holder.js";

View File

@ -1,4 +1,4 @@
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";

View File

@ -1,6 +1,6 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import utils from "../../../services/utils.js"; import utils from "../../../services/utils.js";
import appContext from "../../../services/app_context.js"; import appContext from "../../../components/app_context.js";
import OptionsTab from "./options_tab.js"; import OptionsTab from "./options_tab.js";
const FONT_FAMILIES = [ const FONT_FAMILIES = [

View File

@ -3,7 +3,7 @@ import utils from '../../services/utils.js';
import server from '../../services/server.js'; import server from '../../services/server.js';
import treeService from "../../services/tree.js"; import treeService from "../../services/tree.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import hoistedNoteService from "../../services/hoisted_note.js"; import hoistedNoteService from "../../services/hoisted_note.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import dialogService from "../../services/dialog.js"; import dialogService from "../../services/dialog.js";

View File

@ -3,7 +3,7 @@
// for consistency // for consistency
import libraryLoader from "../services/library_loader.js"; import libraryLoader from "../services/library_loader.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected"; const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected";
const FIND_RESULT_CSS_CLASSNAME = "ck-find-result"; const FIND_RESULT_CSS_CLASSNAME = "ck-find-result";

View File

@ -1,5 +1,5 @@
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import contextMenu from "../../menus/context_menu.js"; import contextMenu from "../../menus/context_menu.js";
import noteCreateService from "../../services/note_create.js"; import noteCreateService from "../../services/note_create.js";
import branchService from "../../services/branches.js"; import branchService from "../../services/branches.js";

View File

@ -1,5 +1,5 @@
import BasicWidget from "./basic_widget.js"; import BasicWidget from "./basic_widget.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
export default class NoteContextAwareWidget extends BasicWidget { export default class NoteContextAwareWidget extends BasicWidget {
isNoteContext(ntxId) { isNoteContext(ntxId) {

View File

@ -3,7 +3,7 @@ import protectedSessionHolder from "../services/protected_session_holder.js";
import SpacedUpdate from "../services/spaced_update.js"; import SpacedUpdate from "../services/spaced_update.js";
import server from "../services/server.js"; import server from "../services/server.js";
import libraryLoader from "../services/library_loader.js"; import libraryLoader from "../services/library_loader.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import keyboardActionsService from "../services/keyboard_actions.js"; import keyboardActionsService from "../services/keyboard_actions.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService from "../services/note_create.js";
import attributeService from "../services/attributes.js"; import attributeService from "../services/attributes.js";
@ -197,7 +197,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
type = 'editable-code'; type = 'editable-code';
} }
if (type === 'shortcut') { if (type === 'launcher') {
type = 'doc'; type = 'doc';
} }

View File

@ -2,7 +2,7 @@ import libraryLoader from "../services/library_loader.js";
import server from "../services/server.js"; import server from "../services/server.js";
import attributeService from "../services/attributes.js"; import attributeService from "../services/attributes.js";
import hoistedNoteService from "../services/hoisted_note.js"; import hoistedNoteService from "../services/hoisted_note.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js"; import NoteContextAwareWidget from "./note_context_aware_widget.js";
import linkContextMenuService from "../menus/link_context_menu.js"; import linkContextMenuService from "../menus/link_context_menu.js";

View File

@ -1,10 +1,10 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js"; import NoteContextAwareWidget from "./note_context_aware_widget.js";
import utils from "../services/utils.js";
import protectedSessionHolder from "../services/protected_session_holder.js"; import protectedSessionHolder from "../services/protected_session_holder.js";
import server from "../services/server.js"; import server from "../services/server.js";
import SpacedUpdate from "../services/spaced_update.js"; import SpacedUpdate from "../services/spaced_update.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import branchService from "../services/branches.js"; import branchService from "../services/branches.js";
import shortcutService from "../services/shortcuts.js";
const TPL = ` const TPL = `
<div class="note-title-widget"> <div class="note-title-widget">
@ -58,13 +58,13 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
this.deleteNoteOnEscape = false; this.deleteNoteOnEscape = false;
}); });
utils.bindElShortcut(this.$noteTitle, 'esc', () => { shortcutService.bindElShortcut(this.$noteTitle, 'esc', () => {
if (this.deleteNoteOnEscape && this.noteContext.isActive()) { if (this.deleteNoteOnEscape && this.noteContext.isActive()) {
branchService.deleteNotes(Object.values(this.noteContext.note.parentToBranch)); branchService.deleteNotes(Object.values(this.noteContext.note.parentToBranch));
} }
}); });
utils.bindElShortcut(this.$noteTitle, 'return', () => { shortcutService.bindElShortcut(this.$noteTitle, 'return', () => {
this.triggerCommand('focusOnDetail', {ntxId: this.noteContext.ntxId}); this.triggerCommand('focusOnDetail', {ntxId: this.noteContext.ntxId});
}); });
} }
@ -73,7 +73,7 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
this.$noteTitle.val(note.title); this.$noteTitle.val(note.title);
this.$noteTitle.prop("readonly", (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) this.$noteTitle.prop("readonly", (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable())
|| ["lb_root", "lb_availableshortcuts", "lb_visibleshortcuts"].includes(note.noteId)); || ["lb_root", "lb_availablelaunchers", "lb_visiblelaunchers"].includes(note.noteId));
this.setProtectedStatus(note); this.setProtectedStatus(note);
} }

View File

@ -9,7 +9,7 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
import server from "../services/server.js"; import server from "../services/server.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService from "../services/note_create.js";
import toastService from "../services/toast.js"; import toastService from "../services/toast.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import keyboardActionsService from "../services/keyboard_actions.js"; import keyboardActionsService from "../services/keyboard_actions.js";
import clipboard from "../services/clipboard.js"; import clipboard from "../services/clipboard.js";
import protectedSessionService from "../services/protected_session.js"; import protectedSessionService from "../services/protected_session.js";
@ -18,6 +18,7 @@ import syncService from "../services/sync.js";
import options from "../services/options.js"; import options from "../services/options.js";
import protectedSessionHolder from "../services/protected_session_holder.js"; import protectedSessionHolder from "../services/protected_session_holder.js";
import dialogService from "../services/dialog.js"; import dialogService from "../services/dialog.js";
import shortcutService from "../services/shortcuts.js";
const TPL = ` const TPL = `
<div class="tree-wrapper"> <div class="tree-wrapper">
@ -396,7 +397,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
autoExpandMS: 600, autoExpandMS: 600,
preventLazyParents: false, preventLazyParents: false,
dragStart: (node, data) => { dragStart: (node, data) => {
if (['root', 'hidden', 'lb_root', 'lb_availableshortcuts', 'lb_visibleshortcuts'].includes(node.data.noteId)) { if (['root', 'hidden', 'lb_root', 'lb_availablelaunchers', 'lb_visiblelaunchers'].includes(node.data.noteId)) {
return false; return false;
} }
@ -426,7 +427,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
return false; return false;
} else if (node.data.noteId === 'lb_root') { } else if (node.data.noteId === 'lb_root') {
return false; return false;
} else if (node.data.noteType === 'shortcut') { } else if (node.data.noteType === 'launcher') {
return ['before', 'after']; return ['before', 'after'];
} else { } else {
return true; return true;
@ -563,7 +564,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
$span.append($refreshSearchButton); $span.append($refreshSearchButton);
} }
if (!['search', 'shortcut'].includes(note.type)) { if (!['search', 'launcher'].includes(note.type)) {
const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>'); const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>');
$span.append($createChildNoteButton); $span.append($createChildNoteButton);
@ -603,8 +604,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const node = $.ui.fancytree.getNode(e); const node = $.ui.fancytree.getNode(e);
if (hoistedNoteService.getHoistedNoteId() === 'lb_root') { if (hoistedNoteService.getHoistedNoteId() === 'lb_root') {
import("../menus/shortcut_context_menu.js").then(({default: ShortcutContextMenu}) => { import("../menus/launcher_context_menu.js").then(({LauncherContextMenu: ShortcutContextMenu}) => {
const shortcutContextMenu = new ShortcutContextMenu(this, node); const shortcutContextMenu = new LauncherContextMenu(this, node);
shortcutContextMenu.show(e); shortcutContextMenu.show(e);
}); });
} else { } else {
@ -1331,7 +1332,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
for (const action of actions) { for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) { for (const shortcut of action.effectiveShortcuts) {
hotKeyMap[utils.normalizeShortcut(shortcut)] = node => { hotKeyMap[shortcutService.normalizeShortcut(shortcut)] = node => {
const notePath = treeService.getNotePath(node); const notePath = treeService.getNotePath(node);
this.triggerCommand(action.actionName, {node, notePath}); this.triggerCommand(action.actionName, {node, notePath});
@ -1550,11 +1551,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
moveShortcutToVisibleCommand({node, selectedOrActiveBranchIds}) { moveShortcutToVisibleCommand({node, selectedOrActiveBranchIds}) {
branchService.moveToParentNote(selectedOrActiveBranchIds, 'lb_visibleshortcuts'); branchService.moveToParentNote(selectedOrActiveBranchIds, 'lb_visiblelaunchers');
} }
moveShortcutToAvailableCommand({node, selectedOrActiveBranchIds}) { moveShortcutToAvailableCommand({node, selectedOrActiveBranchIds}) {
branchService.moveToParentNote(selectedOrActiveBranchIds, 'lb_availableshortcuts'); branchService.moveToParentNote(selectedOrActiveBranchIds, 'lb_availablelaunchers');
} }
addNoteShortcutCommand({node}) { addNoteShortcutCommand({node}) {
@ -1573,8 +1574,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.createShortcutNote(node, 'spacer'); this.createShortcutNote(node, 'spacer');
} }
async createShortcutNote(node, shortcutType) { async createShortcutNote(node, launcherType) {
const resp = await server.post(`special-notes/shortcuts/${node.data.noteId}/${shortcutType}`); const resp = await server.post(`special-notes/shortcuts/${node.data.noteId}/${launcherType}`);
if (!resp.success) { if (!resp.success) {
alert(resp.message); alert(resp.message);

View File

@ -8,7 +8,7 @@ const NOTE_TYPES = [
{ type: "image", title: "Image", selectable: false }, { type: "image", title: "Image", selectable: false },
{ type: "search", title: "Saved Search", selectable: false }, { type: "search", title: "Saved Search", selectable: false },
{ type: "note-map", mime: '', title: "Note Map", selectable: false }, { type: "note-map", mime: '', title: "Note Map", selectable: false },
{ type: "shortcut", mime: '', title: "Shortcut", selectable: false }, { type: "launcher", mime: '', title: "Launcher", selectable: false },
{ type: "doc", mime: '', title: "Doc", selectable: false }, { type: "doc", mime: '', title: "Doc", selectable: false },
{ type: "text", mime: "text/html", title: "Text", selectable: true }, { type: "text", mime: "text/html", title: "Text", selectable: true },

View File

@ -1,10 +1,10 @@
import BasicWidget from "./basic_widget.js"; import BasicWidget from "./basic_widget.js";
import server from "../services/server.js"; import server from "../services/server.js";
import linkService from "../services/link.js"; import linkService from "../services/link.js";
import dateNotesService from "../services/date_notes.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import shortcutService from "../services/shortcuts.js";
const TPL = ` const TPL = `
<div class="quick-search input-group input-group-sm"> <div class="quick-search input-group input-group-sm">
@ -66,7 +66,7 @@ export default class QuickSearchWidget extends BasicWidget {
}) })
} }
utils.bindElShortcut(this.$searchString, 'return', () => { shortcutService.bindElShortcut(this.$searchString, 'return', () => {
if (this.$dropdownMenu.is(":visible")) { if (this.$dropdownMenu.is(":visible")) {
this.search(); // just update already visible dropdown this.search(); // just update already visible dropdown
} else { } else {
@ -76,11 +76,11 @@ export default class QuickSearchWidget extends BasicWidget {
this.$searchString.focus(); this.$searchString.focus();
}); });
utils.bindElShortcut(this.$searchString, 'down', () => { shortcutService.bindElShortcut(this.$searchString, 'down', () => {
this.$dropdownMenu.find('.dropdown-item:first').focus(); this.$dropdownMenu.find('.dropdown-item:first').focus();
}); });
utils.bindElShortcut(this.$searchString, 'esc', () => { shortcutService.bindElShortcut(this.$searchString, 'esc', () => {
this.$dropdownToggle.dropdown('hide'); this.$dropdownToggle.dropdown('hide');
}); });
@ -120,7 +120,7 @@ export default class QuickSearchWidget extends BasicWidget {
appContext.tabManager.getActiveContext().setNote(note.noteId); appContext.tabManager.getActiveContext().setNote(note.noteId);
} }
}); });
utils.bindElShortcut($link, 'return', () => { shortcutService.bindElShortcut($link, 'return', () => {
this.$dropdownToggle.dropdown("hide"); this.$dropdownToggle.dropdown("hide");
appContext.tabManager.getActiveContext().setNote(note.noteId); appContext.tabManager.getActiveContext().setNote(note.noteId);
@ -140,9 +140,9 @@ export default class QuickSearchWidget extends BasicWidget {
$showInFullButton.on('click', () => this.showInFullSearch()); $showInFullButton.on('click', () => this.showInFullSearch());
utils.bindElShortcut($showInFullButton, 'return', () => this.showInFullSearch()); shortcutService.bindElShortcut($showInFullButton, 'return', () => this.showInFullSearch());
utils.bindElShortcut(this.$dropdownMenu.find('.dropdown-item:first'), 'up', () => this.$searchString.focus()); shortcutService.bindElShortcut(this.$dropdownMenu.find('.dropdown-item:first'), 'up', () => this.$searchString.focus());
this.$dropdownToggle.dropdown('update'); this.$dropdownToggle.dropdown('update');
} }

View File

@ -13,7 +13,7 @@ import OrderBy from "../search_options/order_by.js";
import SearchScript from "../search_options/search_script.js"; import SearchScript from "../search_options/search_script.js";
import Limit from "../search_options/limit.js"; import Limit from "../search_options/limit.js";
import Debug from "../search_options/debug.js"; import Debug from "../search_options/debug.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import bulkActionService from "../../services/bulk_action.js"; import bulkActionService from "../../services/bulk_action.js";
const TPL = ` const TPL = `

View File

@ -1,6 +1,6 @@
import server from "../../services/server.js"; import server from "../../services/server.js";
import ws from "../../services/ws.js"; import ws from "../../services/ws.js";
import Component from "../component.js"; import Component from "../../components/component.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
export default class AbstractSearchOption extends Component { export default class AbstractSearchOption extends Component {

View File

@ -1,7 +1,7 @@
import AbstractSearchOption from "./abstract_search_option.js"; import AbstractSearchOption from "./abstract_search_option.js";
import utils from "../../services/utils.js";
import SpacedUpdate from "../../services/spaced_update.js"; import SpacedUpdate from "../../services/spaced_update.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = ` const TPL = `
<tr> <tr>
@ -45,7 +45,7 @@ export default class SearchString extends AbstractSearchOption {
this.$searchString = $option.find('.search-string'); this.$searchString = $option.find('.search-string');
this.$searchString.on('input', () => this.spacedUpdate.scheduleUpdate()); this.$searchString.on('input', () => this.spacedUpdate.scheduleUpdate());
utils.bindElShortcut(this.$searchString, 'return', async () => { shortcutService.bindElShortcut(this.$searchString, 'return', async () => {
// this also in effect disallows new lines in query string. // this also in effect disallows new lines in query string.
// on one hand this makes sense since search string is a label // on one hand this makes sense since search string is a label
// on the other hand it could be nice for structuring long search string. It's probably a niche case though. // on the other hand it could be nice for structuring long search string. It's probably a niche case though.

View File

@ -2,7 +2,7 @@ import BasicWidget from "./basic_widget.js";
import contextMenu from "../menus/context_menu.js"; import contextMenu from "../menus/context_menu.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import keyboardActionService from "../services/keyboard_actions.js"; import keyboardActionService from "../services/keyboard_actions.js";
import appContext from "../services/app_context.js"; import appContext from "../components/app_context.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import attributeService from "../services/attributes.js"; import attributeService from "../services/attributes.js";

View File

@ -1,5 +1,5 @@
import TypeWidget from "./type_widget.js"; import TypeWidget from "./type_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
import linkService from "../../services/link.js"; import linkService from "../../services/link.js";
import noteContentRenderer from "../../services/note_content_renderer.js"; import noteContentRenderer from "../../services/note_content_renderer.js";

View File

@ -1,6 +1,6 @@
import server from "../../services/server.js"; import server from "../../services/server.js";
import ws from "../../services/ws.js"; import ws from "../../services/ws.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import treeService from "../../services/tree.js"; import treeService from "../../services/tree.js";
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";

View File

@ -8,7 +8,7 @@ import treeService from "../../services/tree.js";
import noteCreateService from "../../services/note_create.js"; import noteCreateService from "../../services/note_create.js";
import AbstractTextTypeWidget from "./abstract_text_type_widget.js"; import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
import link from "../../services/link.js"; import link from "../../services/link.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
const ENABLE_INSPECTOR = false; const ENABLE_INSPECTOR = false;

View File

@ -1,6 +1,6 @@
import noteAutocompleteService from '../../services/note_autocomplete.js'; import noteAutocompleteService from '../../services/note_autocomplete.js';
import TypeWidget from "./type_widget.js"; import TypeWidget from "./type_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import searchService from "../../services/search.js"; import searchService from "../../services/search.js";
const TPL = ` const TPL = `

View File

@ -5,7 +5,7 @@ import contextMenu from "../../menus/context_menu.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import attributeAutocompleteService from "../../services/attribute_autocomplete.js"; import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
import TypeWidget from "./type_widget.js"; import TypeWidget from "./type_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
import dialogService from "../../services/dialog.js"; import dialogService from "../../services/dialog.js";

View File

@ -1,5 +1,5 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import appContext from "../../services/app_context.js"; import appContext from "../../components/app_context.js";
export default class TypeWidget extends NoteContextAwareWidget { export default class TypeWidget extends NoteContextAwareWidget {
// for overriding // for overriding

View File

@ -8,15 +8,10 @@ function getKeyboardActions() {
} }
function getShortcutsForNotes() { function getShortcutsForNotes() {
const attrs = becca.findAttributes('label', 'keyboardShortcut'); const labels = becca.findAttributes('label', 'keyboardShortcut');
const map = {}; // launchers have different handling
return labels.filter(attr => becca.getNote(attr.noteId)?.type !== 'launcher');
for (const attr of attrs) {
map[attr.value] = attr.noteId;
}
return map;
} }
module.exports = { module.exports = {

View File

@ -66,12 +66,12 @@ function getHoistedNote() {
return becca.getNote(cls.getHoistedNoteId()); return becca.getNote(cls.getHoistedNoteId());
} }
function createShortcut(req) { function createLauncher(req) {
return specialNotesService.createShortcut(req.params.parentNoteId, req.params.shortcutType); return specialNotesService.createLauncher(req.params.parentNoteId, req.params.launcherType);
} }
function resetShortcut(req) { function resetLauncher(req) {
return specialNotesService.resetShortcut(req.params.noteId); return specialNotesService.resetLauncher(req.params.noteId);
} }
module.exports = { module.exports = {
@ -85,6 +85,6 @@ module.exports = {
saveSqlConsole, saveSqlConsole,
createSearchNote, createSearchNote,
saveSearchNote, saveSearchNote,
createShortcut, createLauncher,
resetShortcut resetLauncher
}; };

View File

@ -326,8 +326,8 @@ function register(app) {
apiRoute(POST, '/api/special-notes/save-sql-console', specialNotesRoute.saveSqlConsole); apiRoute(POST, '/api/special-notes/save-sql-console', specialNotesRoute.saveSqlConsole);
apiRoute(POST, '/api/special-notes/search-note', specialNotesRoute.createSearchNote); apiRoute(POST, '/api/special-notes/search-note', specialNotesRoute.createSearchNote);
apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote); apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote);
apiRoute(POST, '/api/special-notes/shortcuts/:noteId/reset', specialNotesRoute.resetShortcut); apiRoute(POST, '/api/special-notes/launchers/:noteId/reset', specialNotesRoute.resetLauncher);
apiRoute(POST, '/api/special-notes/shortcuts/:parentNoteId/:shortcutType', specialNotesRoute.createShortcut); apiRoute(POST, '/api/special-notes/launchers/:parentNoteId/:launcherType', specialNotesRoute.createLauncher);
// :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename // :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); route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage);

View File

@ -11,6 +11,6 @@ module.exports = [
'mermaid', 'mermaid',
'canvas', 'canvas',
'web-view', 'web-view',
'shortcut', 'launcher',
'doc' 'doc'
]; ];

View File

@ -110,16 +110,16 @@ function getAndValidateParent(params) {
throw new Error(`Parent note "${params.parentNoteId}" not found.`); throw new Error(`Parent note "${params.parentNoteId}" not found.`);
} }
if (parentNote.type === 'shortcut') { if (parentNote.type === 'launcher') {
throw new Error(`Shortcuts should not have child notes.`); throw new Error(`Launchers should not have child notes.`);
} }
if (!params.ignoreForbiddenParents && ['lb_root'].includes(parentNote.noteId)) { if (!params.ignoreForbiddenParents && ['lb_root'].includes(parentNote.noteId)) {
throw new Error(`Creating child notes into '${parentNote.noteId}' is not allowed.`); throw new Error(`Creating child notes into '${parentNote.noteId}' is not allowed.`);
} }
if (['lb_availableshortcuts', 'lb_visibleshortcuts'].includes(parentNote.noteId) && params.type !== 'shortcut') { if (['lb_availablelaunchers', 'lb_visiblelaunchers'].includes(parentNote.noteId) && params.type !== 'launcher') {
throw new Error(`Creating child notes into '${parentNote.noteId}' is only possible for type 'shortcut'.`); throw new Error(`Creating child notes into '${parentNote.noteId}' is only possible for type 'launcher'.`);
} }
return parentNote; return parentNote;

View File

@ -8,7 +8,7 @@ const dateUtils = require("./date_utils");
const LBTPL_ROOT = "lbtpl_root"; const LBTPL_ROOT = "lbtpl_root";
const LBTPL_BASE = "lbtpl_base"; const LBTPL_BASE = "lbtpl_base";
const LBTPL_COMMAND = "lbtpl_command"; const LBTPL_COMMAND = "lbtpl_command";
const LBTPL_NOTE_SHORTCUT = "lbtpl_noteshortcut"; const LBTPL_NOTE_LAUNCHER = "lbtpl_notelauncher";
const LBTPL_SCRIPT = "lbtpl_script"; const LBTPL_SCRIPT = "lbtpl_script";
const LBTPL_BUILTIN_WIDGET = "lbtpl_builtinwidget"; const LBTPL_BUILTIN_WIDGET = "lbtpl_builtinwidget";
const LBTPL_SPACER = "lbtpl_spacer"; const LBTPL_SPACER = "lbtpl_spacer";
@ -276,14 +276,14 @@ function getLaunchBarRoot() {
return note; return note;
} }
function getLaunchBarAvailableShortcutsRoot() { function getLaunchBarAvailableLaunchersRoot() {
let note = becca.getNote('lb_availableshortcuts'); let note = becca.getNote('lb_availablelaunchers');
if (!note) { if (!note) {
note = noteService.createNewNote({ note = noteService.createNewNote({
branchId: 'lb_availableshortcuts', branchId: 'lb_availablelaunchers',
noteId: 'lb_availableshortcuts', noteId: 'lb_availablelaunchers',
title: 'Available shortcuts', title: 'Available launchers',
type: 'doc', type: 'doc',
content: '', content: '',
parentNoteId: getLaunchBarRoot().noteId, parentNoteId: getLaunchBarRoot().noteId,
@ -294,7 +294,7 @@ function getLaunchBarAvailableShortcutsRoot() {
note.addLabel("docName", "launchbar_intro"); note.addLabel("docName", "launchbar_intro");
} }
const branch = becca.getBranch('lb_availableshortcuts'); const branch = becca.getBranch('lb_availablelaunchers');
if (!branch.isExpanded) { if (!branch.isExpanded) {
branch.isExpanded = true; branch.isExpanded = true;
branch.save(); branch.save();
@ -303,14 +303,14 @@ function getLaunchBarAvailableShortcutsRoot() {
return note; return note;
} }
function getLaunchBarVisibleShortcutsRoot() { function getLaunchBarVisibleLaunchersRoot() {
let note = becca.getNote('lb_visibleshortcuts'); let note = becca.getNote('lb_visiblelaunchers');
if (!note) { if (!note) {
note = noteService.createNewNote({ note = noteService.createNewNote({
branchId: 'lb_visibleshortcuts', branchId: 'lb_visiblelaunchers',
noteId: 'lb_visibleshortcuts', noteId: 'lb_visiblelaunchers',
title: 'Visible shortcuts', title: 'Visible launchers',
type: 'doc', type: 'doc',
content: '', content: '',
parentNoteId: getLaunchBarRoot().noteId, parentNoteId: getLaunchBarRoot().noteId,
@ -321,7 +321,7 @@ function getLaunchBarVisibleShortcutsRoot() {
note.addLabel("docName", "launchbar_intro"); note.addLabel("docName", "launchbar_intro");
} }
const branch = becca.getBranch('lb_visibleshortcuts'); const branch = becca.getBranch('lb_visiblelaunchers');
if (!branch.isExpanded) { if (!branch.isExpanded) {
branch.isExpanded = true; branch.isExpanded = true;
branch.save(); branch.save();
@ -330,20 +330,20 @@ function getLaunchBarVisibleShortcutsRoot() {
return note; return note;
} }
const shortcuts = [ const launchers = [
// visible shortcuts: // visible launchers:
{ id: 'lb_newnote', command: 'createNoteIntoInbox', title: 'New note', icon: 'bx bx-file-blank', isVisible: true }, { id: 'lb_newnote', command: 'createNoteIntoInbox', title: 'New note', icon: 'bx bx-file-blank', isVisible: true },
{ id: 'lb_search', command: 'searchNotes', title: 'Search notes', icon: 'bx bx-search', isVisible: true }, { id: 'lb_search', command: 'searchNotes', title: 'Search notes', icon: 'bx bx-search', isVisible: true },
{ id: 'lb_jumpto', command: 'jumpToNote', title: 'Jump to note', icon: 'bx bx-send', isVisible: true }, { id: 'lb_jumpto', command: 'jumpToNote', title: 'Jump to note', icon: 'bx bx-send', isVisible: true },
{ id: 'lb_notemap', targetNoteId: 'globalnotemap', title: 'Note map', icon: 'bx bx-map-alt', isVisible: true }, { id: 'lb_notemap', targetNoteId: 'globalnotemap', title: 'Note map', icon: 'bx bx-map-alt', isVisible: true },
{ id: 'lb_calendar', builtinWidget: 'calendar', title: 'Calendar', icon: 'bx bx-calendar', isVisible: true }, { id: 'lb_calendar', builtinWidget: 'calendar', title: 'Calendar', icon: 'bx bx-calendar', isVisible: true },
{ id: 'lb_spacer1', builtinWidget: 'spacer', title: 'Spacer', isVisible: true, baseSize: 50, growthFactor: 0 }, { id: 'lb_spacer1', builtinWidget: 'spacer', title: 'Spacer', isVisible: true, baseSize: "50", growthFactor: "0" },
{ id: 'lb_bookmarks', builtinWidget: 'bookmarks', title: 'Bookmarks', icon: 'bx bx-bookmark', isVisible: true }, { id: 'lb_bookmarks', builtinWidget: 'bookmarks', title: 'Bookmarks', icon: 'bx bx-bookmark', isVisible: true },
{ id: 'lb_spacer2', builtinWidget: 'spacer', title: 'Spacer', isVisible: true, baseSize: 0, growthFactor: 1 }, { id: 'lb_spacer2', builtinWidget: 'spacer', title: 'Spacer', isVisible: true, baseSize: "0", growthFactor: "1" },
{ id: 'lb_protectedsession', builtinWidget: 'protectedSession', title: 'Protected session', icon: 'bx bx bx-shield-quarter', isVisible: true }, { id: 'lb_protectedsession', builtinWidget: 'protectedSession', title: 'Protected session', icon: 'bx bx bx-shield-quarter', isVisible: true },
{ id: 'lb_syncstatus', builtinWidget: 'syncStatus', title: 'Sync status', icon: 'bx bx-wifi', isVisible: true }, { id: 'lb_syncstatus', builtinWidget: 'syncStatus', title: 'Sync status', icon: 'bx bx-wifi', isVisible: true },
// available shortcuts: // available launchers:
{ id: 'lb_recentchanges', command: 'showRecentChanges', title: 'Recent changes', icon: 'bx bx-history', isVisible: false }, { id: 'lb_recentchanges', command: 'showRecentChanges', title: 'Recent changes', icon: 'bx bx-history', isVisible: false },
{ id: 'lb_backinhistory', builtinWidget: 'backInHistoryButton', title: 'Back in history', icon: 'bx bxs-left-arrow-square', isVisible: false }, { id: 'lb_backinhistory', builtinWidget: 'backInHistoryButton', title: 'Back in history', icon: 'bx bxs-left-arrow-square', isVisible: false },
{ id: 'lb_forwardinhistory', builtinWidget: 'forwardInHistoryButton', title: 'Forward in history', icon: 'bx bxs-right-arrow-square', isVisible: false }, { id: 'lb_forwardinhistory', builtinWidget: 'forwardInHistoryButton', title: 'Forward in history', icon: 'bx bxs-right-arrow-square', isVisible: false },
@ -353,53 +353,53 @@ function createMissingSpecialNotes() {
getSqlConsoleRoot(); getSqlConsoleRoot();
getGlobalNoteMap(); getGlobalNoteMap();
getBulkActionNote(); getBulkActionNote();
createShortcutTemplates(); createLauncherTemplates();
getLaunchBarRoot(); getLaunchBarRoot();
getLaunchBarAvailableShortcutsRoot(); getLaunchBarAvailableLaunchersRoot();
getLaunchBarVisibleShortcutsRoot(); getLaunchBarVisibleLaunchersRoot();
getShareRoot(); getShareRoot();
for (const shortcut of shortcuts) { for (const launcher of launchers) {
let note = becca.getNote(shortcut.id); let note = becca.getNote(launcher.id);
if (note) { if (note) {
continue; continue;
} }
const parentNoteId = shortcut.isVisible const parentNoteId = launcher.isVisible
? getLaunchBarVisibleShortcutsRoot().noteId ? getLaunchBarVisibleLaunchersRoot().noteId
: getLaunchBarAvailableShortcutsRoot().noteId; : getLaunchBarAvailableLaunchersRoot().noteId;
note = noteService.createNewNote({ note = noteService.createNewNote({
noteId: shortcut.id, noteId: launcher.id,
title: shortcut.title, title: launcher.title,
type: 'shortcut', type: 'launcher',
content: '', content: '',
parentNoteId: parentNoteId parentNoteId: parentNoteId
}).note; }).note;
if (shortcut.icon) { if (launcher.icon) {
note.addLabel('iconClass', shortcut.icon); note.addLabel('iconClass', launcher.icon);
} }
if (shortcut.command) { if (launcher.command) {
note.addRelation('template', LBTPL_COMMAND); note.addRelation('template', LBTPL_COMMAND);
note.addLabel('command', shortcut.command); note.addLabel('command', launcher.command);
} else if (shortcut.builtinWidget) { } else if (launcher.builtinWidget) {
if (shortcut.builtinWidget === 'spacer') { if (launcher.builtinWidget === 'spacer') {
note.addRelation('template', LBTPL_SPACER); note.addRelation('template', LBTPL_SPACER);
note.addLabel("baseSize", shortcut.baseSize); note.addLabel("baseSize", launcher.baseSize);
note.addLabel("growthFactor", shortcut.growthFactor); note.addLabel("growthFactor", launcher.growthFactor);
} else { } else {
note.addRelation('template', LBTPL_BUILTIN_WIDGET); note.addRelation('template', LBTPL_BUILTIN_WIDGET);
} }
note.addLabel('builtinWidget', shortcut.builtinWidget); note.addLabel('builtinWidget', launcher.builtinWidget);
} else if (shortcut.targetNoteId) { } else if (launcher.targetNoteId) {
note.addRelation('template', LBTPL_NOTE_SHORTCUT); note.addRelation('template', LBTPL_NOTE_LAUNCHER);
note.addRelation('targetNote', shortcut.targetNoteId); note.addRelation('targetNote', launcher.targetNoteId);
} else { } else {
throw new Error(`No action defined for shortcut ${JSON.stringify(shortcut)}`); throw new Error(`No action defined for launcher ${JSON.stringify(launcher)}`);
} }
} }
@ -412,47 +412,47 @@ function createMissingSpecialNotes() {
} }
} }
function createShortcut(parentNoteId, shortcutType) { function createLauncher(parentNoteId, launcherType) {
let note; let note;
if (shortcutType === 'note') { if (launcherType === 'note') {
note = noteService.createNewNote({ note = noteService.createNewNote({
title: "Note shortcut", title: "Note launcher",
type: 'shortcut', type: 'launcher',
content: '', content: '',
parentNoteId: parentNoteId parentNoteId: parentNoteId
}).note; }).note;
note.addRelation('template', LBTPL_NOTE_SHORTCUT); note.addRelation('template', LBTPL_NOTE_LAUNCHER);
} else if (shortcutType === 'script') { } else if (launcherType === 'script') {
note = noteService.createNewNote({ note = noteService.createNewNote({
title: "Script shortcut", title: "Script launcher",
type: 'shortcut', type: 'launcher',
content: '', content: '',
parentNoteId: parentNoteId parentNoteId: parentNoteId
}).note; }).note;
note.addRelation('template', LBTPL_SCRIPT); note.addRelation('template', LBTPL_SCRIPT);
} else if (shortcutType === 'customWidget') { } else if (launcherType === 'customWidget') {
note = noteService.createNewNote({ note = noteService.createNewNote({
title: "Widget shortcut", title: "Widget launcher",
type: 'shortcut', type: 'launcher',
content: '', content: '',
parentNoteId: parentNoteId parentNoteId: parentNoteId
}).note; }).note;
note.addRelation('template', LBTPL_CUSTOM_WIDGET); note.addRelation('template', LBTPL_CUSTOM_WIDGET);
} else if (shortcutType === 'spacer') { } else if (launcherType === 'spacer') {
note = noteService.createNewNote({ note = noteService.createNewNote({
title: "Spacer", title: "Spacer",
type: 'shortcut', type: 'launcher',
content: '', content: '',
parentNoteId: parentNoteId parentNoteId: parentNoteId
}).note; }).note;
note.addRelation('template', LBTPL_SPACER); note.addRelation('template', LBTPL_SPACER);
} else { } else {
throw new Error(`Unrecognized shortcut type ${shortcutType}`); throw new Error(`Unrecognized launcher type ${launcherType}`);
} }
return { return {
@ -461,7 +461,7 @@ function createShortcut(parentNoteId, shortcutType) {
}; };
} }
function createShortcutTemplates() { function createLauncherTemplates() {
if (!(LBTPL_ROOT in becca.notes)) { if (!(LBTPL_ROOT in becca.notes)) {
noteService.createNewNote({ noteService.createNewNote({
branchId: LBTPL_ROOT, branchId: LBTPL_ROOT,
@ -474,46 +474,46 @@ function createShortcutTemplates() {
} }
if (!(LBTPL_BASE in becca.notes)) { if (!(LBTPL_BASE in becca.notes)) {
const tpl = noteService.createNewNote({ noteService.createNewNote({
branchId: LBTPL_BASE, branchId: LBTPL_BASE,
noteId: LBTPL_BASE, noteId: LBTPL_BASE,
title: 'Launch bar base shortcut', title: 'Launch bar base launcher',
type: 'doc', type: 'doc',
content: '', content: '',
parentNoteId: getHiddenRoot().noteId parentNoteId: getHiddenRoot().noteId
}).note; });
tpl.addLabel('label:keyboardShortcut', 'promoted,text');
} }
if (!(LBTPL_COMMAND in becca.notes)) { if (!(LBTPL_COMMAND in becca.notes)) {
const tpl = noteService.createNewNote({ const tpl = noteService.createNewNote({
branchId: LBTPL_COMMAND, branchId: LBTPL_COMMAND,
noteId: LBTPL_COMMAND, noteId: LBTPL_COMMAND,
title: 'Command shortcut', title: 'Command launcher',
type: 'doc', type: 'doc',
content: '', content: '',
parentNoteId: LBTPL_ROOT parentNoteId: LBTPL_ROOT
}).note; }).note;
tpl.addRelation('template', LBTPL_BASE); tpl.addRelation('template', LBTPL_BASE);
tpl.addLabel('shortcutType', 'command'); tpl.addLabel('launcherType', 'command');
tpl.addLabel('docName', 'launchbar_command_launcher');
} }
if (!(LBTPL_NOTE_SHORTCUT in becca.notes)) { if (!(LBTPL_NOTE_LAUNCHER in becca.notes)) {
const tpl = noteService.createNewNote({ const tpl = noteService.createNewNote({
branchId: LBTPL_NOTE_SHORTCUT, branchId: LBTPL_NOTE_LAUNCHER,
noteId: LBTPL_NOTE_SHORTCUT, noteId: LBTPL_NOTE_LAUNCHER,
title: 'Note shortcut', title: 'Note launcher',
type: 'doc', type: 'doc',
content: '', content: '',
parentNoteId: LBTPL_ROOT parentNoteId: LBTPL_ROOT
}).note; }).note;
tpl.addRelation('template', LBTPL_BASE); tpl.addRelation('template', LBTPL_BASE);
tpl.addLabel('shortcutType', 'note'); tpl.addLabel('launcherType', 'note');
tpl.addLabel('relation:targetNote', 'promoted'); tpl.addLabel('relation:targetNote', 'promoted');
tpl.addLabel('docName', 'launchbar_note_shortcut'); tpl.addLabel('docName', 'launchbar_note_launcher');
tpl.addLabel('label:keyboardShortcut', 'promoted,text');
} }
if (!(LBTPL_SCRIPT in becca.notes)) { if (!(LBTPL_SCRIPT in becca.notes)) {
@ -527,9 +527,10 @@ function createShortcutTemplates() {
}).note; }).note;
tpl.addRelation('template', LBTPL_BASE); tpl.addRelation('template', LBTPL_BASE);
tpl.addLabel('shortcutType', 'script'); tpl.addLabel('launcherType', 'script');
tpl.addLabel('relation:script', 'promoted'); tpl.addLabel('relation:script', 'promoted');
tpl.addLabel('docName', 'launchbar_script_shortcut'); tpl.addLabel('docName', 'launchbar_script_launcher');
tpl.addLabel('label:keyboardShortcut', 'promoted,text');
} }
if (!(LBTPL_BUILTIN_WIDGET in becca.notes)) { if (!(LBTPL_BUILTIN_WIDGET in becca.notes)) {
@ -543,7 +544,7 @@ function createShortcutTemplates() {
}).note; }).note;
tpl.addRelation('template', LBTPL_BASE); tpl.addRelation('template', LBTPL_BASE);
tpl.addLabel('shortcutType', 'builtinWidget'); tpl.addLabel('launcherType', 'builtinWidget');
} }
if (!(LBTPL_SPACER in becca.notes)) { if (!(LBTPL_SPACER in becca.notes)) {
@ -575,13 +576,13 @@ function createShortcutTemplates() {
}).note; }).note;
tpl.addRelation('template', LBTPL_BASE); tpl.addRelation('template', LBTPL_BASE);
tpl.addLabel('shortcutType', 'customWidget'); tpl.addLabel('launcherType', 'customWidget');
tpl.addLabel('relation:widget', 'promoted'); tpl.addLabel('relation:widget', 'promoted');
tpl.addLabel('docName', 'launchbar_widget_shortcut'); tpl.addLabel('docName', 'launchbar_widget_launcher');
} }
} }
function resetShortcut(noteId) { function resetLauncher(noteId) {
if (noteId.startsWith('lb_')) { if (noteId.startsWith('lb_')) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
@ -598,7 +599,7 @@ function resetShortcut(noteId) {
log.info(`Note ${noteId} has not been found and cannot be reset.`); log.info(`Note ${noteId} has not been found and cannot be reset.`);
} }
} else { } else {
log.info(`Note ${noteId} is not a resettable shortcut note.`); log.info(`Note ${noteId} is not a resettable launcher note.`);
} }
createMissingSpecialNotes(); createMissingSpecialNotes();
@ -614,6 +615,6 @@ module.exports = {
getShareRoot, getShareRoot,
getHiddenRoot, getHiddenRoot,
getBulkActionNote, getBulkActionNote,
createShortcut, createLauncher,
resetShortcut resetLauncher
}; };

View File

@ -30,7 +30,7 @@ function getNotes(noteIds) {
} }
function validateParentChild(parentNoteId, childNoteId, branchId = null) { function validateParentChild(parentNoteId, childNoteId, branchId = null) {
if (['root', 'hidden', 'share', 'lb_root', 'lb_availableshortcuts', 'lb_visibleshortcuts'].includes(childNoteId)) { if (['root', 'hidden', 'share', 'lb_root', 'lb_availablelaunchers', 'lb_visiblelaunchers'].includes(childNoteId)) {
return { success: false, message: `Cannot change this note's location.`}; return { success: false, message: `Cannot change this note's location.`};
} }
@ -58,10 +58,10 @@ function validateParentChild(parentNoteId, childNoteId, branchId = null) {
}; };
} }
if (becca.getNote(parentNoteId).type === 'shortcut') { if (becca.getNote(parentNoteId).type === 'launcher') {
return { return {
success: false, success: false,
message: 'Shortcut note cannot have any children.' message: 'Launcher note cannot have any children.'
}; };
} }