Help debugging Sequence Diagram plugin

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: https://github.com/nskerl/discourse-sequence-diagram

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.

2 Likes

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.

1 Like

Great, I posted the project here: https://github.com/nskerl/discourse-sequence-diagram

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');
2 Likes

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:

1 Like

Have a look at the CookedPostProcessor class.

https://github.com/discourse/discourse/blob/master/lib/cooked_post_processor.rb#L100-L110

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

https://github.com/discourse/discourse/blob/master/lib/cooked_post_processor.rb#L72-L83

@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: https://github.com/nskerl/discourse-sequence-diagram/

1 Like

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.