Specular illustrations

Specular pages take an ascent component through illustration { ... }, and mermoid-ascent returns one. That is the whole integration: specular does not depend on mermoid, mermoid does not depend on specular, and neither ships an adapter.

Add mermoid to the docs project only. Your published modules never see it:

// docs project, JVM and Scala.js
libraryDependencies += "rocks.earlyeffect" %%% "mermoid-ascent" % "<version>"

A static figure

Mermaid("...") parses the diagram while your docs compile, so a broken figure is a compile error, not a broken picture. The site SSRs the hybrid HTML and SVG; no JavaScript is needed to see it.

MermoidAscent.diagram(
      Mermaid("""flowchart LR
  spec["DocSpec"] --> tests["sbt test"]
  spec --> site["static site"]
""".stripMargin)
    )

Without the source panel, the same diagram is an illustration: the page region is the figure.

A live figure

diagramInteractive allocates its own width source, so it is an effect. Wrap it in illustrationIO and mark it .live; SpecularClient.fromPages remounts it in the browser with no mounter of your own:

illustrationIO(MermoidAscent.diagramInteractive(pipeline, widthControls = WidthControls.Shown)).live
viewport 720px

A broken figure does not compile

The parser runs inside the Mermaid literal, so the error points at the diagram source:

mermoid.Mermaid("flowchart LR\n  a -->")
Not a Mermaid diagram: parse error at 17: expected diagram / flowchart / end-of-input
  package mermoid.docs

Diagram text you read at runtime goes through Mermaid.from, which returns a typed ParseError.