Add more event hooks/subscriptions for plugins

When writing plugins it helps to be able to react to the app’s events, changes to the data, namely the Logseq primitives like blocks and pages. It would be great to subscribe to what blocks get loaded or changed on a page, for example.

It’s one thing if a plugin creates its own UI, but some plugins are designed to interact only with the main page/view. While the iframe isolation prohibits direct actions against the main UI there are still reasonable actions which can be taken. For example, my plugin toggles the visibility of completed to-dos in the main UI.

Basically, if the plugin knows what content has been rendered to the page, it can react to it by adding dynamic styles. This has proven completely workable except for the limitations and timing of what can be observed.

For example, the standard ready handler is called when logseq is ready but not after the initial page content is rendered. Therefore, if you call getCurrentPageBlocksTree too early you get null. And if you call this on the journal page, regardless of when, you get null. It should be possible to tap into some event on("journals:loaded") or on("page:blocks:loaded"). This would work similar to getCurrentPageBlocksTree except that it happens in response to events. And it’s clear some blocks are loaded lazily. Therefore, these events could fire at any time. For example, as you scroll down the journals page and new days scroll into view.

Polling is a wasteful hack because it requires a vigilant plugin which eats resources for no good reason. The app already knows when it’s loading (or updating) data. It should simply share these events and allow interested plugins to react.

Likewise, sometimes the current page gets edited. There is no on("page:change") hook or on("journal:change") hook. Likewise, to go finer grained, there could be a on("block:change") hook. The point is, these things are already happening in the app. All I’m asking is they be shared with a varied set of channels. If everything happens via on then you don’t need custom methods like onRouteChanged but rather on("route:change"). I believe, there is an emit build somewhere into the api. The pairing of emit and on along with custom events could make the Logseq plugin core very jQuery like. And this would allow for a lot of hacks (like polling) to be avoided.

Consider my plugin and what would need to be done to dynamically update the styles when updates are made to either a page or the master bottomless journal page. Right now, things seem harder than they need to be.

Likewise, I’m not sure how I would even know what got rendered in the linked references on the page I’m viewing. Again, my plugin has no UI. It merely augments the master UI by knowing what’s been added to the page and then it reacts by adding styles.

I realize that if I kept the plugin targeted to TODOs I could have simplified quite a few things, but the aim of the plugin was to permit some flexibility with what gets selected and how it gets rendered based on two possible states. Hooks to the rescue!

My plugin monitors incoming data and adds styles but tapping directly into the rendering pipeline would be far better.

hmm a workaround for your journal home page would would be this. you can just in the following order:

  1. getBlock
  2. get the parent
  3. Then use getPageBlockTree(parent.uuid)

Thanks for the feedback. Will investigate this.

Often component authors render classes and attributes (e.g. [data-tags='Book Tolkien']) too far down the DOM hierarchy. Since CSS doesn’t allow parent/grandparent selectors, one requires scripting (e.g. jQuery) to dynamically updates for stylistic purposes. If classes and attributes had been rendered on the uppermost parent and dynamically updated by the core app, it would be unnecessary for plugins to fill this gap. The plugin becomes minimal (perhaps toggling an attribute on the root node) and everything else relies on a custom stylesheet.