mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
basic support for custom widgets
This commit is contained in:
parent
8ae78a9e23
commit
173030e02e
14
package-lock.json
generated
14
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"version": "0.40.5",
|
"version": "0.40.6",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2079,9 +2079,9 @@
|
|||||||
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
|
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
|
||||||
},
|
},
|
||||||
"dayjs": {
|
"dayjs": {
|
||||||
"version": "1.8.22",
|
"version": "1.8.23",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.22.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.23.tgz",
|
||||||
"integrity": "sha512-N8IXfxBD62Y9cKTuuuSoOlCXRnnzaTj1vu91r855iq6FbY5cZqOZnW/95nUn6kJiR+W9PHHrLykEoQOe6fUKxQ=="
|
"integrity": "sha512-NmYHMFONftoZbeOhVz6jfiXI4zSiPN6NoVWJgC0aZQfYVwzy/ZpESPHuCcI0B8BUMpSJQ08zenHDbofOLKq8hQ=="
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@ -2590,9 +2590,9 @@
|
|||||||
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA=="
|
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA=="
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "9.0.0-beta.7",
|
"version": "9.0.0-beta.9",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.7.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.9.tgz",
|
||||||
"integrity": "sha512-IHWBLDUjlctyzSY1cXT9b2xdUFACiwWXV38WSkIbW4Ykd2fsNxn2ESRK/JUSby1oQZL6z4fUQRUOrg/gzuoV4Q==",
|
"integrity": "sha512-jVt+UL22S+uRiHkt2piAoVduL/n7UCTRKbp0c42kq1+Q36BVDu4j2HrePGX1Zilmnk91j8a6AqQ/x0dv6LkDnA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
"ws": "7.2.3"
|
"ws": "7.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "9.0.0-beta.7",
|
"electron": "9.0.0-beta.9",
|
||||||
"electron-builder": "22.4.1",
|
"electron-builder": "22.4.1",
|
||||||
"electron-packager": "14.2.1",
|
"electron-packager": "14.2.1",
|
||||||
"electron-rebuild": "1.10.1",
|
"electron-rebuild": "1.10.1",
|
||||||
|
@ -67,6 +67,7 @@ import ProtectedSessionTypeWidget from "./widgets/type_widgets/protected_session
|
|||||||
import BookTypeWidget from "./widgets/type_widgets/book.js";
|
import BookTypeWidget from "./widgets/type_widgets/book.js";
|
||||||
import contextMenu from "./services/context_menu.js";
|
import contextMenu from "./services/context_menu.js";
|
||||||
import DesktopLayout from "./widgets/desktop_layout.js";
|
import DesktopLayout from "./widgets/desktop_layout.js";
|
||||||
|
import bundleService from "./services/bundle.js";
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
require('electron').ipcRenderer.on('globalShortcut', async function(event, actionName) {
|
require('electron').ipcRenderer.on('globalShortcut', async function(event, actionName) {
|
||||||
@ -80,8 +81,12 @@ $('[data-toggle="tooltip"]').tooltip({
|
|||||||
|
|
||||||
macInit.init();
|
macInit.init();
|
||||||
|
|
||||||
appContext.setLayout(new DesktopLayout());
|
bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
||||||
|
const desktopLayout = new DesktopLayout(widgetBundles);
|
||||||
|
|
||||||
|
appContext.setLayout(desktopLayout);
|
||||||
appContext.start();
|
appContext.start();
|
||||||
|
});
|
||||||
|
|
||||||
noteTooltipService.setupGlobalTooltip();
|
noteTooltipService.setupGlobalTooltip();
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import server from "./server.js";
|
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
import bundleService from "./bundle.js";
|
import bundleService from "./bundle.js";
|
||||||
import DialogCommandExecutor from "./dialog_command_executor.js";
|
import DialogCommandExecutor from "./dialog_command_executor.js";
|
||||||
@ -18,10 +17,10 @@ class AppContext extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
options.load(await server.get('options'));
|
|
||||||
|
|
||||||
this.showWidgets();
|
this.showWidgets();
|
||||||
|
|
||||||
|
await Promise.all([treeCache.initializedPromise, options.initializedPromise]);
|
||||||
|
|
||||||
this.tabManager.loadTabs();
|
this.tabManager.loadTabs();
|
||||||
|
|
||||||
setTimeout(() => bundleService.executeStartupBundles(), 2000);
|
setTimeout(() => bundleService.executeStartupBundles(), 2000);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import ScriptContext from "./script_context.js";
|
import ScriptContext from "./script_context.js";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
|
import treeCache from "./tree_cache.js";
|
||||||
|
|
||||||
async function getAndExecuteBundle(noteId, originEntity = null) {
|
async function getAndExecuteBundle(noteId, originEntity = null) {
|
||||||
const bundle = await server.get('script/bundle/' + noteId);
|
const bundle = await server.get('script/bundle/' + noteId);
|
||||||
@ -29,8 +30,40 @@ async function executeStartupBundles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getWidgetBundlesByParent() {
|
||||||
|
const scriptBundles = await server.get("script/widgets");
|
||||||
|
|
||||||
|
const byParent = {};
|
||||||
|
|
||||||
|
for (const bundle of scriptBundles) {
|
||||||
|
|
||||||
|
let widget;
|
||||||
|
|
||||||
|
try {
|
||||||
|
widget = await executeBundle(bundle);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error("Widget initialization failed: ", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!widget.getParentWidget) {
|
||||||
|
console.log(`Custom widget does not have mandatory 'getParent()' method defined`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentWidgetName = widget.getParentWidget();
|
||||||
|
|
||||||
|
byParent[parentWidgetName] = byParent[parentWidgetName] || [];
|
||||||
|
byParent[parentWidgetName].push(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
return byParent;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
executeBundle,
|
executeBundle,
|
||||||
getAndExecuteBundle,
|
getAndExecuteBundle,
|
||||||
executeStartupBundles
|
executeStartupBundles,
|
||||||
|
getWidgetBundlesByParent
|
||||||
}
|
}
|
@ -282,7 +282,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
* @method
|
* @method
|
||||||
* @returns {NoteShort} active note (loaded into right pane)
|
* @returns {NoteShort} active note (loaded into right pane)
|
||||||
*/
|
*/
|
||||||
this.getActiveTabNote = appContext.tabManager.getActiveTabNote;
|
this.getActiveTabNote = () => appContext.tabManager.getActiveTabNote();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||||
@ -296,7 +296,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
* @method
|
* @method
|
||||||
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||||
*/
|
*/
|
||||||
this.getActiveTabNotePath = appContext.tabManager.getActiveTabNotePath;
|
this.getActiveTabNotePath = () => appContext.tabManager.getActiveTabNotePath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
|
|
||||||
const loadListeners = [];
|
|
||||||
|
|
||||||
class Options {
|
class Options {
|
||||||
|
constructor() {
|
||||||
|
this.initializedPromise = server.get('options').then(data => this.load(data));
|
||||||
|
}
|
||||||
|
|
||||||
load(arr) {
|
load(arr) {
|
||||||
this.arr = arr;
|
this.arr = arr;
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,6 @@ export default class TabManager extends Component {
|
|||||||
async loadTabs() {
|
async loadTabs() {
|
||||||
const openTabs = options.getJson('openTabs') || [];
|
const openTabs = options.getJson('openTabs') || [];
|
||||||
|
|
||||||
await treeCache.initializedPromise;
|
|
||||||
|
|
||||||
// if there's notePath in the URL, make sure it's open and active
|
// if there's notePath in the URL, make sure it's open and active
|
||||||
// (useful, among others, for opening clipped notes from clipper)
|
// (useful, among others, for opening clipped notes from clipper)
|
||||||
if (window.location.hash) {
|
if (window.location.hash) {
|
||||||
|
@ -9,6 +9,8 @@ class BasicWidget extends Component {
|
|||||||
style: ''
|
style: ''
|
||||||
};
|
};
|
||||||
this.classes = [];
|
this.classes = [];
|
||||||
|
|
||||||
|
this.position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
id(id) {
|
id(id) {
|
||||||
@ -87,6 +89,10 @@ class BasicWidget extends Component {
|
|||||||
return this.$widget.is(":visible");
|
return this.$widget.is(":visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPosition() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
if (this.$widget) {
|
if (this.$widget) {
|
||||||
this.$widget.remove();
|
this.$widget.remove();
|
||||||
|
@ -36,6 +36,14 @@ export default class Component {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addChildren(components = []) {
|
||||||
|
for (const component of components) {
|
||||||
|
this.child(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return {Promise} */
|
/** @return {Promise} */
|
||||||
handleEvent(name, data) {
|
handleEvent(name, data) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
@ -99,6 +99,10 @@ const RIGHT_PANE_CSS = `
|
|||||||
</style>`;
|
</style>`;
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
constructor(customWidgets) {
|
||||||
|
this.customWidgets = customWidgets;
|
||||||
|
}
|
||||||
|
|
||||||
getRootWidget(appContext) {
|
getRootWidget(appContext) {
|
||||||
appContext.mainTreeWidget = new NoteTreeWidget();
|
appContext.mainTreeWidget = new NoteTreeWidget();
|
||||||
|
|
||||||
@ -144,6 +148,7 @@ export default class DesktopLayout {
|
|||||||
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
|
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
|
||||||
.child(new TabCachingWidget(() => new SimilarNotesWidget()))
|
.child(new TabCachingWidget(() => new SimilarNotesWidget()))
|
||||||
.child(new TabCachingWidget(() => new WhatLinksHereWidget()))
|
.child(new TabCachingWidget(() => new WhatLinksHereWidget()))
|
||||||
|
.addChildren(this.customWidgets['right-pane'])
|
||||||
)
|
)
|
||||||
.child(new SidePaneToggles().hideInZenMode())
|
.child(new SidePaneToggles().hideInZenMode())
|
||||||
);
|
);
|
||||||
|
@ -11,6 +11,17 @@ export default class FlexContainer extends BasicWidget {
|
|||||||
this.attrs.style = `display: flex; flex-direction: ${direction};`;
|
this.attrs.style = `display: flex; flex-direction: ${direction};`;
|
||||||
|
|
||||||
this.children = [];
|
this.children = [];
|
||||||
|
|
||||||
|
this.positionCounter = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
child(component) {
|
||||||
|
super.child(component);
|
||||||
|
|
||||||
|
component.position = this.positionCounter;
|
||||||
|
this.positionCounter += 10;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
|
@ -29,8 +29,8 @@ async function run(req) {
|
|||||||
return { executionResult: result };
|
return { executionResult: result };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getStartupBundles() {
|
async function getBundlesWithLabel(label, value) {
|
||||||
const notes = await attributeService.getNotesWithLabel("run", "frontendStartup");
|
const notes = await attributeService.getNotesWithLabel(label, value);
|
||||||
|
|
||||||
const bundles = [];
|
const bundles = [];
|
||||||
|
|
||||||
@ -45,6 +45,14 @@ async function getStartupBundles() {
|
|||||||
return bundles;
|
return bundles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getStartupBundles() {
|
||||||
|
return await getBundlesWithLabel("run", "frontendStartup");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getWidgetBundles() {
|
||||||
|
return await getBundlesWithLabel("widget");
|
||||||
|
}
|
||||||
|
|
||||||
async function getRelationBundles(req) {
|
async function getRelationBundles(req) {
|
||||||
const noteId = req.params.noteId;
|
const noteId = req.params.noteId;
|
||||||
const note = await repository.getNote(noteId);
|
const note = await repository.getNote(noteId);
|
||||||
@ -84,6 +92,7 @@ module.exports = {
|
|||||||
exec,
|
exec,
|
||||||
run,
|
run,
|
||||||
getStartupBundles,
|
getStartupBundles,
|
||||||
|
getWidgetBundles,
|
||||||
getRelationBundles,
|
getRelationBundles,
|
||||||
getBundle
|
getBundle
|
||||||
};
|
};
|
@ -225,6 +225,7 @@ function register(app) {
|
|||||||
apiRoute(POST, '/api/script/exec', scriptRoute.exec);
|
apiRoute(POST, '/api/script/exec', scriptRoute.exec);
|
||||||
apiRoute(POST, '/api/script/run/:noteId', scriptRoute.run);
|
apiRoute(POST, '/api/script/run/:noteId', scriptRoute.run);
|
||||||
apiRoute(GET, '/api/script/startup', scriptRoute.getStartupBundles);
|
apiRoute(GET, '/api/script/startup', scriptRoute.getStartupBundles);
|
||||||
|
apiRoute(GET, '/api/script/widgets', scriptRoute.getWidgetBundles);
|
||||||
apiRoute(GET, '/api/script/bundle/:noteId', scriptRoute.getBundle);
|
apiRoute(GET, '/api/script/bundle/:noteId', scriptRoute.getBundle);
|
||||||
apiRoute(GET, '/api/script/relation/:noteId/:relationName', scriptRoute.getRelationBundles);
|
apiRoute(GET, '/api/script/relation/:noteId/:relationName', scriptRoute.getRelationBundles);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user