Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@zsviczian/mermaid-to-excalidraw

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zsviczian/mermaid-to-excalidraw - npm Package Compare versions

Comparing version 0.1.2-obsidian-3 to 0.2.0-obsidian-1

dist/converter/transformToExcalidrawSkeleton.d.ts

9

dist/constants.d.ts

@@ -5,1 +5,10 @@ export declare const DEFAULT_FONT_SIZE = 20;

};
export declare const MERMAID_CONFIG: {
startOnLoad: boolean;
flowchart: {
curve: string;
};
themeVariables: {
fontSize: string;
};
};

@@ -6,1 +6,8 @@ export const DEFAULT_FONT_SIZE = 20;

};
export const MERMAID_CONFIG = {
startOnLoad: false,
flowchart: { curve: "linear" },
themeVariables: {
fontSize: `${DEFAULT_FONT_SIZE * 1.25}px`,
},
};

138

dist/converter/types/sequence.js

@@ -0,118 +1,4 @@

import { nanoid } from "nanoid";
import { GraphConverter } from "../GraphConverter.js";
import { nanoid } from "nanoid";
// Arrow mapper for the supported sequence arrow types
const EXCALIDRAW_STROKE_STYLE_FOR_ARROW = {
SOLID: "solid",
DOTTED: "dotted",
SOLID_CROSS: "solid",
DOTTED_CROSS: "dotted",
SOLID_OPEN: "solid",
DOTTED_OPEN: "dotted",
SOLID_POINT: "solid",
DOTTED_POINT: "dotted",
};
const createLine = (line) => {
const lineElement = {
type: "line",
x: line.startX,
y: line.startY,
points: [
[0, 0],
[line.endX - line.startX, line.endY - line.startY],
],
width: line.endX - line.startX,
height: line.endY - line.startY,
strokeStyle: line.strokeStyle || "solid",
strokeColor: line.strokeColor || "#000",
strokeWidth: line.strokeWidth || 1,
};
if (line.groupId) {
Object.assign(lineElement, { groupIds: [line.groupId] });
}
if (line.id) {
Object.assign(lineElement, { id: line.id });
}
return lineElement;
};
const createText = (element) => {
const textElement = {
type: "text",
x: element.x,
y: element.y,
width: element.width,
height: element.height,
text: element.text || "",
fontSize: element.fontSize,
verticalAlign: "middle",
};
if (element.groupId) {
Object.assign(textElement, { groupIds: [element.groupId] });
}
if (element.id) {
Object.assign(textElement, { id: element.id });
}
return textElement;
};
const createContainer = (element) => {
let extraProps = {};
if (element.type === "rectangle" && element.subtype === "activation") {
extraProps = {
backgroundColor: "#e9ecef",
fillStyle: "solid",
};
}
const container = {
id: element.id,
type: element.type,
x: element.x,
y: element.y,
width: element.width,
height: element.height,
label: {
text: element?.label?.text || "",
fontSize: element?.label?.fontSize,
verticalAlign: "middle",
strokeColor: element.label?.color || "#000",
},
strokeStyle: element?.strokeStyle,
strokeWidth: element?.strokeWidth,
strokeColor: element?.strokeColor,
backgroundColor: element?.bgColor,
fillStyle: "solid",
...extraProps,
};
if (element.groupId) {
Object.assign(container, { groupIds: [element.groupId] });
}
return container;
};
const createArrow = (arrow) => {
const strokeStyle = EXCALIDRAW_STROKE_STYLE_FOR_ARROW[arrow.strokeStyle];
const arrowElement = {
type: "arrow",
x: arrow.startX,
y: arrow.startY,
points: arrow.points || [
[0, 0],
[arrow.endX - arrow.startX, arrow.endY - arrow.startY],
],
width: arrow.endX - arrow.startX,
height: arrow.endY - arrow.startY,
strokeStyle,
endArrowhead: arrow.strokeStyle === "SOLID_OPEN" || arrow.strokeStyle === "DOTTED_OPEN"
? null
: "arrow",
label: {
text: arrow?.label?.text || "",
fontSize: 16,
},
roundness: {
type: 2,
},
};
if (arrow.groupId) {
Object.assign(arrowElement, { groupIds: [arrow.groupId] });
}
return arrowElement;
};
import { transformToExcalidrawLineSkeleton, transformToExcalidrawTextSkeleton, transformToExcalidrawContainerSkeleton, transformToExcalidrawArrowSkeleton, } from "../transformToExcalidrawSkeleton.js";
export const SequenceToExcalidrawSkeletonConvertor = new GraphConverter({

@@ -130,10 +16,10 @@ converter: (chart) => {

case "line":
excalidrawElement = createLine(element);
excalidrawElement = transformToExcalidrawLineSkeleton(element);
break;
case "rectangle":
case "ellipse":
excalidrawElement = createContainer(element);
excalidrawElement = transformToExcalidrawContainerSkeleton(element);
break;
case "text":
excalidrawElement = createText(element);
excalidrawElement = transformToExcalidrawTextSkeleton(element);
break;

@@ -156,3 +42,3 @@ default:

}
elements.push(createLine(line));
elements.push(transformToExcalidrawLineSkeleton(line));
});

