mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +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>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>
|
<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>
|
<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") {
|
if (env === "frontend") {
|
||||||
const bundleService = (await import("../services/bundle.js")).default;
|
const bundleService = (await import("../services/bundle.js")).default;
|
||||||
await bundleService.getAndExecuteBundle(this.noteId);
|
return await bundleService.getAndExecuteBundle(this.noteId);
|
||||||
}
|
}
|
||||||
else if (env === "backend") {
|
else if (env === "backend") {
|
||||||
await server.post('script/run/' + this.noteId);
|
return await server.post('script/run/' + this.noteId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error(`Unrecognized env type ${env} for note ${this.noteId}`);
|
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 ws from "./ws.js";
|
||||||
import appContext from "./app_context.js";
|
import appContext from "./app_context.js";
|
||||||
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.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 BasicWidget from "../widgets/basic_widget.js";
|
||||||
import SpacedUpdate from "./spaced_update.js";
|
import SpacedUpdate from "./spaced_update.js";
|
||||||
|
|
||||||
@ -40,24 +39,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
/** @property {CollapsibleWidget} */
|
/** @property {CollapsibleWidget} */
|
||||||
this.CollapsibleWidget = CollapsibleWidget;
|
this.CollapsibleWidget = CollapsibleWidget;
|
||||||
|
|
||||||
/**
|
|
||||||
* @property {NoteContextAwareWidget}
|
|
||||||
* @deprecated use NoteContextAwareWidget instead
|
|
||||||
*/
|
|
||||||
this.TabAwareWidget = NoteContextAwareWidget;
|
|
||||||
|
|
||||||
/** @property {NoteContextAwareWidget} */
|
/** @property {NoteContextAwareWidget} */
|
||||||
this.NoteContextAwareWidget = NoteContextAwareWidget;
|
this.NoteContextAwareWidget = NoteContextAwareWidget;
|
||||||
|
|
||||||
/**
|
|
||||||
* @property {NoteContextCachingWidget}
|
|
||||||
* @deprecated use NoteContextCachingWidget instead
|
|
||||||
*/
|
|
||||||
this.TabCachingWidget = NoteContextCachingWidget;
|
|
||||||
|
|
||||||
/** @property {NoteContextAwareWidget} */
|
|
||||||
this.NoteContextCachingWidget = NoteContextCachingWidget;
|
|
||||||
|
|
||||||
/** @property {BasicWidget} */
|
/** @property {BasicWidget} */
|
||||||
this.BasicWidget = BasicWidget;
|
this.BasicWidget = BasicWidget;
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ export default class Component {
|
|||||||
|
|
||||||
/** @returns {Promise} */
|
/** @returns {Promise} */
|
||||||
handleEvent(name, data) {
|
handleEvent(name, data) {
|
||||||
|
try {
|
||||||
const callMethodPromise = this.initialized
|
const callMethodPromise = this.initialized
|
||||||
? this.initialized.then(() => this.callMethod(this[name + 'Event'], data))
|
? this.initialized.then(() => this.callMethod(this[name + 'Event'], data))
|
||||||
: this.callMethod(this[name + 'Event'], data);
|
: this.callMethod(this[name + 'Event'], data);
|
||||||
@ -53,6 +54,12 @@ export default class Component {
|
|||||||
? Promise.all([callMethodPromise, childrenPromise])
|
? Promise.all([callMethodPromise, childrenPromise])
|
||||||
: (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} */
|
/** @returns {Promise} */
|
||||||
triggerEvent(name, data) {
|
triggerEvent(name, data) {
|
||||||
|
@ -33,6 +33,37 @@ export default class ShortcutContainer extends FlexContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const shortcut of await visibleShortcutsRoot.getChildNotes()) {
|
for (const shortcut of await visibleShortcutsRoot.getChildNotes()) {
|
||||||
|
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();
|
||||||
|
|
||||||
|
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")) {
|
if (shortcut.getLabelValue("command")) {
|
||||||
this.child(new ButtonWidget()
|
this.child(new ButtonWidget()
|
||||||
.title(shortcut.title)
|
.title(shortcut.title)
|
||||||
@ -43,6 +74,21 @@ export default class ShortcutContainer extends FlexContainer {
|
|||||||
.title(shortcut.title)
|
.title(shortcut.title)
|
||||||
.icon(shortcut.getIcon())
|
.icon(shortcut.getIcon())
|
||||||
.onClick(() => appContext.tabManager.openTabWithNoteWithHoisting(shortcut.getRelationValue('targetNote'), true)));
|
.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 {
|
} else {
|
||||||
const builtinWidget = shortcut.getLabelValue("builtinWidget");
|
const builtinWidget = shortcut.getLabelValue("builtinWidget");
|
||||||
|
|
||||||
@ -76,12 +122,6 @@ export default class ShortcutContainer extends FlexContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$widget.empty();
|
|
||||||
this.renderChildren();
|
|
||||||
|
|
||||||
this.handleEventInChildren('initialRenderComplete');
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesReloadedEvent({loadResults}) {
|
entitiesReloadedEvent({loadResults}) {
|
||||||
if (loadResults.getNoteIds().find(noteId => froca.notes[noteId]?.isLaunchBarConfig())
|
if (loadResults.getNoteIds().find(noteId => froca.notes[noteId]?.isLaunchBarConfig())
|
||||||
|| loadResults.getBranches().find(branch => branch.parentNoteId.startsWith("lb_"))
|
|| loadResults.getBranches().find(branch => branch.parentNoteId.startsWith("lb_"))
|
||||||
|
@ -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
|
return true; // allow dragging to start
|
||||||
},
|
},
|
||||||
dragEnter: (node, data) => {
|
dragEnter: (node, data) => {
|
||||||
console.log(data, node.data.noteType);
|
|
||||||
|
|
||||||
if (node.data.noteType === 'search') {
|
if (node.data.noteType === 'search') {
|
||||||
return false;
|
return false;
|
||||||
} else if (node.data.noteId === 'lb_root') {
|
} else if (node.data.noteId === 'lb_root') {
|
||||||
@ -565,7 +563,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
$span.append($refreshSearchButton);
|
$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>');
|
const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>');
|
||||||
|
|
||||||
$span.append($createChildNoteButton);
|
$span.append($createChildNoteButton);
|
||||||
|
@ -5,6 +5,13 @@ const TPL = `<div class="note-detail-doc note-detail-printable">
|
|||||||
.note-detail-doc-content {
|
.note-detail-doc-content {
|
||||||
padding: 15px;
|
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>
|
</style>
|
||||||
|
|
||||||
<div class="note-detail-doc-content"></div>
|
<div class="note-detail-doc-content"></div>
|
||||||
|
@ -103,6 +103,28 @@ function getNewNoteTitle(parentNote) {
|
|||||||
return title;
|
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:
|
* Following object properties are mandatory:
|
||||||
* - {string} parentNoteId
|
* - {string} parentNoteId
|
||||||
@ -121,11 +143,7 @@ function getNewNoteTitle(parentNote) {
|
|||||||
* @return {{note: Note, branch: Branch}}
|
* @return {{note: Note, branch: Branch}}
|
||||||
*/
|
*/
|
||||||
function createNewNote(params) {
|
function createNewNote(params) {
|
||||||
const parentNote = becca.notes[params.parentNoteId];
|
const parentNote = getAndValidateParent(params);
|
||||||
|
|
||||||
if (!parentNote) {
|
|
||||||
throw new Error(`Parent note "${params.parentNoteId}" not found.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.title === null || params.title === undefined) {
|
if (params.title === null || params.title === undefined) {
|
||||||
params.title = getNewNoteTitle(parentNote);
|
params.title = getNewNoteTitle(parentNote);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user