[verified] feat: add diagram toolbox capabilities

This commit is contained in:
2026-09-03 19:11:50 +00:00
parent 5146b14d43
commit fa62a5fc4c
16 changed files with 971 additions and 7 deletions
+16 -1
View File
@@ -6,6 +6,7 @@ import { layoutPage } from "../services/layout/layout-engine.js";
const DEFAULT_NODE_STYLE = "whiteSpace=wrap;html=1;rounded=0;fillColor=#f5f5f5;strokeColor=#666666;";
const DEFAULT_CONTAINER_STYLE = "swimlane;html=1;rounded=0;startSize=30;fillColor=#f5f5f5;strokeColor=#666666;";
const DEFAULT_EDGE_STYLE = "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;";
const FIXED_MODEL_ATTRIBUTES = new Set(["grid", "gridSize", "page", "pageWidth", "pageHeight"]);
function escapeXml(value: string): string {
return value
@@ -33,6 +34,19 @@ function dataAttributes(values: Record<string, unknown>): string {
.join("");
}
function rawAttributes(extensions: Record<string, unknown> | undefined, prefix: string, reserved = new Set<string>()): string {
const drawio = extensions?.$drawio;
const attributes = drawio && typeof drawio === "object" && !Array.isArray(drawio)
? (drawio as Record<string, unknown>).attributes
: undefined;
if (!attributes || typeof attributes !== "object" || Array.isArray(attributes)) return "";
return Object.entries(attributes as Record<string, unknown>)
.filter(([key, value]) => key.startsWith(prefix) && !reserved.has(key.slice(prefix.length)) && /^[A-Za-z_][\w:.-]*$/.test(key.slice(prefix.length))
&& (typeof value === "string" || typeof value === "number" || typeof value === "boolean"))
.map(([key, value]) => ` ${key.slice(prefix.length)}="${escapeXml(String(value))}"`)
.join("");
}
function geometryForNode(node: DiagramNode, index: number, page: DiagramPage): { x: number; y: number; width: number; height: number } {
if (node.geometry) return node.geometry;
const horizontal = (page.layout?.direction ?? "horizontal") === "horizontal";
@@ -91,7 +105,8 @@ function serializePage(input: DiagramPage): string {
const pageWidth = Math.max(page.width ?? 1169, fit(extent.width));
const pageHeight = Math.max(page.height ?? 827, fit(extent.height));
const metadata = dataAttributes({ properties: page.properties, extensions: page.extensions });
return ` <diagram id="${escapeXml(page.id)}" name="${escapeXml(page.title)}"${metadata}>\n <mxGraphModel grid="1" gridSize="${page.layout?.gridSize ?? 10}" page="1" pageWidth="${pageWidth}" pageHeight="${pageHeight}">\n <root>\n <mxCell id="0"/>\n <mxCell id="1" parent="0"/>\n${[...nodeCells, ...edgeCells].join("\n")}\n </root>\n </mxGraphModel>\n </diagram>`;
const modelAttributes = rawAttributes(page.extensions, "model:", FIXED_MODEL_ATTRIBUTES);
return ` <diagram id="${escapeXml(page.id)}" name="${escapeXml(page.title)}"${metadata}>\n <mxGraphModel grid="1" gridSize="${page.layout?.gridSize ?? 10}" page="1" pageWidth="${pageWidth}" pageHeight="${pageHeight}"${modelAttributes}>\n <root>\n <mxCell id="0"/>\n <mxCell id="1" parent="0"/>\n${[...nodeCells, ...edgeCells].join("\n")}\n </root>\n </mxGraphModel>\n </diagram>`;
}
export function diagramIRToDrawio(ir: DiagramIR): string {