@@ -163,5 +49,5 @@ Object.values(chart.arrows).forEach((arrow) => {

}
elements.push(createArrow(arrow));
elements.push(transformToExcalidrawArrowSkeleton(arrow));
if (arrow.sequenceNumber) {
elements.push(createContainer(arrow.sequenceNumber));
elements.push(transformToExcalidrawContainerSkeleton(arrow.sequenceNumber));
}

@@ -174,9 +60,9 @@ });

lines.forEach((line) => {
elements.push(createLine(line));
elements.push(transformToExcalidrawLineSkeleton(line));
});
texts.forEach((text) => {
elements.push(createText(text));
elements.push(transformToExcalidrawTextSkeleton(text));
});
nodes.forEach((node) => {
elements.push(createContainer(node));
elements.push(transformToExcalidrawContainerSkeleton(node));
});

@@ -220,3 +106,3 @@ }

const groupRectId = nanoid();
const groupRect = createContainer({
const groupRect = transformToExcalidrawContainerSkeleton({
type: "rectangle",

@@ -223,0 +109,0 @@ x: groupRectX,

@@ -5,2 +5,3 @@ import { MermaidOptions } from "./index.js";

import { Flowchart } from "./parser/flowchart.js";
export declare const graphToExcalidraw: (graph: Flowchart | GraphImage | Sequence, options?: MermaidOptions) => MermaidToExcalidrawResult;
import { Class } from "./parser/class.js";
export declare const graphToExcalidraw: (graph: Flowchart | GraphImage | Sequence | Class, options?: MermaidOptions) => MermaidToExcalidrawResult;
import { FlowchartToExcalidrawSkeletonConverter } from "./converter/types/flowchart.js";
import { GraphImageConverter } from "./converter/types/graphImage.js";
import { SequenceToExcalidrawSkeletonConvertor } from "./converter/types/sequence.js";
import { classToExcalidrawSkeletonConvertor } from "./converter/types/class.js";
export const graphToExcalidraw = (graph, options = {}) => {

@@ -15,2 +16,5 @@ switch (graph.type) {

}
case "class": {
return classToExcalidrawSkeletonConvertor.convert(graph, options);
}
default: {

@@ -17,0 +21,0 @@ throw new Error(`graphToExcalidraw: unknown graph type "${graph.type}, only flowcharts are supported!"`);

import { GraphImage } from "./interfaces.js";
import { Flowchart } from "./parser/flowchart.js";
import { Sequence } from "./parser/sequence.js";
import { Class } from "./parser/class.js";
import { MermaidOptions } from "./index.js";

@@ -10,2 +11,2 @@ declare global {

}
export declare const parseMermaid: (definition: string, forceSVG?: boolean, options?: MermaidOptions) => Promise<Flowchart | GraphImage | Sequence>;
export declare const parseMermaid: (definition: string, forceSVG?: boolean, options?: MermaidOptions) => Promise<Flowchart | GraphImage | Sequence | Class>;

@@ -5,2 +5,3 @@ //import { DEFAULT_FONT_SIZE } from "./constants.js"; //zsviczian

import { parseMermaidSequenceDiagram } from "./parser/sequence.js";
import { parseMermaidClassDiagram } from "./parser/class.js";
import { replaceSVGStyle } from "./obsidianUtil.js"; //zsviczian

@@ -51,4 +52,4 @@ import { DEFAULT_FONT_SIZE } from "./constants.js";

});
const diagram = await window.mermaid.mermaidAPI.getDiagramFromText(encodeEntities(definition) //zsviczian
);
const diagram = await window.mermaid.mermaidAPI.getDiagramFromText(//zsviczian
encodeEntities(definition));
// Render the SVG diagram

@@ -75,2 +76,6 @@ const { svg } = await window.mermaid.render("mermaid-to-excalidraw", definition); //zsviczian

}
case "classDiagram": {
data = parseMermaidClassDiagram(diagram, svgContainer);
break;
}
// fallback to image if diagram type not-supported

@@ -77,0 +82,0 @@ default: {

@@ -1,2 +0,2 @@

import { entityCodesToText, getTransformAttr } from "../utils.js";
import { computeEdgePositions, entityCodesToText, getTransformAttr, } from "../utils.js";
const parseSubGraph = (data, containerEl) => {

@@ -140,49 +140,2 @@ // Extract only node id for better reference

};
const computeEdgePositions = (pathElement, offset = { x: 0, y: 0 }) => {
if (pathElement.tagName.toLowerCase() !== "path") {
throw new Error(`Invalid input: Expected an HTMLElement of tag "path", got ${pathElement.tagName}`);
}
const dAttr = pathElement.getAttribute("d");
if (!dAttr) {
throw new Error('Path element does not contain a "d" attribute');
}
// Split the d attribute based on M (Move To) and L (Line To) commands
const commands = dAttr.split(/(?=[LM])/);
const startPosition = commands[0]
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
const endPosition = commands[commands.length - 1]
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
const reflectionPoints = commands
.map((command) => {
const coords = command
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
return { x: coords[0], y: coords[1] };
})
.filter((point, index, array) => {
if (index === array.length - 1) {
return true;
}
const prevPoint = array[index - 1];
return (index === 0 || (point.x !== prevPoint.x && point.y !== prevPoint.y));
})
.map((p) => {
return {
x: p.x + offset.x,
y: p.y + offset.y,
};
});
return {
startX: startPosition[0] + offset.x,
startY: startPosition[1] + offset.y,
endX: endPosition[0] + offset.x,
endY: endPosition[1] + offset.y,
reflectionPoints,
};
};
export const parseMermaidFlowChartDiagram = (diagram, containerEl) => {

@@ -189,0 +142,0 @@ // This does some cleanup and initialization making sure

@@ -1,57 +0,3 @@

import { Diagram } from "mermaid/dist/Diagram.js";
import { ExcalidrawLinearElement } from "@excalidraw/excalidraw/types/element/types.js";
export type Line = {
id?: string;
startX: number;
startY: number;
endX: number;
endY: number;
strokeColor: string | null;
strokeWidth: number | null;
strokeStyle: ExcalidrawLinearElement["strokeStyle"] | null;
type: "line";
groupId?: string;
};
type ARROW_KEYS = keyof typeof SEQUENCE_ARROW_TYPES;
export type Arrow = Omit<Line, "type" | "strokeStyle"> & {
type: "arrow";
label?: {
text: string | null;
fontSize: number;
};
strokeStyle: (typeof SEQUENCE_ARROW_TYPES)[ARROW_KEYS];
points?: number[][];
sequenceNumber: Container;
};
export type Text = {
id?: string;
type: "text";
text: string;
x: number;
y: number;
width: number;
height: number;
fontSize: number;
groupId?: string;
};
export type Container = {
id?: string;
type: "rectangle" | "ellipse";
label?: {
text: string | null;
fontSize: number;
color?: string;
};
x: number;
y: number;
width?: number;
height?: number;
strokeStyle?: "dashed" | "solid";
strokeWidth?: number;
strokeColor?: string;
bgColor?: string;
subtype?: "actor" | "activation" | "highlight" | "note" | "sequence";
groupId?: string;
};
export type Node = Container | Line | Arrow | Text;
import { Arrow, Container, Line, Node, Text } from "../elementSkeleton.js";
import type { Diagram } from "mermaid/dist/Diagram.js";
type Loop = {

@@ -75,13 +21,3 @@ lines: Line[];

}
declare const SEQUENCE_ARROW_TYPES: {
0: string;
1: string;
3: string;
4: string;
5: string;
6: string;
24: string;
25: string;
};
export declare const parseMermaidSequenceDiagram: (diagram: Diagram, containerEl: Element) => Sequence;
export {};
import { SVG_TO_SHAPE_MAPPER } from "../constants.js";
import { nanoid } from "nanoid";
import { entityCodesToText } from "../utils.js";
import { createArrowSkeletonFromSVG, createContainerSkeletonFromSVG, createLineSkeletonFromSVG, createTextSkeletonFromSVG, } from "../elementSkeleton.js";
// Currently mermaid supported these 6 arrow types, the names are taken from mermaidParser.LINETYPE

@@ -47,127 +47,27 @@ const SEQUENCE_ARROW_TYPES = {

};
const createContainerElement = (node, type, opts = {}) => {
const container = {};
container.type = type;
const { text, subtype, id, groupId } = opts;
container.id = id;
if (groupId) {
container.groupId = groupId;
}
if (text) {
container.label = {
text: entityCodesToText(text),
fontSize: 16,
};
}
const boundingBox = node.getBBox();
container.x = boundingBox.x;
container.y = boundingBox.y;
container.width = boundingBox.width;
container.height = boundingBox.height;
container.subtype = subtype;
switch (subtype) {
case "highlight":
const bgColor = node.getAttribute("fill");
if (bgColor) {
container.bgColor = bgColor;
}
const getStrokeStyle = (type) => {
let strokeStyle;
switch (type) {
case MESSAGE_TYPE.SOLID:
case MESSAGE_TYPE.SOLID_CROSS:
case MESSAGE_TYPE.SOLID_OPEN:
case MESSAGE_TYPE.SOLID_POINT:
strokeStyle = "solid";
break;
case "note":
container.strokeStyle = "dashed";
case MESSAGE_TYPE.DOTTED:
case MESSAGE_TYPE.DOTTED_CROSS:
case MESSAGE_TYPE.DOTTED_OPEN:
case MESSAGE_TYPE.DOTTED_POINT:
strokeStyle = "dotted";
break;
default:
strokeStyle = "solid";
break;
}
return container;
return strokeStyle;
};
const createTextElement = (textNode, text, opts) => {
const node = {};
const x = Number(textNode.getAttribute("x"));
const y = Number(textNode.getAttribute("y"));
node.type = "text";
node.text = entityCodesToText(text);
if (opts?.id) {
node.id = opts.id;
}
if (opts?.groupId) {
node.groupId = opts.groupId;
}
const boundingBox = textNode.getBBox();
node.width = boundingBox.width;
node.height = boundingBox.height;
node.x = x - boundingBox.width / 2;
node.y = y;
const fontSize = parseInt(getComputedStyle(textNode).fontSize);
node.fontSize = fontSize;
return node;
};
const createLineElement = (lineNode, startX, startY, endX, endY, opts) => {
const line = {};
line.startX = startX;
line.startY = startY;
line.endX = endX;
if (opts?.groupId) {
line.groupId = opts.groupId;
}
if (opts?.id) {
line.id = opts.id;
}
// 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
line.endY = endY;
line.strokeColor = lineNode.getAttribute("stroke");
line.strokeWidth = Number(lineNode.getAttribute("stroke-width"));
line.type = "line";
return line;
};
const createArrowElement = (arrowNode, message) => {
const arrow = {};
arrow.label = { text: entityCodesToText(message.message), fontSize: 16 };
const tagName = arrowNode.tagName;
if (tagName === "line") {
arrow.startX = Number(arrowNode.getAttribute("x1"));
arrow.startY = Number(arrowNode.getAttribute("y1"));
arrow.endX = Number(arrowNode.getAttribute("x2"));
arrow.endY = Number(arrowNode.getAttribute("y2"));
}
else if (tagName === "path") {
const dAttr = arrowNode.getAttribute("d");
if (!dAttr) {
throw new Error('Path element does not contain a "d" attribute');
}
// Split the d attribute based on M (Move To) and C (Curve) commands
const commands = dAttr.split(/(?=[LC])/);
const startPosition = commands[0]
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
const points = [];
commands.forEach((command) => {
const currPoints = command
.substring(1)
.trim()
.split(" ")
.map((pos) => {
const [x, y] = pos.split(",");
return [
parseFloat(x) - startPosition[0],
parseFloat(y) - startPosition[1],
];
});
points.push(...currPoints);
});
const endPosition = points[points.length - 1];
arrow.startX = startPosition[0];
arrow.startY = startPosition[1];
arrow.endX = endPosition[0];
arrow.endY = endPosition[1];
arrow.points = points;
}
if (message) {
// In mermaid the text is positioned above arrow but in Excalidraw
// its postioned on the arrow hence the elements below it might look cluttered so shifting the arrow by an offset of 10px
const offset = 10;
arrow.startY = arrow.startY - offset;
arrow.endY = arrow.endY - offset;
}
const showSequenceNumber = !!arrowNode.nextElementSibling?.classList.contains("sequenceNumber");
const attachSequenceNumberToArrow = (node, arrow) => {
const showSequenceNumber = !!node.nextElementSibling?.classList.contains("sequenceNumber");
if (showSequenceNumber) {
const text = arrowNode.nextElementSibling?.textContent;
const text = node.nextElementSibling?.textContent;
if (!text) {

@@ -188,9 +88,4 @@ throw new Error("sequence number not present");

};
arrow.sequenceNumber = sequenceNumber;
Object.assign(arrow, { sequenceNumber });
}
arrow.strokeColor = arrowNode.getAttribute("stroke");
arrow.strokeWidth = Number(arrowNode.getAttribute("stroke-width"));
arrow.type = "arrow";
arrow.strokeStyle = SEQUENCE_ARROW_TYPES[message.type];
return arrow;
};

@@ -213,10 +108,13 @@ const createActorSymbol = (rootNode, text, opts) => {

const endY = Number(child.getAttribute("y2"));
ele = createLineElement(child, startX, startY, endX, endY, { groupId, id });
ele = createLineSkeletonFromSVG(child, startX, startY, endX, endY, { groupId, id });
break;
case "text":
ele = createTextElement(child, text, { groupId, id });
ele = createTextSkeletonFromSVG(child, text, {
groupId,
id,
});
break;
case "circle":
ele = createContainerElement(child, "ellipse", {
text: child.textContent || undefined,
ele = createContainerSkeletonFromSVG(child, "ellipse", {
label: child.textContent ? { text: child.textContent } : undefined,
groupId,

@@ -226,3 +124,7 @@ id,

default:
ele = createContainerElement(child, SVG_TO_SHAPE_MAPPER[child.tagName], { text: child.textContent || undefined, groupId, id });
ele = createContainerSkeletonFromSVG(child, SVG_TO_SHAPE_MAPPER[child.tagName], {
label: child.textContent ? { text: child.textContent } : undefined,
groupId,
id,
});
}

@@ -236,3 +138,3 @@ nodeElements.push(ele);

.filter((node) => node.tagName === "text")
.map((actor) => actor.tagName === "text" && actor.parentElement);
.map((actor) => actor.parentElement);
const nodes = [];

@@ -242,8 +144,6 @@ const lines = [];

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];
//@ts-ignore
const bottomRootNode = actorRootNodes[actorsLength + index];
if (!topRootNode) {
if (!topRootNode || !bottomRootNode) {
throw "root not found";

@@ -254,3 +154,3 @@ }

// creating top actor node element
const topNodeElement = createContainerElement(topRootNode.firstChild, "rectangle", { id: `${actor.name}-top`, text, subtype: "actor" });
const topNodeElement = createContainerSkeletonFromSVG(topRootNode.firstChild, "rectangle", { id: `${actor.name}-top`, label: { text }, subtype: "actor" });
if (!topNodeElement) {

@@ -261,3 +161,3 @@ throw "Top Node element not found!";

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

@@ -277,3 +177,3 @@ // Get the line connecting the top and bottom nodes. As per the DOM, the line is rendered as first child of parent element

const endX = Number(lineNode.getAttribute("x2"));
const line = createLineElement(lineNode, startX, startY, endX, endY);
const line = createLineSkeletonFromSVG(lineNode, startX, startY, endX, endY);
lines.push(line);

@@ -302,3 +202,3 @@ }

const endY = bottomEllipseNode.y;
const line = createLineElement(lineNode, startX, startY, endX, endY);
const line = createLineSkeletonFromSVG(lineNode, startX, startY, endX, endY);
lines.push(line);

@@ -311,3 +211,3 @@ }

const parseActor = (actors, containerEl) => {
//@ts-ignore
//@ts-ignore //zsviczian
if (!window.ExcalidrawAutomate.obsidian.requireApiVersion("1.5.0"))

@@ -317,3 +217,3 @@ return parseActor_old(actors, containerEl);

.filter((node) => node.tagName === "text")
.map((actor) => actor.tagName === "text" && actor.parentElement);
.map((actor) => actor.parentElement);
const lineNodes = containerEl.querySelectorAll("line"); //zsviczian

@@ -324,8 +224,6 @@ const nodes = [];

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];
//@ts-ignore
const bottomRootNode = actorRootNodes[actorsLength + index];
if (!topRootNode) {
if (!topRootNode || !bottomRootNode) {
throw "root not found";

@@ -336,3 +234,3 @@ }

// creating top actor node element
const topNodeElement = createContainerElement(topRootNode.firstChild, "rectangle", { id: `${actor.name}-top`, text, subtype: "actor" });
const topNodeElement = createContainerSkeletonFromSVG(topRootNode.firstChild, "rectangle", { id: `${actor.name}-top`, label: { text }, subtype: "actor" });
if (!topNodeElement) {

@@ -343,6 +241,7 @@ throw "Top Node element not found!";

// creating bottom actor node element
const bottomNodeElement = createContainerElement(bottomRootNode.firstChild, "rectangle", { id: `${actor.name}-bottom`, text, subtype: "actor" });
const bottomNodeElement = createContainerSkeletonFromSVG(bottomRootNode.firstChild, "rectangle", { id: `${actor.name}-bottom`, label: { 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
//const lineNode = topRootNode.previousElementSibling as SVGLineElement;
if (lineNode?.tagName !== "line") {

@@ -355,7 +254,7 @@ throw "Line not found";

}
const startY = topNodeElement.y;
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);
const line = createLineSkeletonFromSVG(lineNode, startX, startY, endX, endY);
lines.push(line);

@@ -384,3 +283,3 @@ }

const endY = bottomEllipseNode.y;
const line = createLineElement(lineNode, startX, startY, endX, endY);
const line = createLineSkeletonFromSVG(lineNode, startX, startY, endX, endY);
lines.push(line);

@@ -399,3 +298,11 @@ }

const message = arrowMessages[index];
const arrow = createArrowElement(arrowNode, message);
const messageType = SEQUENCE_ARROW_TYPES[message.type];
const arrow = createArrowSkeletonFromSVG(arrowNode, {
label: message?.message,
strokeStyle: getStrokeStyle(message.type),
endArrowhead: messageType === "SOLID_OPEN" || messageType === "DOTTED_OPEN"
? null
: "arrow",
});
attachSequenceNumberToArrow(arrowNode, arrow);
arrows.push(arrow);

@@ -415,4 +322,4 @@ });

const text = noteText[index].message;
const note = createContainerElement(rect, "rectangle", {
text,
const note = createContainerSkeletonFromSVG(rect, "rectangle", {
label: { text },
subtype: "note",

@@ -428,4 +335,4 @@ });

activationNodes.forEach((node) => {
const rect = createContainerElement(node, "rectangle", {
text: "",
const rect = createContainerSkeletonFromSVG(node, "rectangle", {
label: { text: "" },
subtype: "activation",

@@ -447,3 +354,3 @@ });

const endY = Number(node.getAttribute("y2"));
const line = createLineElement(node, startX, startY, endX, endY);
const line = createLineSkeletonFromSVG(node, startX, startY, endX, endY);
line.strokeStyle = "dotted";

@@ -460,3 +367,3 @@ line.strokeColor = "#adb5bd";

const text = node.textContent || "";
const textElement = createTextElement(node, text);
const textElement = createTextSkeletonFromSVG(node, text);
// The text is rendered between [ ] in DOM hence getting the text excluding the [ ]

@@ -475,5 +382,5 @@ const rawText = text.match(/\[(.*?)\]/)?.[1] || "";

labelBoxes.forEach((labelBox, index) => {
const labelText = labelTextNode[index]?.textContent || "";
const container = createContainerElement(labelBox, "rectangle", {
text: labelText,
const text = labelTextNode[index]?.textContent || "";
const container = createContainerSkeletonFromSVG(labelBox, "rectangle", {
label: { text },
});

@@ -495,4 +402,4 @@ container.strokeColor = "#adb5bd";

rects.forEach((rect) => {
const node = createContainerElement(rect, "rectangle", {
text: "",
const node = createContainerSkeletonFromSVG(rect, "rectangle", {
label: { text: "" },
subtype: "highlight",

@@ -499,0 +406,0 @@ });

@@ -0,1 +1,2 @@

import { Position } from "./interfaces.js";
export declare const entityCodesToText: (input: string) => string;

@@ -8,1 +9,10 @@ export declare const getTransformAttr: (el: Element) => {

export declare const decodeEntities: (text: string) => string;
interface EdgePositionData {
startX: number;
startY: number;
endX: number;
endY: number;
reflectionPoints: Position[];
}
export declare const computeEdgePositions: (pathElement: SVGPathElement, offset?: Position) => EdgePositionData;
export {};

@@ -13,3 +13,3 @@ // Convert mermaid entity codes to text e.g. "#9829;" to "♥"

const transformAttr = el.getAttribute("transform");
const translateMatch = transformAttr?.match(/translate\(([\d.-]+),\s*([\d.-]+)\)/);
const translateMatch = transformAttr?.match(/translate\(([ \d.-]+),\s*([\d.-]+)\)/);
let transformX = 0;

@@ -45,1 +45,48 @@ let transformY = 0;

};
export const computeEdgePositions = (pathElement, offset = { x: 0, y: 0 }) => {
if (pathElement.tagName.toLowerCase() !== "path") {
throw new Error(`Invalid input: Expected an HTMLElement of tag "path", got ${pathElement.tagName}`);
}
const dAttr = pathElement.getAttribute("d");
if (!dAttr) {
throw new Error('Path element does not contain a "d" attribute');
}
// Split the d attribute based on M (Move To) and L (Line To) commands
const commands = dAttr.split(/(?=[LM])/);
const startPosition = commands[0]
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
const endPosition = commands[commands.length - 1]
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
const reflectionPoints = commands
.map((command) => {
const coords = command
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
return { x: coords[0], y: coords[1] };
})
.filter((point, index, array) => {
if (index === array.length - 1) {
return true;
}
const prevPoint = array[index - 1];
return (index === 0 || (point.x !== prevPoint.x && point.y !== prevPoint.y));
})
.map((p) => {
return {
x: p.x + offset.x,
y: p.y + offset.y,
};
});
return {
startX: startPosition[0] + offset.x,
startY: startPosition[1] + offset.y,
endX: endPosition[0] + offset.x,
endY: endPosition[1] + offset.y,
reflectionPoints,
};
};
{
"name": "@zsviczian/mermaid-to-excalidraw",
"version": "0.1.2-obsidian-3",
"version": "0.2.0-obsidian-1",
"description": "Mermaid to Excalidraw Diagrams",

@@ -21,3 +21,3 @@ "main": "dist/index.js",

"build": "rimraf ./dist && cross-env tsc -b src",
"start": "parcel playground/index.html --open --dist-dir ./public",
"start": "rimraf .parcel-cache && parcel playground/index.html --open --dist-dir ./public",
"build:playground": "rimraf ./public && parcel build playground/index.html --no-scope-hoist --dist-dir ./public --public-url /",

@@ -34,3 +34,3 @@ "test:code": "eslint --max-warnings=0 --ext .js,.ts,.tsx ."

"@excalidraw/eslint-config": "1.0.3",
"@excalidraw/excalidraw": "0.16.1-6920-d3d0bd0",
"@excalidraw/excalidraw": "0.17.1-7381-cdf6d3e",
"@parcel/transformer-sass": "2.9.1",

@@ -37,0 +37,0 @@ "@types/mermaid": "9.2.0",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc