Creating a Kroki plugin

I’m imagining a plugin to use images from Kroki, which is an omnibus text-to-diagram tool. I’m not ready to start coding it yet, but was thinking through how I might do it.

Obviously, this would be simple enough to do the same way PlantUML does it, online only. But I think we could do better than that. I think we should be able to cache the image generated and only create new ones when the underlying text is changed (and saved). This would mean that we would only need to be online when creating and saving such images, not when using them.

I’m picturing it as an entirely new tiddler type with multiple fields being used for the process, text, of course, but also stored, which caches a hash of the text and which we can can use to check for changes., and rendered which stores the SVG data returned by Kroki.

Add a new tiddler type for Kroki. No idea of a proper MIME type, but when triggered (by a save or at startup), it would

  • deflate and base64 encode the text field
  • check if the result matches the stored field
    • if it did, call the image/svg+xml format with the rendered field, rendering that
    • if not, and offline, render the text field as a formatted text block with some sort of warning that the data cannot be retrieved while offline, a remove any rendered property (or leave it to show the older value?)
    • if not, and online, call Kroki’s API with the value of the inputType field and this value
      • if this returns an error, display the error near the editing box
      • if the return is good, store the resulting value in the rendered field, and render that.

Also add a startup action that – if we’re online – scans for this type of tiddler without a rendered property and performs most of the above.

Major Problem: While this would be either an extension to ImageParser.js, or more likely an alternative to it, I don’t yet know how that’s called, and it looks quite possible that such functions do not have access to the tiddler fields. (If they do, it would have to be passed through the options parameter.)


An alternative to storing this information in the tiddler would be to have a single data tiddler (plain text or JSON) with keys of these hashes and values of the SVG text. I like this less; if that tiddler gets screwed up, all your images might be destroyed, but it might be simpler to set up. And it might let us just use a widget or macro instead of a new type.


Questions:

  • Does the above sound reasonable?
  • Where to do type parsers such as ImageParser.js get invoked?
  • Does the options object they’re supplied include the current tiddler or offer a way to get to it?
  • Is there a deflate tool built into the core, or would the plugin need to supply one?
1 Like

I wrote a thing along these lines like a year ago, and I’m pretty happy with it, for my personal use: kroki.json (19.8 KB)

Please check it out and let me know if it works OK for you.

4 Likes

README contents below:

This is a TiddlyWiki plugin that integrates the Kroki diagram web service into TiddlyWiki, allowing you to write diagrams as text tiddlers in suitable formats such as graphviz or plantuml, and have them rendered as SVG images in your Tiddlywiki.

While the plugin requires access to a Kroki instance for creating and editing diagrams, it doesn’t require that for showing the diagrams; this is because the plugin takes care of storing the SVG data in each diagram tiddler. From TiddlyWiki’s point of view, they are just SVG images and therefore, for visualizing the contents of your TiddlyWiki, you can rest assured that everything in it is self contained, without relying on external files or services.

1 Like

Oh, that’s perfect!

It works beautifully. I haven’t dug far into the implementation yet, but it’s clearly a little simpler than I was imagining. I thought I’d have to store two extra fields, but you get away with only one! Nice.

1 Like

Oh, I get it. You do store another field, but only in the case that we can’t get a current update (e.g., we’re offline.) Nice!

Thank you for sharing @jerojasro, that’s great to see.

Is the code available in a GitHub repo, or another online location?

3 Likes

hello; it wasn’t, but now it is: GitHub - jerojasro/tw-kroki

I was sort of afraid of publishing it, because I don’t really feel confident my implementation is “kosher” and extends TW in the right way… but there it is. Any comments/advice/PRs are more than welcome.

5 Likes

Thank you @jerojasro

The main thing is that it works, and the code is clear and readable. Anything beyond that is to some extent a luxury!

The main point that I noticed is that the <$kroki> widget makes HTTP requests via its render method. The usual problem with that pattern is that widgets are volatile; the refresh cycle causes widget to be deleted and recreated freely. I don’t think it matters much in this case because the callback from the request doesn’t appear to make assumptions about the widget still being in the tree.

An alternative approach would be to structure the code as a daemon that runs in the background looking for tiddlers that need to be processed by the Kroki server, dispatches the HTTP requests, and saves the results back to the originating tiddler.

The implementation could be similar to @saqimtiaz’s file upload plugin, which looks for image tiddlers that require uploading to a server, issues the HTTP request and then adjusts the tiddler once it has been successfully saved to the server.

3 Likes

I’m starting to try to learn about TW’s refresh cycle. Does the following mean that this wouldn’t be a problem?:

KrokiWidget.prototype.refresh = function(changedTiddlers) {
	return false;
};

Or is that just an irrelevancy?

1 Like

Widgets higher up the widget tree when refreshing can destroy and recreate all descendants, so the refresh problem remains.

For example if there is a surrounding $let widget that needs to refresh to update the value of a variable, it will destroy all of its children and they will be re-rendered to propagate the new variable value.

A good rule of thumb is:

  • long running or async tasks, send a widget message up the widget tree and have the handler for it attached to the root widget ($tw.rootWidget) which is outside the refresh cycle.
2 Likes

Ok, that makes sense. I’ll check the file upload plugin for an example, if it follows this pattern.

The File upload plugin actually demonstrates both the patterns mentioned here, a background daemon that uploads tiddlers matching a filter, as well as an affordance to send a message to the root widget to upload a particular tiddler.

If you have any further questions please feel free to ask.

2 Likes

Thank you for your help. I will look into this, and hopefully soon. But I’m back to working on my Periodic Table demo, and it will likely be a few days before I get the chance.

I will get to it though.

Your help – and your willingness to help further – is much appreciated!

2 Likes

Can you provide me with an example?

Why not mermaidjs? We have several mermaid plugins in the CPL.

while I don’t use it myself, kroki also includes/supports mermaid.

However, keep in mind that Kroki’s model is: “the software for generating all these diagrams is rather cumbersome to install/not necessarily available as JS modules, so let’s abstract all that complexity away behind an API, and use that from JS instead”

1 Like