excalidraw / mermaid-to-excalidraw

Generate Excalidraw diagrams from Mermaid
https://mermaid-to-excalidraw.vercel.app
MIT License
265 stars 46 forks source link

Upgrading to mermaid 10.6.1 breaks the sequence diagram parsing logic #38

Closed zsviczian closed 6 months ago

zsviczian commented 11 months ago

@ad1992, I use the mermaid object that comes with Obsidian. Obsidian upgraded to 10.6.1 which broke the sequence diagram parsing logic. I am sharing my solution here, so when you eventually upgrade mermaid-to-excalidraw to mermaid 10.6.1 you don't need to go through the same debugging. Look for the lines with //zsviczian Essentially the sequence of elements changed in the SVG file and lineNode = topRootNode.previousElementSibling as SVGLineElement; returns null. Also the logic for avoiding the overlapping vertical line changed... I hope you find this helpful.

const parseActor = (actors: { [key: string]: Actor }, containerEl: Element) => {
  const actorRootNodes = Array.from(containerEl.querySelectorAll(".actor"))
    .filter((node) => node.tagName === "text")
    .map((actor) => actor.tagName === "text" && actor.parentElement);
  const lineNodes = containerEl.querySelectorAll("line"); //zsviczian
  const nodes: Array<Node[]> = [];
  const lines: Array<Line> = [];
  const actorsLength = Object.keys(actors).length;
  Object.values(actors).forEach((actor, index) => {
    //@ts-ignore
    // For each actor there are two nodes top and bottom which is connected by a line
    const topRootNode = actorRootNodes[index] as SVGGElement;
    //@ts-ignore
    const bottomRootNode = actorRootNodes[actorsLength + index] as SVGGElement;

    if (!topRootNode) {
      throw "root not found";
    }
    const text = actor.description;
    if (actor.type === "participant") {
      // creating top actor node element
      const topNodeElement = createContainerElement(
        topRootNode.firstChild as SVGSVGElement,
        "rectangle",
        { id: `${actor.name}-top`, text, subtype: "actor" }
      );
      if (!topNodeElement) {
        throw "Top Node element not found!";
      }
      nodes.push([topNodeElement]);

      // creating bottom actor node element
      const bottomNodeElement = createContainerElement(
        bottomRootNode.firstChild as SVGSVGElement,
        "rectangle",
        { id: `${actor.name}-bottom`, text, subtype: "actor" }
      );
      nodes.push([bottomNodeElement]);

      // Get the line connecting the top and bottom nodes. As per the DOM, the line is rendered as first child of parent element
      const lineNode = lineNodes[index]; //zsviczian

      if (lineNode?.tagName !== "line") {
        throw "Line not found";
      }
      const startX = Number(lineNode.getAttribute("x1"));
      if (!bottomNodeElement.height) { //zsviczian
        throw "Bottom node element height is null";
      }
      const startY = topNodeElement.y; //zsviczian
      // Make sure lines don't overlap with the nodes, in mermaid it overlaps but isn't visible as its pushed back and containers are non transparent
      const endY = bottomNodeElement.y + bottomNodeElement.height; //zsviczian
      const endX = Number(lineNode.getAttribute("x2"));
      const line = createLineElement(lineNode, startX, startY, endX, endY);
      lines.push(line);
    } else if (actor.type === "actor") {
      const topNodeElement = createActorSymbol(topRootNode, text, {
        id: `${actor.name}-top`,
      });
      nodes.push(topNodeElement);
      const bottomNodeElement = createActorSymbol(bottomRootNode, text, {
        id: `${actor.name}-bottom`,
      });
      nodes.push(bottomNodeElement);

      // Get the line connecting the top and bottom nodes. As per the DOM, the line is rendered as first child of parent element
      const lineNode = lineNodes[index]; //zsviczian

      if (lineNode?.tagName !== "line") {
        throw "Line not found";
      }
      const startX = Number(lineNode.getAttribute("x1"));
      const startY = Number(lineNode.getAttribute("y1"));

      const endX = Number(lineNode.getAttribute("x2"));
      // Make sure lines don't overlap with the nodes, in mermaid it overlaps but isn't visible as its pushed back and containers are non transparent
      const bottomEllipseNode = bottomNodeElement.find(
        (node): node is Container => node.type === "ellipse"
      );
      if (bottomEllipseNode) {
        const endY = bottomEllipseNode.y;
        const line = createLineElement(lineNode, startX, startY, endX, endY);
        lines.push(line);
      }
    }
  });
ad1992 commented 10 months ago

Hi @zsviczian 👋🏻 just back from time off, yes the sequence diagrams ordering was updated, I had already fixed the issue in mermaid js here and now it works absolutely fine, however, I am waiting for them to ship the next release, so I will either release it from fork if needed (in case the official release is delayed). So the above patch won't be needed.

ad1992 commented 6 months ago

This has been fixed in the latest version so closing