launcher improvements

This commit is contained in:
zadam 2022-12-01 16:22:04 +01:00
parent 7aa801fc1f
commit 0e68e8069b
10 changed files with 177 additions and 112 deletions

View File

@ -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);
}
}

View File

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

View File

@ -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) {

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

@ -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_"))

View File

@ -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 = {

View File

@ -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)) {