How to load and use viz-standalone (Graphviz)?

I know this is likely easy, but I have been stuck for days and cannot figure it out on my own with my current meager knowledge.

The question has two parts:

  • specific
    • I would like to generate diagrams dynamically (and locally) within logseq with GraphViz. To that end, I would like to load Viz.js and use it with kits.
      • Is this the right approach? if anyone would provide instructions or pointers, I would appreciate it.
  • general
    • what is the right/best way to load external libraries such as this one, which are not always needed, but may be needed by a block of code?

Prior knowledge:

  • I have used GraphViz before, but in the command line. I have not used Viz.js before outside of the WYSIWYG editor they provide at https://viz-js.com/
  • I have loaded python libraries with kits and pyodide (but cannot figure out how to do the same with js)
  • essentially zero javascript. At most, I can read or very minorly modify existing code.

What I have tried so far:

  • look for an existing solution through website searches and in the forums.
  • import { instance } from "../assets/custom/vizjs-3.8.0/viz-standalone.js"; in a block and run it with kits using {{evalparent}}
    • Error: Uncaught (in promise) SyntaxError: Cannot use import statement outside a module. I went on an exploration about how to solve this, but most about the explanation on modules, and react and node.js specific cases escape me at this time.
  • load viz-standalone.js with the custom-js.url: line in config.edn. I am unsure if this loads the script and I do know know how to use it or if it never loads. Searching in the sources tab of the electron inspector does not show the file viz-standalone.js anywhere. When I run the examples, in Viz.js - API, the console returns ReferenceError: Viz is not defined or ReferenceError: instance is not defined, which tells me it either did not load or I am invoking it wrong.
    •   instance().then(viz => {
        const svg = viz.renderSVGElement("digraph { a -> b }");
        document.getElementById("graph").appendChild(svg);
        });
      
    •    Viz.instance().then(function(viz) {
         var svg = viz.renderSVGElement("digraph { a -> b }");
         document.getElementById("graph").appendChild(svg);
         });
      
  • create a skeleton plugin and load viz-standalone.js with it from index.html with <script type="module" crossorigin src="./assets/viz-standalone-3.8.0.js"></script>
    • The plugin loads, although with a warning that it takes long to load and will delay app startup time (ideally, it would only load on use). However, I am unable to generate a graph with either of the two examples above. The errors are the same as when with the custom-js: in config.edn option.

Thank you very much in advance to anyone who can help : ).

  • Can do it e.g. with this:
    • load it:
      const srcGraphviz = "https://rsms.me/graphviz/graphviz.js"
      import(srcGraphviz).then(()=>logseq.Module.Msg.success("Graphviz ready"))
      
      • The source can be from elsewhere.
      • The import statement should be with parentheses.
    • use it:
      graphviz.layout(`
        digraph { a -> b }
      `).then( (svg)=>{
        document.getElementById("graph").appendChild(svg)
      })
      
  • The rest (integration with kits, conditional loading etc.) depends on how you plan to use it.
1 Like

Thanks for the help. I am sure this works, but I had no success. Here is what I tried:

  • Failure 1:
    • Code in code blocks in the journal, with kits:
      •  const srcGraphviz = "https://rsms.me/graphviz/graphviz.js" // after testing I will replace this with path to local copy of the file
         import(srcGraphviz).then(()=>logseq.Module.Msg.success("Graphviz ready"))
        
        • {{evalparent}}
      • graphviz.layout(`
        digraph { a -> b }
        `).then( (svg)=>{
        document.getElementById("graph").appendChild(svg)
        })
        
        • {{evalparent}}
    • Failure mode:
      • Output: => undefined
  • Failure 2:
    • Code in custom.js
      • // custom code to load Graphviz (source: https://discuss.logseq.com/t/how-to-load-and-use-vizjs-standalone-graphviz/28629)
        const srcGraphviz = "https://rsms.me/graphviz/graphviz.js" // after testing I will replace this with path to local copy of the file
        import(srcGraphviz).then(()=>logseq.Module.Msg.success("Graphviz ready"))
        
    • Code in code blocks in the journal, with kits:
      • graphviz.layout(`
        digraph { a -> b }
        `).then( (svg)=>{
        document.getElementById("graph").appendChild(svg)
        })
        
        • {{evalparent}}
    • Failure mode:
      • no Graphviz ready message on application startup.
      • output of {{evalparent}} stuck at ...running...

Also a question on the code of https://rsms.me/graphviz/graphviz.js :

  • I do not understand it fully, but it seems too short and it looks like it might connect to an online tool at https://rsms.me/graphviz to generate the image.
    • Is that right?
      • If so, I could still test it, but would need to explore a way to use vizjs-standalone.js instead.

Thanks again for all the help!

  • The undefined output is normal.
  • Stuck at ...running... is normal whenever an error is not handled.
    • Open the console to check for error messages.
  • The lack of Graphviz ready message is not normal and needs debugging.
    • To me all the scenarios succeed in loading.
  • :custom-js-url in config.edn is irrelevant (only to change the path of custom.js)
  • You can also paste viz-standalone.js directly inside custom.js
    • Then use this:
        Viz.instance().then(function(viz) {
          var svg = viz.renderSVGElement("digraph { a -> b }");
          document.getElementById("graph").appendChild(svg);
        });
      
      • These example codes assume that there is an HTML element of id #graph
        • This is not the case in typical Logseq, so you should provide your own element.
        • This is relevant only after loading. Missing element should not prevent loading.
1 Like

This worked perfectly! Thank you so very much for all your help @mentaloid.

I would expect the previous options to work for me as well, but what do I know. I’ll try to figure it out. In any case, my use case is fully covered now. Thank you!

If you can, please share what you do with this (screenshots or video). I’m curious!

I’m not sure I can share much of general interest. I generate flow diagrams and graphs that allow me to more visually interpret relations between topics or steps in a process and their dependencies. I think this is what I would expect the graph feature and/or the mindmap plug-in to do, but I could never get them to show what I needed.

I am currently using it as I would in the command line, but within logseq, which is just enough for me and an improvement (think a Jupyter Notebook).

  • Ideally, I would feed existing blocks to it, but I am not savvy enough and do not have the time/energy to figure it out at the moment (it’s on my someday/maybe, though!)