mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
launcher improvements
This commit is contained in:
parent
7aa801fc1f
commit
0e68e8069b
@ -15,14 +15,14 @@ export default class ShortcutComponent extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
bindNoteShortcutHandler(attr) {
|
||||
const handler = () => appContext.tabManager.getActiveContext().setNote(attr.noteId);
|
||||
const namespace = attr.attributeId;
|
||||
bindNoteShortcutHandler(label) {
|
||||
const handler = () => appContext.tabManager.getActiveContext().setNote(label.noteId);
|
||||
const namespace = label.attributeId;
|
||||
|
||||
if (attr.isDeleted) {
|
||||
if (label.isDeleted) {
|
||||
shortcutService.removeGlobalShortcut(namespace);
|
||||
} else {
|
||||
shortcutService.bindGlobalShortcut(attr.value, handler, namespace);
|
||||
shortcutService.bindGlobalShortcut(label.value, handler, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
|
1
src/public/app/doc_notes/launchbar_command_launcher.html
Normal file
1
src/public/app/doc_notes/launchbar_command_launcher.html
Normal file
@ -0,0 +1 @@
|
||||
<p>Keyboard shortcut for this launcher action can be configured in Options -> Shortcuts.</p>
|
@ -36,9 +36,9 @@ export default class LauncherContextMenu {
|
||||
const canBeReset = note.noteId.startsWith("lb_");
|
||||
|
||||
return [
|
||||
(isVisibleRoot || isAvailableRoot) ? { title: 'Add note launcher', command: 'addNoteLauncher', uiIcon: "bx bx-plus" } : null,
|
||||
(isVisibleRoot || isAvailableRoot) ? { title: 'Add script launcher', command: 'addScriptLauncher', uiIcon: "bx bx-plus" } : null,
|
||||
(isVisibleRoot || isAvailableRoot) ? { title: 'Add widget launcher', command: 'addWidgetLauncher', uiIcon: "bx bx-plus" } : null,
|
||||
(isVisibleRoot || isAvailableRoot) ? { title: 'Add a note launcher', command: 'addNoteLauncher', uiIcon: "bx bx-plus" } : null,
|
||||
(isVisibleRoot || isAvailableRoot) ? { title: 'Add a script launcher', command: 'addScriptLauncher', 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: 'addSpacerLauncher', uiIcon: "bx bx-plus" } : null,
|
||||
(isVisibleRoot || isAvailableRoot) ? { title: "----" } : null,
|
||||
{ title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash", enabled: canBeDeleted },
|
||||
@ -54,7 +54,7 @@ export default class LauncherContextMenu {
|
||||
async selectMenuItemHandler({command}) {
|
||||
if (command === 'resetLauncher') {
|
||||
const confirmed = await dialogService.confirm(`Do you really want to reset "${this.node.title}"?
|
||||
All data / settings in this launcher (and its children) will be lost
|
||||
All data / settings in this note (and its children) will be lost
|
||||
and the launcher will be returned to its original location.`);
|
||||
|
||||
if (confirmed) {
|
||||
|
149
src/public/app/widgets/containers/launcher.js
Normal file
149
src/public/app/widgets/containers/launcher.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,7 @@
|
||||
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 "../../components/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";
|
||||
import LauncherWidget from "./launcher.js";
|
||||
|
||||
export default class LauncherContainer extends FlexContainer {
|
||||
constructor() {
|
||||
@ -35,7 +27,16 @@ export default class LauncherContainer extends FlexContainer {
|
||||
|
||||
await Promise.allSettled(
|
||||
(await visibleLaunchersRoot.getChildNotes())
|
||||
.map(launcher => this.initLauncher(launcher))
|
||||
.map(async launcherNote => {
|
||||
try {
|
||||
const launcherWidget = new LauncherWidget(launcherNote);
|
||||
await launcherWidget.initLauncher();
|
||||
this.child(launcherWidget);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e.message);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.$widget.empty();
|
||||
@ -59,93 +60,6 @@ export default class LauncherContainer extends FlexContainer {
|
||||
}
|
||||
}
|
||||
|
||||
async initLauncher(launcher) {
|
||||
try {
|
||||
if (launcher.type !== 'launcher') {
|
||||
console.warn(`Note ${launcher.noteId} is not a launcher even though it's in launcher subtree`);
|
||||
return;
|
||||
}
|
||||
|
||||
const launcherType = launcher.getLabelValue("launcherType");
|
||||
|
||||
if (launcherType === 'command') {
|
||||
this.child(new ButtonWidget()
|
||||
.title(launcher.title)
|
||||
.icon(launcher.getIcon())
|
||||
.command(launcher.getLabelValue("command")));
|
||||
} 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.child(new ButtonWidget()
|
||||
.title(launcher.title)
|
||||
.icon(launcher.getIcon())
|
||||
.onClick(() => {
|
||||
const targetNoteId = launcher.getRelationValue('targetNote');
|
||||
|
||||
if (!targetNoteId) {
|
||||
dialogService.info("This launcher doesn't define target note.");
|
||||
return;
|
||||
}
|
||||
|
||||
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId, true)
|
||||
}));
|
||||
} else if (launcherType === 'script') {
|
||||
this.child(new ButtonWidget()
|
||||
.title(launcher.title)
|
||||
.icon(launcher.getIcon())
|
||||
.onClick(async () => {
|
||||
const script = await launcher.getRelationTarget('script');
|
||||
|
||||
await script.executeScript();
|
||||
}));
|
||||
} else if (launcherType === 'customWidget') {
|
||||
const widget = await launcher.getRelationTarget('widget');
|
||||
|
||||
if (widget) {
|
||||
const res = await widget.executeScript();
|
||||
|
||||
this.child(res);
|
||||
}
|
||||
} else if (launcherType === 'builtinWidget') {
|
||||
const builtinWidget = launcher.getLabelValue("builtinWidget");
|
||||
|
||||
if (builtinWidget) {
|
||||
if (builtinWidget === 'calendar') {
|
||||
this.child(new CalendarWidget(launcher.title, launcher.getIcon()));
|
||||
} else if (builtinWidget === 'spacer') {
|
||||
// || has to be inside since 0 is a valid value
|
||||
const baseSize = parseInt(launcher.getLabelValue("baseSize") || "40");
|
||||
const growthFactor = parseInt(launcher.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 launcher ${launcher.noteId} "${launcher.title}"`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn(`Unrecognized launcher type ${launcherType} for launcher '${launcher.noteId}' title ${launcher.title}`);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Initialization of launcher '${launcher.noteId}' with title '${launcher.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_"))
|
||||
|
@ -8,10 +8,10 @@ function getKeyboardActions() {
|
||||
}
|
||||
|
||||
function getShortcutsForNotes() {
|
||||
const attrs = becca.findAttributes('label', 'keyboardShortcut');
|
||||
const labels = becca.findAttributes('label', 'keyboardShortcut');
|
||||
|
||||
// launchers have different handling
|
||||
return attrs.filter(attr => becca.getNote(attr.noteId)?.type !== 'launcher');
|
||||
return labels.filter(attr => becca.getNote(attr.noteId)?.type !== 'launcher');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -474,16 +474,14 @@ function createLauncherTemplates() {
|
||||
}
|
||||
|
||||
if (!(LBTPL_BASE in becca.notes)) {
|
||||
const tpl = noteService.createNewNote({
|
||||
noteService.createNewNote({
|
||||
branchId: LBTPL_BASE,
|
||||
noteId: LBTPL_BASE,
|
||||
title: 'Launch bar base launcher',
|
||||
type: 'doc',
|
||||
content: '',
|
||||
parentNoteId: getHiddenRoot().noteId
|
||||
}).note;
|
||||
|
||||
tpl.addLabel('label:keyboardLauncher', 'promoted,text');
|
||||
});
|
||||
}
|
||||
|
||||
if (!(LBTPL_COMMAND in becca.notes)) {
|
||||
@ -498,6 +496,7 @@ function createLauncherTemplates() {
|
||||
|
||||
tpl.addRelation('template', LBTPL_BASE);
|
||||
tpl.addLabel('launcherType', 'command');
|
||||
tpl.addLabel('docName', 'launchbar_command_launcher');
|
||||
}
|
||||
|
||||
if (!(LBTPL_NOTE_LAUNCHER in becca.notes)) {
|
||||
@ -514,6 +513,7 @@ function createLauncherTemplates() {
|
||||
tpl.addLabel('launcherType', 'note');
|
||||
tpl.addLabel('relation:targetNote', 'promoted');
|
||||
tpl.addLabel('docName', 'launchbar_note_launcher');
|
||||
tpl.addLabel('label:keyboardShortcut', 'promoted,text');
|
||||
}
|
||||
|
||||
if (!(LBTPL_SCRIPT in becca.notes)) {
|
||||
@ -530,6 +530,7 @@ function createLauncherTemplates() {
|
||||
tpl.addLabel('launcherType', 'script');
|
||||
tpl.addLabel('relation:script', 'promoted');
|
||||
tpl.addLabel('docName', 'launchbar_script_launcher');
|
||||
tpl.addLabel('label:keyboardShortcut', 'promoted,text');
|
||||
}
|
||||
|
||||
if (!(LBTPL_BUILTIN_WIDGET in becca.notes)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user