`dot_ix`: Toy web app to generate interactive graphs

Heya, I made this:

and here’s some links:

The video doesn’t show it, but you can tag nodes with a tag, and when you click on the tag in the tag legend, it highlights the node.

It’s very much a hacked together project right now, but I thought people would find it fun.

enjoy :v:

2 Likes

Very nice. It helped me ses some of the opportunities and challenges of interactive Graphviz.
thanks

1 Like

Thank you :bowing_man:!

I intend to use it to visualize automation processes – when a step is running, style it blue + animate it; and one can click on the node to bring up more information / show actions that can be done – example animation.

Is the node tagging accomplished by embedding the tag name in the css classes? Or is there some other mechanism?

Yeap, I’m using tailwind, and for the node tagging, it specifically needs:

  1. The tags to appear in the <svg> before the nodes (or clusters) that need to be highlighted.

    The General sibling combinator used later (step 4) only knows about sibling nodes that occur before this node in the DOM. That’s why we need to force tags to render first.

    This is done by rendering the tags as clusters (graphviz renders clusters before nodes), and placing the tag-clusters above the node clusters in the graph.

    It doesn’t always work, I think GraphViz may be doing some parallel tasks, because the tag-clusters sometimes appear after the node clusters in the SVG.

  2. Make tag nodes focusable.

    Add tabindex=0 to every tag cluster – this is actually after GraphViz dot has been called, and is a string replacement (GraphViz doesn’t appear to let you add it).

  3. Add this css class to tag clusters: peer/{tag_id}

  4. For the nodes that are tagged, add classes to style them when their sibling peer is focused.

    e.g.:

    peer-focus/{tag_id}:[&>path]:fill-lime-200
    peer-focus/{tag_id}:[&>path]:stroke-lime-500
    
  5. Use the tailwind non-production JS library to process elements on the page to produce the necessary CSS.

    I haven’t researched very far, but I think it’s “non-production” because I think the production use-case is meant to be “you have the static CSS for your whole site”, but that requires knowing the tailwind classes up front, which we don’t since we’re generating them based on input.

    I do plan to take tailwind classes in instead of hardcoding it into the app, just slowly edging on my projects.

@azriel That seems very complicated and unreliable… making me think there has go to be a better way?

Are you starting with graphviz syntax? What if you started with a JSON model that compiled to graphviz syntax in a more deterministic way where you could then know how graphviz would give IDs to certain elements in the svg, and then use some JS to post-process the graphviz svg output (using the original JSON model) to add the interactivity you want? JS would also give you more options for interactivity than css alone.

What do you think?

That seems very complicated and unreliable… making me think there has go to be a better way?

At least in computing, we really need more information than “seems”, because what you describe, is very close what I’m doing (just different markup / language – yaml + rust instead of json + js).

See the video in the first post – the IDs are deterministic from the input yaml, and (minimal) post processing is done – only adding tab index.

Also, I try to aim for minimal javascript, as that means:

  • interactivity doesn’t need the client to have javascript enabled
  • programmatic access of the information is easier – javascript isn’t run to augment more information, so http clients can just run and have the same info, instead of “ahh this is only useful in a browser”.

My apologies. I used the word “seems” to indicate that my understanding may be incorrect; I did not mean for it to be a vague and thus unhelpful critique. Honestly, I had a hard time following your description and the two “red flags” in my mind were that it seemed to rely on string replacements and you said “It doesn’t always work”. It also looks like it relies on implicit knowledge on things outside of your control, like the need to specify tag-clusters above node clusters to achieve the desired result. What if that changes? and how many more examples of little things like that does one need to keep in mind to make progress in adding interactivity?

I don’t know your specific use case, but I thought I’d share some thoughts in case they were helpful in pointing in a better direction (if there even is one). I may well have misunderstood.

I have struggled with adding interactivity to graphviz diagrams in my own app, so my comments above were my own reservations about adopting a similar approach. Our use cases might be very different though as mine is a web app, so I take the browser for granted.

Also, you mention minimal javascript / not relying on a browser, but it seems you do use some javascript and css; if so, what environment is your client using to access this feature that supports minimal javascript and css and is not a browser?

Also, using CSS for interactivity seems like a road where you will probably hit a dead end where the behavior you want is only available in JS and not CSS (as I believe interactivity was JS’s primary purpose and not the main purpose of CSS). So you might end up with more JS anyways, in which case the code might be more complicated having some things achieved via complex css rules and others via JS.

