Help debugging Sequence Diagram plugin

AI-generated summary

Help debugging Sequence Diagram plugin

User nskerl is developing a plugin to support simple sequence diagrams in posts using the JS Sequence Diagram library. The plugin uses replaceBlock() to find the [seqdiagram] tag and push its contents into a div needed by the diagram library. However, an error is thrown in the console: “Uncaught TypeError: e.stop.exec is not a function”.

Debugging Progress

nskerl made progress by removing replaceBlock() and using addPreProcessor() instead. However, issues persist, including a debounce effect that causes the preview window to fire decorateCooked on every other keystroke.

Advice and Solutions

User eviltrout suggested looking at the details dialect as a reference. eviltrout identified the problem as a global regular expression in the whitelist, which was causing the issue with every other keystroke. Changing to Discourse.Markdown.whiteListTag('div', 'class', 'discourse-sequence-diagram'); fixed the issue.

Rendering Issues

nskerl reported differences in rendering between the editor preview and cooked content, with the SVG output having different dimensions. User zogstrip suggested looking at the CookedPostProcessor class, which may be acting on the SVG. eviltrout requested more details on how the post processor is affecting the SVG.

Performance Issues

nskerl reported that the plugin is slow while typing in the preview editor due to the expensive call to the JS library that performs the markup-to-SVG transform. eviltrout suggested throttling the call and caching the result to improve performance. However, this may be difficult to implement. A future solution may involve moving the rendering pipeline to emit virtual DOM nodes, which would allow for more control over rendering.

I am trying to cobble together a plugin that will support simple sequence diagrams in posts.

For example, this:

[seqdiagram]
A->B:one
B->A:two
[/seqdiagram]

Would result in this:

The JS Sequence Diagram library handles the text–>svg function, so my first attempt is to simply wrap that library within Discourse.

Based on examples found here I put together this plugin: GitHub - nskerl/discourse-sequence-diagram · GitHub

Here I am using replaceBlock() to find the [seqdiagram] tag and push its contents into the div needed by the diagram lib.

    //assets/javascripts/sequence_diagram_dialect.js
    (function() {
      Discourse.Dialect.replaceBlock({
        start: /(\[seqdiagram\])([\s\S]*)/igm,
        stop: '[/seqdiagram]',
        rawContents: true,
        emitter: function(contents) {
    		return ['p', ['div', { class: 'discourse-sequence-diagram'}].concat(contents)];
        }
      });
      Discourse.Markdown.whiteListTag('div', 'class', /^discourse-sequence-diagram$/igm);
    })();

Then, in my initializer I am calling the lib to do the text to diagram transform:

import { withPluginApi } from 'discourse/lib/plugin-api';

    export default {
    	name: 'discourse-sequence-diagram',
    	initialize() {	
    		withPluginApi('0.1', api => {
    			api.decorateCooked($elem => $elem.find("div.discourse-sequence-diagram").sequenceDiagram({theme: 'hand'}));
    		});
    	}
    }

When I type in the opening [seqdiagram] tag I see the following error thrown in the console: “Uncaught TypeError: e.stop.exec is not a function”

It appears its coming from this section of code in replaceBlock():

Any help in debugging this in much appreciated! I’d also like to know how others would implement this feature; perhaps a plugin is not the place?

I am confident the other areas of the plugin are setup ok-- if I swap the call from replaceBlock to inlineBetween I can get svg output, but only on a single line and its a bit wonky with spaces.

I managed to make some progress by removing replaceBlock and simply doing the transform myself in the dialect.

I also switched to addPreProcessor() and this seems to work ok for the preview window, but the main post still has some problems:

There also seems to be some debounce affect that causes my preview window to fire decorateCooked on every other keystroke?

Any guidance is appreciated.

Any advice or examples we can offer here @eviltrout?

Thanks!

@eviltrout let me know if you have any plugins that I can look to for guidance. I know you recently made a lot of changes to the rendering engine, its possible I am following out-of-date plugin examples.

The details dialect seems quite similar to this. It uses addPreProcessor as well and it seems to work.

I might be able to help debug further if you share the code so far in a public place such as a github repo.

Great, I posted the project here: GitHub - nskerl/discourse-sequence-diagram · GitHub

Thanks so much.

Looks like your problem is your regular expression in your whitelist was a global. Global regular expressions will retain state on repeated calls based on the last index, so every second call was failing.

An easy fix is to change it to:

Discourse.Markdown.whiteListTag('div', 'class', 'discourse-sequence-diagram');

wow, yea that would explain the every other keystroke issue. Thanks, ill try this tonight.

Thanks again, that fixed the first issue.

Do you have any idea what would cause the rendering to be different between the editor preview (which looks good) and the cooked content? It doesnt look like tags are being stripped anywhere that I can see; the svg output simply has different dimensions when viewed in the editor vs. the cooked. When we hit Reply is the content re-rendered on the backend or should it take the rendered html from the editor?

Editor:


Cooked:

Have a look at the CookedPostProcessor class.

Doesn’t that only act on <img> tags? It does look suspect though, will continue researching

@eviltrout Is there a way to prevent the post processor from acting on the resulting svg?

Can you provide more details? How is it acting on the SVG? An example would be good.

@eviltrout See the screenshots above, the preview editor is fine but when the content is viewed in the main post the image (svg) generated by the js plugin is reduced in size. You can see the diagram itself is squashed.

@eviltrout Thanks for the help. I managed to get this “working” tonight… though it is incredibly slow while typing in the preview edit.The call to the js library that performs the markup–>svg transform is obviously expensive.

At this point I think my strategy is flawed, as I can’t be rebuilding the svg output on every keystroke, even if the diagram markup isn’t modified. Perhaps I can throttle the call somehow and only rebuild the diagram output after some small delay in keystrokes?

Any ideas to improve are much appreciated!

Latest code is here: GitHub - nskerl/discourse-sequence-diagram · GitHub

That sounds like a good idea, but it might be difficult. I’m not sure if any other plugin I’ve experienced has introduced a throttle on render, but since you’re just calling out to Javascript code it should be possible to check the last time you rendered it and return a cached result if it hasn’t changed right?

In the future we’d like to move our rendering pipeline to emit virtual dom nodes which are diffed in the preview. That way we could control when something is re-rendered or not. But that’s unfortunately a while out.