mirror of
https://github.com/zadam/trilium.git
synced 2025-06-05 01:18:44 +02:00
shortcuts improvements
This commit is contained in:
parent
e4f57ab2fe
commit
a459230aa9
@ -1,3 +1,9 @@
|
||||
<p>Please define the target script note in the promoted attributes. This script will be executed immediately upon clicking the launchbar icon.</p>
|
||||
|
||||
<p>Launchbar displays the title / icon from the shortcut which does not necessarily mirrors those of the target script note.</p>
|
||||
|
||||
<h4>Example script</h4>
|
||||
|
||||
<pre>
|
||||
alert("Current note is " + api.getActiveContextNote().title);
|
||||
</pre>
|
||||
|
@ -1 +1,34 @@
|
||||
<p>Please define the target widget note in the promoted attributes. The widget will be used to render the launchbar icon.</p>
|
||||
|
||||
<h4>Example launchbar widget</h4>
|
||||
|
||||
<pre>
|
||||
const TPL = `<div style="height: 53px; width: 53px;"></div>`;
|
||||
|
||||
class ExampleLaunchbarWidget extends api.NoteContextAwareWidget {
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
this.$widget.css("background-color", this.stringToColor(note.title));
|
||||
}
|
||||
|
||||
stringToColor(str) {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
|
||||
let color = '#';
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const value = (hash >> (i * 8)) & 0xFF;
|
||||
color += ('00' + value.toString(16)).substr(-2);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ExampleLaunchbarWidget();
|
||||
</pre>
|
||||
|
@ -792,10 +792,10 @@ class NoteShort {
|
||||
|
||||
if (env === "frontend") {
|
||||
const bundleService = (await import("../services/bundle.js")).default;
|
||||
await bundleService.getAndExecuteBundle(this.noteId);
|
||||
return await bundleService.getAndExecuteBundle(this.noteId);
|
||||
}
|
||||
else if (env === "backend") {
|
||||
await server.post('script/run/' + this.noteId);
|
||||
return await server.post('script/run/' + this.noteId);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unrecognized env type ${env} for note ${this.noteId}`);
|
||||
|
@ -11,7 +11,6 @@ import CollapsibleWidget from '../widgets/collapsible_widget.js';
|
||||
import ws from "./ws.js";
|
||||
import appContext from "./app_context.js";
|
||||
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
|
||||
import NoteContextCachingWidget from "../widgets/note_context_caching_widget.js";
|
||||
import BasicWidget from "../widgets/basic_widget.js";
|
||||
import SpacedUpdate from "./spaced_update.js";
|
||||
|
||||
@ -40,24 +39,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
||||
/** @property {CollapsibleWidget} */
|
||||
this.CollapsibleWidget = CollapsibleWidget;
|
||||
|
||||
/**
|
||||
* @property {NoteContextAwareWidget}
|
||||
* @deprecated use NoteContextAwareWidget instead
|
||||
*/
|
||||
this.TabAwareWidget = NoteContextAwareWidget;
|
||||
|
||||
/** @property {NoteContextAwareWidget} */
|
||||
this.NoteContextAwareWidget = NoteContextAwareWidget;
|
||||
|
||||
/**
|
||||
* @property {NoteContextCachingWidget}
|
||||
* @deprecated use NoteContextCachingWidget instead
|
||||
*/
|
||||
this.TabCachingWidget = NoteContextCachingWidget;
|
||||
|
||||
/** @property {NoteContextAwareWidget} */
|
||||
this.NoteContextCachingWidget = NoteContextCachingWidget;
|
||||
|
||||
/** @property {BasicWidget} */
|
||||
this.BasicWidget = BasicWidget;
|
||||
|
||||
|
@ -42,16 +42,23 @@ export default class Component {
|
||||
|
||||
/** @returns {Promise} */
|
||||
handleEvent(name, data) {
|
||||
const callMethodPromise = this.initialized
|
||||
? this.initialized.then(() => this.callMethod(this[name + 'Event'], data))
|
||||
: this.callMethod(this[name + 'Event'], data);
|
||||
try {
|
||||
const callMethodPromise = this.initialized
|
||||
? this.initialized.then(() => this.callMethod(this[name + 'Event'], data))
|
||||
: this.callMethod(this[name + 'Event'], data);
|
||||
|
||||
const childrenPromise = this.handleEventInChildren(name, data);
|
||||
const childrenPromise = this.handleEventInChildren(name, data);
|
||||
|
||||
// don't create promises if not needed (optimization)
|
||||
return callMethodPromise && childrenPromise
|
||||
? Promise.all([callMethodPromise, childrenPromise])
|
||||
: (callMethodPromise || childrenPromise);
|
||||
// don't create promises if not needed (optimization)
|
||||
return callMethodPromise && childrenPromise
|
||||
? Promise.all([callMethodPromise, childrenPromise])
|
||||
: (callMethodPromise || childrenPromise);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Handling of event '${name}' failed in ${this.constructor.name} with error ${e.message} ${e.stack}`);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {Promise} */
|
||||
|
@ -33,53 +33,93 @@ export default class ShortcutContainer extends FlexContainer {
|
||||
}
|
||||
|
||||
for (const shortcut of await visibleShortcutsRoot.getChildNotes()) {
|
||||
if (shortcut.getLabelValue("command")) {
|
||||
this.child(new ButtonWidget()
|
||||
.title(shortcut.title)
|
||||
.icon(shortcut.getIcon())
|
||||
.command(shortcut.getLabelValue("command")));
|
||||
} else if (shortcut.hasRelation('targetNote')) {
|
||||
this.child(new ButtonWidget()
|
||||
.title(shortcut.title)
|
||||
.icon(shortcut.getIcon())
|
||||
.onClick(() => appContext.tabManager.openTabWithNoteWithHoisting(shortcut.getRelationValue('targetNote'), true)));
|
||||
} else {
|
||||
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 === 'pluginButtons') {
|
||||
this.child(new FlexContainer("column")
|
||||
.id("plugin-buttons")
|
||||
.contentSized());
|
||||
} 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.log(`Unrecognized builtin widget ${builtinWidget} for shortcut ${shortcut.noteId} "${shortcut.title}"`);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await this.initShortcut(shortcut);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Initialization of shortcut '${shortcut.noteId}' with title '${shortcut.title}' failed with error: ${e.message} ${e.stack}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.$widget.empty();
|
||||
this.renderChildren();
|
||||
|
||||
this.handleEventInChildren('initialRenderComplete');
|
||||
await this.handleEventInChildren('initialRenderComplete');
|
||||
|
||||
const activeContext = appContext.tabManager.getActiveContext();
|
||||
|
||||
await this.handleEvent('setNoteContext', {
|
||||
noteContext: activeContext
|
||||
});
|
||||
await this.handleEvent('noteSwitched', {
|
||||
noteContext: activeContext,
|
||||
notePath: activeContext.notePath
|
||||
});
|
||||
}
|
||||
|
||||
async initShortcut(shortcut) {
|
||||
if (shortcut.type !== 'shortcut') {
|
||||
console.warn(`Note ${shortcut.noteId} is not a shortcut even though it's in shortcut subtree`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shortcut.getLabelValue("command")) {
|
||||
this.child(new ButtonWidget()
|
||||
.title(shortcut.title)
|
||||
.icon(shortcut.getIcon())
|
||||
.command(shortcut.getLabelValue("command")));
|
||||
} else if (shortcut.hasRelation('targetNote')) {
|
||||
this.child(new ButtonWidget()
|
||||
.title(shortcut.title)
|
||||
.icon(shortcut.getIcon())
|
||||
.onClick(() => appContext.tabManager.openTabWithNoteWithHoisting(shortcut.getRelationValue('targetNote'), true)));
|
||||
} else if (shortcut.hasRelation('script')) {
|
||||
this.child(new ButtonWidget()
|
||||
.title(shortcut.title)
|
||||
.icon(shortcut.getIcon())
|
||||
.onClick(async () => {
|
||||
const script = await shortcut.getRelationTarget('script');
|
||||
|
||||
await script.executeScript();
|
||||
}));
|
||||
} else if (shortcut.hasRelation('widget')) {
|
||||
const widget = await shortcut.getRelationTarget('widget');
|
||||
|
||||
const res = await widget.executeScript();
|
||||
|
||||
this.child(res);
|
||||
} else {
|
||||
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 === 'pluginButtons') {
|
||||
this.child(new FlexContainer("column")
|
||||
.id("plugin-buttons")
|
||||
.contentSized());
|
||||
} 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.log(`Unrecognized builtin widget ${builtinWidget} for shortcut ${shortcut.noteId} "${shortcut.title}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entitiesReloadedEvent({loadResults}) {
|
||||
|
@ -1,99 +0,0 @@
|
||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||
import keyboardActionsService from "../services/keyboard_actions.js";
|
||||
|
||||
export default class NoteContextCachingWidget extends NoteContextAwareWidget {
|
||||
constructor(widgetFactory) {
|
||||
super();
|
||||
|
||||
this.widgetFactory = widgetFactory;
|
||||
this.widgets = {};
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return this.$widget = $(`<div class="marker" style="display: none;">`);
|
||||
}
|
||||
|
||||
async newNoteContextCreatedEvent({noteContext}) {
|
||||
const {ntxId} = noteContext;
|
||||
|
||||
if (this.widgets[ntxId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.widgets[ntxId] = this.widgetFactory();
|
||||
|
||||
const $renderedWidget = this.widgets[ntxId].render();
|
||||
this.widgets[ntxId].toggleExt(false); // new tab is always not active, can be activated after creation
|
||||
|
||||
this.$widget.after($renderedWidget);
|
||||
|
||||
this.widgets[ntxId].handleEvent('initialRenderComplete');
|
||||
|
||||
keyboardActionsService.updateDisplayedShortcuts($renderedWidget);
|
||||
|
||||
await this.widgets[ntxId].handleEvent('setNoteContext', {noteContext});
|
||||
|
||||
this.child(this.widgets[ntxId]); // add as child only once it is ready (rendered with noteContext)
|
||||
}
|
||||
|
||||
noteContextRemovedEvent({ntxIds}) {
|
||||
for (const ntxId of ntxIds) {
|
||||
const widget = this.widgets[ntxId];
|
||||
|
||||
if (widget) {
|
||||
widget.remove();
|
||||
delete this.widgets[ntxId];
|
||||
|
||||
this.children = this.children.filter(ch => ch !== widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
this.toggleExt(true);
|
||||
}
|
||||
|
||||
toggleInt(show) {} // not needed
|
||||
|
||||
toggleExt(show) {
|
||||
for (const ntxId in this.widgets) {
|
||||
this.widgets[ntxId].toggleExt(show && this.isNoteContext(ntxId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* widget.hasBeenAlreadyShown is intended for lazy loading of cached tabs - initial note switches of new tabs
|
||||
* are not executed, we're waiting for the first tab activation and then we update the tab. After this initial
|
||||
* activation further note switches are always propagated to the tabs.
|
||||
*/
|
||||
handleEventInChildren(name, data) {
|
||||
if (['noteSwitched', 'noteSwitchedAndActivated'].includes(name)) {
|
||||
// this event is propagated only to the widgets of a particular tab
|
||||
const widget = this.widgets[data.noteContext.ntxId];
|
||||
|
||||
if (widget && (widget.hasBeenAlreadyShown || name === 'noteSwitchedAndActivated')) {
|
||||
widget.hasBeenAlreadyShown = true;
|
||||
|
||||
return widget.handleEvent('noteSwitched', data);
|
||||
}
|
||||
else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
if (name === 'activeContextChanged') {
|
||||
const widget = this.widgets[data.noteContext.ntxId];
|
||||
|
||||
if (widget.hasBeenAlreadyShown) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else {
|
||||
widget.hasBeenAlreadyShown = true;
|
||||
|
||||
return widget.handleEvent(name, data);
|
||||
}
|
||||
} else {
|
||||
return super.handleEventInChildren(name, data);
|
||||
}
|
||||
}
|
||||
}
|
@ -422,8 +422,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
return true; // allow dragging to start
|
||||
},
|
||||
dragEnter: (node, data) => {
|
||||
console.log(data, node.data.noteType);
|
||||
|
||||
if (node.data.noteType === 'search') {
|
||||
return false;
|
||||
} else if (node.data.noteId === 'lb_root') {
|
||||
@ -565,7 +563,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
$span.append($refreshSearchButton);
|
||||
}
|
||||
|
||||
if (note.type !== 'search') {
|
||||
if (!['search', 'shortcut'].includes(note.type)) {
|
||||
const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>');
|
||||
|
||||
$span.append($createChildNoteButton);
|
||||
|
@ -5,6 +5,13 @@ const TPL = `<div class="note-detail-doc note-detail-printable">
|
||||
.note-detail-doc-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.note-detail-doc-content pre {
|
||||
background-color: var(--accented-background-color);
|
||||
border: 1px solid var(--main-border-color);
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="note-detail-doc-content"></div>
|
||||
|
@ -103,6 +103,28 @@ function getNewNoteTitle(parentNote) {
|
||||
return title;
|
||||
}
|
||||
|
||||
function getAndValidateParent(params) {
|
||||
const parentNote = becca.notes[params.parentNoteId];
|
||||
|
||||
if (!parentNote) {
|
||||
throw new Error(`Parent note "${params.parentNoteId}" not found.`);
|
||||
}
|
||||
|
||||
if (parentNote.type === 'shortcut') {
|
||||
throw new Error(`Shortcuts should not have child notes.`);
|
||||
}
|
||||
|
||||
if (['hidden', 'lb_root'].includes(parentNote.noteId)) {
|
||||
throw new Error(`Creating child notes into '${parentNote.noteId}' is not allowed.`);
|
||||
}
|
||||
|
||||
if (['lb_availableshortcuts', 'lb_visibleshortcuts'].includes(parentNote.noteId) && params.type !== 'shortcut') {
|
||||
throw new Error(`Creating child notes into '${parentNote.noteId}' is only possible for type 'shortcut'.`);
|
||||
}
|
||||
|
||||
return parentNote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Following object properties are mandatory:
|
||||
* - {string} parentNoteId
|
||||
@ -121,11 +143,7 @@ function getNewNoteTitle(parentNote) {
|
||||
* @return {{note: Note, branch: Branch}}
|
||||
*/
|
||||
function createNewNote(params) {
|
||||
const parentNote = becca.notes[params.parentNoteId];
|
||||
|
||||
if (!parentNote) {
|
||||
throw new Error(`Parent note "${params.parentNoteId}" not found.`);
|
||||
}
|
||||
const parentNote = getAndValidateParent(params);
|
||||
|
||||
if (params.title === null || params.title === undefined) {
|
||||
params.title = getNewNoteTitle(parentNote);
|
||||
|
Loading…
x
Reference in New Issue
Block a user