Also, you mention that you are doing something very close to what I suggested, which I didn’t notice at first, but did now. In that case, why not do more with Rust and less with CSS? Is the way Rust interacts with the svg just as capable as JS interacting with the svg via the DOM? Or is it more like string manipulation?

My comments are meant to be collaborative, my apologies again if they come off as critical.

Heya, apology accepted, and I feel I should say sorry as well :bowing_man: – my last post feels rather cold.

So, let’s try and clarify, I’ll start with some goals:

  1. Let’s make a diagram generator that helps to communicate information clearly

    Showing layers of detail, highlighting relevant information using colour.

  2. It should be easy to “just use”, both through typing by hand and programmatically

  3. It should be robust – remove / reduce opportunity for errors, or handle them gracefully

  4. It should be predictable

With that in mind (and quotations out of order):

minimal javascript / not relying on a browser

For rendering a nice diagram, it would be really nice from a user of this tool, to be able to go:

<img src="http://dot_ix/?src=the_diagram_source" />

and have it render!

Though having things in an <img /> tag means, we lose the fonts / interactivity, even if it’s CSS only – I only realized this while typing this answer.

What I’ve just discovered is, having:

<object data="diagram.svg" type="image/svg+xml"></object>

with diagram.svg containing both the SVG and the styles inlined into the SVG, you can get the interactivity through one GET request! (easy to “just use”).

what environment is your client using to access this feature that supports minimal javascript and css and is not a browser?

It’s… it’s… a browser :grimacing:! But from a web page that I don’t control. This would be:

  • users who turn off javascript in their browser
  • users who curl for the SVG, then push it somewhere else – don’t require them to also ship the javascript / CSS, because they may only be allowed to upload SVGs.
  • other website developers who don’t want to include random javascript on their page – saves loading time.

it seems you do use some javascript and css

I do use some JS / WASM for that app; I think the use cases I want to support for both myself and others are:

  • 100% client side rendering – client side logic renders and styles the whole graph.

    This is good for people with poor internet connections, or when you have to turn off mobile data. At least if you load the page once, you can save it. Browser storage would be very useful here as well.

  • 100% server side rendering – Server logic renders and styles the whole graph.

    Good for scripts / other websites to call as a service.

using CSS for interactivity seems like a road where you will probably hit a dead end where the behavior you want is only available in JS and not CSS

why not do more with Rust and less with CSS?

I conclude that you speak from a position of experience, and at least in web development, I come from a position of inexperience (and don’t necessarily know what’s a bad idea).

I suspect I will end up doing more with Rust/JS – e.g. click on a node in the SVG, and show more details in a different element somewhere else on the page. And that really feels like a case not for CSS.

string replacements + little things

Definitely a red flag to me in general :smile:, and I’d certainly want to parse and edit structured values in memory than using string replacements in an unguaranteed format; just haven’t gotten there yet. Indeed, at this stage it’s still a toy project, and doesn’t satisfy the robustness criteria.

It doesn’t always work

If memory serves me right, this one’s to do with CSS combinator rules conflicting with each other. The generated SVG and stylesheet are consistent (robustness), but because the behaviour is:

  • CSS selector: If a previous sibling is focused, I should have this styling
  • Node C has a previous sibling A, and previous sibling B.
  • Node C has two CSS selectors, “style me as green when A is focused”, “style me as green when B is focused”
  • The unstated rules are “don’t style me as green when A is unfocused” (and same for B)
  • The engine somehow mixes the selectors and styles C half on (the stroke is green) and half off (the fill is not green), when I want it to be green for both the stroke and fill when either A or B are focused.

It really may be impossible to solve via CSS selector rules; I just hope that it is possible.

It also looks like it relies on implicit knowledge on things outside of your control, like the need to specify tag-clusters above node clusters to achieve the desired result.

Yeap this one needs some internal knowledge; a robust way would be to parse the SVG and re-outputing the SVG nodes in a certain order (based on ID / class). There’s also an idea to generate separate SVGs, then combine them (offsetting all the coordinates), which I may end up doing long term.

My comments are meant to be collaborative, my apologies again if they come off as critical.

Truth be told, your questions have piqued my motivation to pick this up again :smile:, so thank you.

Peace :v:

1 Like