mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-27 08:16:40 +01:00 
			
		
		
		
	basic support for custom widgets
This commit is contained in:
		
							
								
								
									
										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 => { | ||||||
| appContext.start(); |     const desktopLayout = new DesktopLayout(widgetBundles); | ||||||
|  |  | ||||||
|  |     appContext.setLayout(desktopLayout); | ||||||
|  |     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); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user