drawio-mcp-server 1.8.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/build/emitter_bus.js +2 -3
- package/build/index.js +178 -332
- package/build/plugin/mcp-plugin.js +853 -60
- package/build/prefetch-assets.js +11 -0
- package/build/real-environment/add-cell-of-shape.test.js +51 -0
- package/build/real-environment/add-edge.test.js +86 -0
- package/build/real-environment/assertions.js +18 -0
- package/build/real-environment/delete-cell-by-id.test.js +45 -0
- package/build/real-environment/edge-editing.test.js +70 -0
- package/build/real-environment/edit-cell.test.js +64 -0
- package/build/real-environment/harness.js +175 -0
- package/build/real-environment/import-export.test.js +82 -0
- package/build/real-environment/layers-and-selection.test.js +70 -0
- package/build/real-environment/logger.js +25 -0
- package/build/real-environment/screenshot.js +46 -0
- package/build/real-environment/set-cell-parent.test.js +64 -0
- package/build/real-environment/shapes.test.js +153 -0
- package/build/real-environment/test-helpers.js +10 -0
- package/build/real-environment/tools.js +22 -0
- package/build/real-environment/types.js +1 -0
- package/build/tool.js +46 -0
- package/build/tools/add-cell-of-shape.js +42 -0
- package/build/tools/add-edge.js +33 -0
- package/build/tools/add-rectangle.js +41 -0
- package/build/tools/create-layer.js +8 -0
- package/build/tools/delete-cell-by-id.js +10 -0
- package/build/tools/edit-cell.js +28 -0
- package/build/tools/edit-edge.js +30 -0
- package/build/tools/export-diagram.js +96 -0
- package/build/tools/get-active-layer.js +5 -0
- package/build/tools/get-selected-cell.js +5 -0
- package/build/tools/get-shape-by-name.js +10 -0
- package/build/tools/get-shape-categories.js +5 -0
- package/build/tools/get-shapes-in-category.js +10 -0
- package/build/tools/import-diagram.js +22 -0
- package/build/tools/index.js +49 -0
- package/build/tools/list-layers.js +5 -0
- package/build/tools/list-paged-model.js +41 -0
- package/build/tools/move-cell-to-layer.js +11 -0
- package/build/tools/set-active-layer.js +8 -0
- package/build/tools/set-cell-data.js +14 -0
- package/build/tools/set-cell-parent.js +9 -0
- package/build/tools/set-cell-shape.js +13 -0
- package/build/tools/shared.js +7 -0
- package/build/tools/types.js +1 -0
- package/package.json +25 -23
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var DrawMcp = (() => {
|
|
3
|
-
// src/
|
|
3
|
+
// src/shape-library.ts
|
|
4
4
|
var shapeLibrary = {
|
|
5
5
|
rectangle: {
|
|
6
6
|
category: "general",
|
|
@@ -337,7 +337,7 @@ var DrawMcp = (() => {
|
|
|
337
337
|
}
|
|
338
338
|
};
|
|
339
339
|
|
|
340
|
-
// src/
|
|
340
|
+
// src/websocketManager.ts
|
|
341
341
|
function createWebSocketManager(options) {
|
|
342
342
|
let socket = null;
|
|
343
343
|
let reconnectAttempts = 0;
|
|
@@ -495,7 +495,7 @@ var DrawMcp = (() => {
|
|
|
495
495
|
};
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
-
// src/
|
|
498
|
+
// src/pluginConfig.ts
|
|
499
499
|
var DEFAULT_PLUGIN_CONFIG = {
|
|
500
500
|
websocketPort: 3333,
|
|
501
501
|
serverUrl: ""
|
|
@@ -573,7 +573,7 @@ var DrawMcp = (() => {
|
|
|
573
573
|
return `ws://${host}:${config.websocketPort}`;
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
-
// src/
|
|
576
|
+
// src/settingsDialog.ts
|
|
577
577
|
function createDialogContainer() {
|
|
578
578
|
const container = document.createElement("div");
|
|
579
579
|
container.id = "mcp-settings-dialog";
|
|
@@ -1013,7 +1013,7 @@ var DrawMcp = (() => {
|
|
|
1013
1013
|
}
|
|
1014
1014
|
}
|
|
1015
1015
|
|
|
1016
|
-
// src/
|
|
1016
|
+
// src/drawio-tools.ts
|
|
1017
1017
|
function transform_NamedNodeMap_to_record(attributes) {
|
|
1018
1018
|
const tstr = Object.prototype.toString.call(attributes);
|
|
1019
1019
|
if (tstr !== "[object NamedNodeMap]") {
|
|
@@ -1065,7 +1065,7 @@ var DrawMcp = (() => {
|
|
|
1065
1065
|
const result = {};
|
|
1066
1066
|
for (const key in obj) {
|
|
1067
1067
|
const value = obj[key];
|
|
1068
|
-
if (typeof value !== "function" && key !== "children" && key !== "edges" && !key.startsWith("aria") && key !== "ownerDocument" && key !== "part" && key !== "classList" && key !== "childNodes" && key !== "shadowRoot" && key !== "innerHTML" && key !== "outerHTML" && key !== "scrollTop" && key !== "scrollLeft" && key !== "scrollWidth" && key !== "scrollHeight" && key !== "clientTop" && key !== "clientLeft" && key !== "clientWidth" && key !== "clientHeight" && key !== "onbeforecopy" && key !== "onbeforecut" && key !== "onbeforepaste" && key !== "onsearch" && key !== "elementTiming" && key !== "onfullscreenchange" && key !== "onfullscreenerror" && key !== "onwebkitfullscreenchange" && key !== "onwebkitfullscreenerror" && key !== "firstElementChild" && key !== "lastElementChild" && key !== "childElementCount" && key !== "previousElementSibling" && key !== "nextElementSibling" && key !== "currentCSSZoom" && key !== "parentNode" && key !== "parentElement" && key !== "firstChild" && key !== "lastChild" && key !== "previousSibling" && key !== "nextSibling" && key !== "nodeValue" && key !== "textContent") {
|
|
1068
|
+
if (typeof value !== "function" && key !== "children" && key !== "edges" && !key.startsWith("aria") && key !== "ownerDocument" && key !== "part" && key !== "classList" && key !== "childNodes" && key !== "shadowRoot" && key !== "innerHTML" && key !== "outerHTML" && key !== "scrollTop" && key !== "scrollLeft" && key !== "scrollWidth" && key !== "scrollHeight" && key !== "clientTop" && key !== "clientLeft" && key !== "clientWidth" && key !== "clientHeight" && key !== "onbeforecopy" && key !== "onbeforecut" && key !== "onbeforepaste" && key !== "onsearch" && key !== "elementTiming" && key !== "onfullscreenchange" && key !== "onfullscreenerror" && key !== "onwebkitfullscreenchange" && key !== "onwebkitfullscreenerror" && key !== "firstElementChild" && key !== "lastElementChild" && key !== "childElementCount" && key !== "previousElementSibling" && key !== "nextElementSibling" && key !== "currentCSSZoom" && key !== "parentNode" && key !== "parentElement" && key !== "firstChild" && key !== "lastChild" && key !== "previousSibling" && key !== "nextSibling" && key !== "nodeValue" && key !== "textContent" && key !== "ELEMENT_NODE" && key !== "ATTRIBUTE_NODE" && key !== "TEXT_NODE" && key !== "CDATA_SECTION_NODE" && key !== "ENTITY_REFERENCE_NODE" && key !== "ENTITY_NODE" && key !== "PROCESSING_INSTRUCTION_NODE" && key !== "COMMENT_NODE" && key !== "DOCUMENT_NODE" && key !== "DOCUMENT_TYPE_NODE" && key !== "DOCUMENT_FRAGMENT_NODE" && key !== "NOTATION_NODE" && key !== "DOCUMENT_POSITION_DISCONNECTED" && key !== "DOCUMENT_POSITION_PRECEDING" && key !== "DOCUMENT_POSITION_FOLLOWING" && key !== "DOCUMENT_POSITION_CONTAINS" && key !== "DOCUMENT_POSITION_CONTAINED_BY" && key !== "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC") {
|
|
1069
1069
|
let stripped_value = {};
|
|
1070
1070
|
if ((key === "parent" || key === "source" || key === "target") && value !== void 0 && value !== null) {
|
|
1071
1071
|
stripped_value = {
|
|
@@ -1082,6 +1082,122 @@ var DrawMcp = (() => {
|
|
|
1082
1082
|
}
|
|
1083
1083
|
return result;
|
|
1084
1084
|
}
|
|
1085
|
+
function transform_cell_for_display(cell) {
|
|
1086
|
+
if (!cell || typeof cell !== "object") {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
const transformed = {
|
|
1090
|
+
id: cell.id || "",
|
|
1091
|
+
mxObjectId: cell.mxObjectId || "",
|
|
1092
|
+
value: "",
|
|
1093
|
+
geometry: cell.geometry,
|
|
1094
|
+
style: cell.style,
|
|
1095
|
+
edge: cell.edge,
|
|
1096
|
+
edges: cell.edges,
|
|
1097
|
+
parent: cell.parent,
|
|
1098
|
+
source: cell.source,
|
|
1099
|
+
target: cell.target
|
|
1100
|
+
};
|
|
1101
|
+
if (cell.value !== null && cell.value !== void 0) {
|
|
1102
|
+
if (typeof cell.value === "string") {
|
|
1103
|
+
transformed.value = cell.value;
|
|
1104
|
+
} else if (typeof cell.value === "object") {
|
|
1105
|
+
const transformed_attributes = transform_cells_NamedNodeMap_to_attributes(cell);
|
|
1106
|
+
transformed.value = {
|
|
1107
|
+
attributes: transformed_attributes,
|
|
1108
|
+
nodeName: cell.value.nodeName,
|
|
1109
|
+
localName: cell.value.localName,
|
|
1110
|
+
tagName: cell.value.tagName
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return transformed;
|
|
1115
|
+
}
|
|
1116
|
+
function get_cell_layer(graph, cell) {
|
|
1117
|
+
if (!cell || !graph) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
const layer = graph.getLayerForCell(cell);
|
|
1122
|
+
if (layer) {
|
|
1123
|
+
return {
|
|
1124
|
+
id: layer.id || "",
|
|
1125
|
+
name: layer.value || "Default Layer"
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
console.warn("Could not get layer for cell:", error);
|
|
1130
|
+
}
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
function get_cell_tags(graph, cell) {
|
|
1134
|
+
if (!cell || !graph) {
|
|
1135
|
+
return [];
|
|
1136
|
+
}
|
|
1137
|
+
try {
|
|
1138
|
+
const tags = graph.getTagsForCell(cell);
|
|
1139
|
+
return tags || [];
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
console.warn("Could not get tags for cell:", error);
|
|
1142
|
+
return [];
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function mx_isRoot(root_cell) {
|
|
1146
|
+
return function(cell) {
|
|
1147
|
+
return null != cell && cell == root_cell;
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
function mx_isLayer(root_cell) {
|
|
1151
|
+
return function(cell) {
|
|
1152
|
+
return mx_isRoot(root_cell)(cell.getParent());
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
function resolve_parent(graph, parent_id) {
|
|
1156
|
+
if (parent_id) {
|
|
1157
|
+
const model = graph.getModel();
|
|
1158
|
+
const parent = model.getCell(parent_id);
|
|
1159
|
+
if (!parent) {
|
|
1160
|
+
throw new Error(`Parent cell '${parent_id}' not found`);
|
|
1161
|
+
}
|
|
1162
|
+
return parent;
|
|
1163
|
+
}
|
|
1164
|
+
return graph.getDefaultParent();
|
|
1165
|
+
}
|
|
1166
|
+
function normalize_shape_style(shapeName, style) {
|
|
1167
|
+
if (!style || !shapeName.startsWith("mxgraph.aws4.")) {
|
|
1168
|
+
return style;
|
|
1169
|
+
}
|
|
1170
|
+
const hasExplicitShape = style.includes("shape=");
|
|
1171
|
+
if (hasExplicitShape) {
|
|
1172
|
+
return style;
|
|
1173
|
+
}
|
|
1174
|
+
return `shape=${shapeName};${style}`;
|
|
1175
|
+
}
|
|
1176
|
+
function apply_style_to_cell(graph, cell, style) {
|
|
1177
|
+
if (!style || !cell) {
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
const model = graph.getModel();
|
|
1181
|
+
model.setStyle(cell, style);
|
|
1182
|
+
if (typeof cell.setStyle === "function") {
|
|
1183
|
+
cell.setStyle(style);
|
|
1184
|
+
}
|
|
1185
|
+
if (typeof graph.refresh === "function") {
|
|
1186
|
+
graph.refresh(cell);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function create_shape_vertex(graph, options) {
|
|
1190
|
+
const mxCell = window.mxCell;
|
|
1191
|
+
const mxGeometry = window.mxGeometry;
|
|
1192
|
+
const cell = new mxCell(
|
|
1193
|
+
options.text,
|
|
1194
|
+
new mxGeometry(options.x, options.y, options.width, options.height),
|
|
1195
|
+
options.style
|
|
1196
|
+
);
|
|
1197
|
+
cell.vertex = true;
|
|
1198
|
+
graph.addCell(cell, options.parent);
|
|
1199
|
+
return cell;
|
|
1200
|
+
}
|
|
1085
1201
|
function add_new_rectangle(ui2, options) {
|
|
1086
1202
|
const { editor } = ui2;
|
|
1087
1203
|
const { graph } = editor;
|
|
@@ -1091,10 +1207,11 @@ var DrawMcp = (() => {
|
|
|
1091
1207
|
const height = options.height || 60;
|
|
1092
1208
|
const text = options.text || "";
|
|
1093
1209
|
const style = options.style || "whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;";
|
|
1210
|
+
const parent = resolve_parent(graph, options.parent_id);
|
|
1094
1211
|
graph.getModel().beginUpdate();
|
|
1095
1212
|
try {
|
|
1096
1213
|
const vertex = graph.insertVertex(
|
|
1097
|
-
|
|
1214
|
+
parent,
|
|
1098
1215
|
null,
|
|
1099
1216
|
text,
|
|
1100
1217
|
x,
|
|
@@ -1124,6 +1241,7 @@ var DrawMcp = (() => {
|
|
|
1124
1241
|
graph.getModel().endUpdate();
|
|
1125
1242
|
}
|
|
1126
1243
|
}
|
|
1244
|
+
var SELF_CONNECTOR_DEFAULT_STYLE = "edgeStyle=loopEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;";
|
|
1127
1245
|
function add_edge(ui2, options) {
|
|
1128
1246
|
const { editor } = ui2;
|
|
1129
1247
|
const { graph } = editor;
|
|
@@ -1133,19 +1251,20 @@ var DrawMcp = (() => {
|
|
|
1133
1251
|
if (!source || !target) {
|
|
1134
1252
|
return null;
|
|
1135
1253
|
}
|
|
1136
|
-
const
|
|
1254
|
+
const isSelfConnector = options.source_id === options.target_id;
|
|
1255
|
+
const defaultStyle = isSelfConnector ? SELF_CONNECTOR_DEFAULT_STYLE : "endArrow=classic;html=1;rounded=0;";
|
|
1137
1256
|
const style = options.style || defaultStyle;
|
|
1138
1257
|
const text = options.text || "";
|
|
1258
|
+
const parent = resolve_parent(graph, options.parent_id);
|
|
1139
1259
|
model.beginUpdate();
|
|
1140
1260
|
try {
|
|
1141
|
-
const edge = graph.insertEdge(
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
);
|
|
1261
|
+
const edge = graph.insertEdge(parent, null, text, source, target, style);
|
|
1262
|
+
if (options.points && options.points.length > 0) {
|
|
1263
|
+
const geo = edge.getGeometry();
|
|
1264
|
+
const mxPoint = window.mxPoint;
|
|
1265
|
+
geo.points = options.points.map((p) => new mxPoint(p.x, p.y));
|
|
1266
|
+
model.setGeometry(edge, geo);
|
|
1267
|
+
}
|
|
1149
1268
|
return edge;
|
|
1150
1269
|
} finally {
|
|
1151
1270
|
model.endUpdate();
|
|
@@ -1233,6 +1352,12 @@ var DrawMcp = (() => {
|
|
|
1233
1352
|
if (options.style !== void 0) {
|
|
1234
1353
|
graph.setCellStyle(options.style, [edge]);
|
|
1235
1354
|
}
|
|
1355
|
+
if (options.points !== void 0) {
|
|
1356
|
+
const geo = edge.getGeometry().clone();
|
|
1357
|
+
const mxPoint = window.mxPoint;
|
|
1358
|
+
geo.points = options.points.map((p) => new mxPoint(p.x, p.y));
|
|
1359
|
+
model.setGeometry(edge, geo);
|
|
1360
|
+
}
|
|
1236
1361
|
} finally {
|
|
1237
1362
|
model.endUpdate();
|
|
1238
1363
|
}
|
|
@@ -1267,7 +1392,7 @@ var DrawMcp = (() => {
|
|
|
1267
1392
|
}
|
|
1268
1393
|
model.beginUpdate();
|
|
1269
1394
|
try {
|
|
1270
|
-
graph
|
|
1395
|
+
apply_style_to_cell(graph, cell, style);
|
|
1271
1396
|
} finally {
|
|
1272
1397
|
model.endUpdate();
|
|
1273
1398
|
}
|
|
@@ -1343,28 +1468,34 @@ var DrawMcp = (() => {
|
|
|
1343
1468
|
function add_cell_of_shape(ui2, options) {
|
|
1344
1469
|
const { editor } = ui2;
|
|
1345
1470
|
const { graph } = editor;
|
|
1346
|
-
const shape_name = options.shape_name
|
|
1471
|
+
const shape_name = options.shape_name;
|
|
1472
|
+
if (!shape_name) {
|
|
1473
|
+
throw new Error("add_cell_of_shape requires a shape_name");
|
|
1474
|
+
}
|
|
1347
1475
|
const x = options.x || 100;
|
|
1348
1476
|
const y = options.y || 100;
|
|
1349
1477
|
const width = options.width || 120;
|
|
1350
1478
|
const height = options.height || 80;
|
|
1351
1479
|
const text = options.text || "";
|
|
1352
1480
|
const style = options.style || "";
|
|
1481
|
+
const parent = resolve_parent(graph, options.parent_id);
|
|
1353
1482
|
const shape_entry = get_shape_by_name(ui2, { shape_name });
|
|
1354
1483
|
if (!shape_entry) return null;
|
|
1484
|
+
const mergedStyle = normalize_shape_style(
|
|
1485
|
+
shape_name,
|
|
1486
|
+
[shape_entry.style, style].filter((entry) => typeof entry === "string" && entry.trim().length > 0).join(";")
|
|
1487
|
+
);
|
|
1355
1488
|
graph.getModel().beginUpdate();
|
|
1356
1489
|
try {
|
|
1357
|
-
const cell = graph
|
|
1358
|
-
|
|
1359
|
-
null,
|
|
1490
|
+
const cell = create_shape_vertex(graph, {
|
|
1491
|
+
parent,
|
|
1360
1492
|
text,
|
|
1361
1493
|
x,
|
|
1362
1494
|
y,
|
|
1363
1495
|
width,
|
|
1364
1496
|
height,
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
);
|
|
1497
|
+
style: mergedStyle
|
|
1498
|
+
});
|
|
1368
1499
|
return cell;
|
|
1369
1500
|
} finally {
|
|
1370
1501
|
graph.getModel().endUpdate();
|
|
@@ -1406,7 +1537,29 @@ var DrawMcp = (() => {
|
|
|
1406
1537
|
}
|
|
1407
1538
|
return attributes;
|
|
1408
1539
|
}
|
|
1409
|
-
function
|
|
1540
|
+
function evaluate_filter_expression(expression, attributes) {
|
|
1541
|
+
if (!Array.isArray(expression) || expression.length === 0) {
|
|
1542
|
+
return true;
|
|
1543
|
+
}
|
|
1544
|
+
const [operator, ...operands] = expression;
|
|
1545
|
+
switch (operator) {
|
|
1546
|
+
case "and":
|
|
1547
|
+
return operands.every(
|
|
1548
|
+
(op) => evaluate_filter_expression(op, attributes)
|
|
1549
|
+
);
|
|
1550
|
+
case "or":
|
|
1551
|
+
return operands.some(
|
|
1552
|
+
(op) => evaluate_filter_expression(op, attributes)
|
|
1553
|
+
);
|
|
1554
|
+
case "equal":
|
|
1555
|
+
if (operands.length !== 2) return false;
|
|
1556
|
+
const [key, value] = operands;
|
|
1557
|
+
return attributes[key] === value;
|
|
1558
|
+
default:
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
function matches_cell_type(cell, cell_type, isLayer) {
|
|
1410
1563
|
switch (cell_type) {
|
|
1411
1564
|
case "edge":
|
|
1412
1565
|
return cell.edge === true || cell.edge === 1;
|
|
@@ -1416,6 +1569,8 @@ var DrawMcp = (() => {
|
|
|
1416
1569
|
return cell.value?.nodeName === "object";
|
|
1417
1570
|
case "group":
|
|
1418
1571
|
return cell.style === "group";
|
|
1572
|
+
case "layer":
|
|
1573
|
+
return isLayer(cell);
|
|
1419
1574
|
default:
|
|
1420
1575
|
return true;
|
|
1421
1576
|
}
|
|
@@ -1423,27 +1578,60 @@ var DrawMcp = (() => {
|
|
|
1423
1578
|
let filtered_cells = Object.values(cells);
|
|
1424
1579
|
if (options.filter) {
|
|
1425
1580
|
const filter = options.filter;
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1581
|
+
const allParentIds = [
|
|
1582
|
+
...filter.parent_ids || [],
|
|
1583
|
+
...filter.layer_ids || []
|
|
1584
|
+
];
|
|
1585
|
+
filtered_cells = filtered_cells.filter((cell) => {
|
|
1586
|
+
if (options.filter?.cell_type && !matches_cell_type(
|
|
1587
|
+
cell,
|
|
1588
|
+
options.filter.cell_type,
|
|
1589
|
+
mx_isLayer(model.root)
|
|
1590
|
+
)) {
|
|
1591
|
+
return false;
|
|
1592
|
+
}
|
|
1593
|
+
if (options.filter?.attributes && options.filter.attributes.length > 0) {
|
|
1594
|
+
const cellAttributes = extract_cell_attributes(cell);
|
|
1595
|
+
if (!evaluate_filter_expression(options.filter.attributes, cellAttributes)) {
|
|
1596
|
+
return false;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (allParentIds.length > 0) {
|
|
1600
|
+
const parent = cell.parent;
|
|
1601
|
+
if (!parent || !parent.id || !allParentIds.includes(parent.id)) {
|
|
1602
|
+
return false;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (filter.ids && filter.ids.length > 0) {
|
|
1606
|
+
if (!filter.ids.includes(cell.id)) {
|
|
1607
|
+
return false;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return true;
|
|
1611
|
+
});
|
|
1436
1612
|
}
|
|
1437
1613
|
const page = Math.max(0, options.page || 0);
|
|
1438
1614
|
const page_size = Math.max(1, options.page_size || 50);
|
|
1439
1615
|
const start_index = page * page_size;
|
|
1440
|
-
const
|
|
1616
|
+
const cell_ids = filtered_cells.map((cell) => cell.id);
|
|
1617
|
+
const paginated_ids = cell_ids.slice(start_index, start_index + page_size);
|
|
1441
1618
|
const transformed_cells = [];
|
|
1442
1619
|
for (const cell_id of paginated_ids) {
|
|
1443
1620
|
const cell = cells[cell_id];
|
|
1444
1621
|
if (cell) {
|
|
1445
1622
|
const sanitized_cell = remove_circular_dependencies(cell);
|
|
1446
|
-
|
|
1623
|
+
const transformed_cell = transform_cell_for_display(sanitized_cell);
|
|
1624
|
+
if (transformed_cell) {
|
|
1625
|
+
const layer_info = get_cell_layer(graph, cell);
|
|
1626
|
+
if (layer_info) {
|
|
1627
|
+
transformed_cell.layer = layer_info;
|
|
1628
|
+
}
|
|
1629
|
+
const tags_info = get_cell_tags(graph, cell);
|
|
1630
|
+
if (tags_info && tags_info.length > 0) {
|
|
1631
|
+
transformed_cell.tags = tags_info;
|
|
1632
|
+
}
|
|
1633
|
+
transformed_cells.push(transformed_cell);
|
|
1634
|
+
}
|
|
1447
1635
|
}
|
|
1448
1636
|
}
|
|
1449
1637
|
return transformed_cells;
|
|
@@ -1506,6 +1694,29 @@ var DrawMcp = (() => {
|
|
|
1506
1694
|
to_layer: options.target_layer_id
|
|
1507
1695
|
};
|
|
1508
1696
|
}
|
|
1697
|
+
function set_cell_parent(ui2, options) {
|
|
1698
|
+
const { editor } = ui2;
|
|
1699
|
+
const { graph } = editor;
|
|
1700
|
+
const model = graph.getModel();
|
|
1701
|
+
const cell = model.getCell(options.cell_id);
|
|
1702
|
+
if (!cell) {
|
|
1703
|
+
throw new Error(`Cell with ID ${options.cell_id} not found`);
|
|
1704
|
+
}
|
|
1705
|
+
const parent = model.getCell(options.parent_id);
|
|
1706
|
+
if (!parent) {
|
|
1707
|
+
throw new Error(`Parent cell with ID ${options.parent_id} not found`);
|
|
1708
|
+
}
|
|
1709
|
+
model.beginUpdate();
|
|
1710
|
+
try {
|
|
1711
|
+
model.add(parent, cell);
|
|
1712
|
+
} finally {
|
|
1713
|
+
model.endUpdate();
|
|
1714
|
+
}
|
|
1715
|
+
return {
|
|
1716
|
+
cell_id: options.cell_id,
|
|
1717
|
+
parent_id: options.parent_id
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1509
1720
|
function get_active_layer(ui2) {
|
|
1510
1721
|
const { editor } = ui2;
|
|
1511
1722
|
const { graph } = editor;
|
|
@@ -1534,8 +1745,541 @@ var DrawMcp = (() => {
|
|
|
1534
1745
|
name: options.name
|
|
1535
1746
|
};
|
|
1536
1747
|
}
|
|
1748
|
+
var PNG_SIZE_WARNING_THRESHOLD = 5 * 1024 * 1024;
|
|
1749
|
+
function export_diagram(ui2, options) {
|
|
1750
|
+
const format = options.format || "xml";
|
|
1751
|
+
const scale = options.scale ?? 1;
|
|
1752
|
+
const border = options.border ?? 0;
|
|
1753
|
+
const background = options.transparent ? null : options.background ?? null;
|
|
1754
|
+
const includeShadow = options.shadow ?? false;
|
|
1755
|
+
const cropToDiagram = options.crop ?? true;
|
|
1756
|
+
const selectionOnly = options.selection_only ?? false;
|
|
1757
|
+
const embedXml = options.embed_xml ?? false;
|
|
1758
|
+
const size = options.size ?? "diagram";
|
|
1759
|
+
const dpi = options.dpi ?? 96;
|
|
1760
|
+
switch (format) {
|
|
1761
|
+
case "xml":
|
|
1762
|
+
return export_xml(ui2, { selectionOnly, size });
|
|
1763
|
+
case "svg":
|
|
1764
|
+
return export_svg(ui2, {
|
|
1765
|
+
scale,
|
|
1766
|
+
border,
|
|
1767
|
+
background,
|
|
1768
|
+
includeShadow,
|
|
1769
|
+
cropToDiagram,
|
|
1770
|
+
selectionOnly,
|
|
1771
|
+
embedXml,
|
|
1772
|
+
size
|
|
1773
|
+
});
|
|
1774
|
+
case "png":
|
|
1775
|
+
return export_png(ui2, {
|
|
1776
|
+
scale,
|
|
1777
|
+
border,
|
|
1778
|
+
background,
|
|
1779
|
+
includeShadow,
|
|
1780
|
+
cropToDiagram,
|
|
1781
|
+
selectionOnly,
|
|
1782
|
+
embedXml,
|
|
1783
|
+
size,
|
|
1784
|
+
dpi
|
|
1785
|
+
});
|
|
1786
|
+
default:
|
|
1787
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
function export_xml(ui2, params) {
|
|
1791
|
+
const mxUtils = window.mxUtils;
|
|
1792
|
+
const { selectionOnly, size } = params;
|
|
1793
|
+
const ignoreSelection = !selectionOnly;
|
|
1794
|
+
const currentPage = size === "page" || size === "selection";
|
|
1795
|
+
const uncompressed = true;
|
|
1796
|
+
let xml;
|
|
1797
|
+
if (size === "selection" && selectionOnly) {
|
|
1798
|
+
const enc = new window.mxCodec();
|
|
1799
|
+
const node = enc.encode(ui2.editor.graph.getModel());
|
|
1800
|
+
xml = mxUtils.getXml(node);
|
|
1801
|
+
} else {
|
|
1802
|
+
const node = ui2.getXmlFileData(ignoreSelection, currentPage, uncompressed);
|
|
1803
|
+
xml = mxUtils.getXml(node);
|
|
1804
|
+
}
|
|
1805
|
+
return {
|
|
1806
|
+
format: "xml",
|
|
1807
|
+
mimeType: "application/xml",
|
|
1808
|
+
data: xml
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
function export_svg(ui2, params) {
|
|
1812
|
+
const mxUtils = window.mxUtils;
|
|
1813
|
+
const Graph = window.Graph;
|
|
1814
|
+
const {
|
|
1815
|
+
scale,
|
|
1816
|
+
border,
|
|
1817
|
+
background,
|
|
1818
|
+
includeShadow,
|
|
1819
|
+
selectionOnly,
|
|
1820
|
+
embedXml,
|
|
1821
|
+
size
|
|
1822
|
+
} = params;
|
|
1823
|
+
const graph = ui2.editor.graph;
|
|
1824
|
+
const ignoreSelection = !selectionOnly && size !== "selection";
|
|
1825
|
+
const bg = background !== "none" ? background : null;
|
|
1826
|
+
if (embedXml) {
|
|
1827
|
+
const currentPage = size === "page" || size === "selection";
|
|
1828
|
+
const svgString2 = ui2.getFileData(
|
|
1829
|
+
false,
|
|
1830
|
+
true,
|
|
1831
|
+
null,
|
|
1832
|
+
null,
|
|
1833
|
+
ignoreSelection,
|
|
1834
|
+
currentPage
|
|
1835
|
+
);
|
|
1836
|
+
const bounds = graph.getGraphBounds();
|
|
1837
|
+
const w2 = Math.ceil(bounds.width * scale + bounds.x * scale + 2 * border);
|
|
1838
|
+
const h2 = Math.ceil(bounds.height * scale + bounds.y * scale + 2 * border);
|
|
1839
|
+
return {
|
|
1840
|
+
format: "svg",
|
|
1841
|
+
mimeType: "image/svg+xml",
|
|
1842
|
+
data: svgString2,
|
|
1843
|
+
width: w2,
|
|
1844
|
+
height: h2
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
const svgRoot = graph.getSvg(
|
|
1848
|
+
bg,
|
|
1849
|
+
scale,
|
|
1850
|
+
border,
|
|
1851
|
+
!params.cropToDiagram,
|
|
1852
|
+
null,
|
|
1853
|
+
ignoreSelection
|
|
1854
|
+
);
|
|
1855
|
+
if (graph.shadowVisible || includeShadow) {
|
|
1856
|
+
graph.addSvgShadow(svgRoot, null, null, border === 0);
|
|
1857
|
+
}
|
|
1858
|
+
const svgString = (Graph.xmlDeclaration || '<?xml version="1.0" encoding="UTF-8"?>') + "\n" + (Graph.svgDoctype || "") + "\n" + mxUtils.getXml(svgRoot);
|
|
1859
|
+
const w = parseInt(svgRoot.getAttribute("width")) || 0;
|
|
1860
|
+
const h = parseInt(svgRoot.getAttribute("height")) || 0;
|
|
1861
|
+
return {
|
|
1862
|
+
format: "svg",
|
|
1863
|
+
mimeType: "image/svg+xml",
|
|
1864
|
+
data: svgString,
|
|
1865
|
+
width: w,
|
|
1866
|
+
height: h
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
function export_png(ui2, params) {
|
|
1870
|
+
return new Promise((resolve, reject) => {
|
|
1871
|
+
const {
|
|
1872
|
+
scale,
|
|
1873
|
+
border,
|
|
1874
|
+
background,
|
|
1875
|
+
includeShadow,
|
|
1876
|
+
selectionOnly,
|
|
1877
|
+
embedXml,
|
|
1878
|
+
size,
|
|
1879
|
+
dpi,
|
|
1880
|
+
cropToDiagram
|
|
1881
|
+
} = params;
|
|
1882
|
+
const imageCache = {};
|
|
1883
|
+
const ignoreSelection = !selectionOnly && size !== "selection";
|
|
1884
|
+
let xml = null;
|
|
1885
|
+
if (embedXml) {
|
|
1886
|
+
xml = ui2.getFileData(
|
|
1887
|
+
true,
|
|
1888
|
+
null,
|
|
1889
|
+
null,
|
|
1890
|
+
null,
|
|
1891
|
+
ignoreSelection,
|
|
1892
|
+
size === "page" || size === "selection"
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
ui2.exportToCanvas(
|
|
1896
|
+
(canvas) => {
|
|
1897
|
+
try {
|
|
1898
|
+
let dataUrl;
|
|
1899
|
+
if (xml != null) {
|
|
1900
|
+
dataUrl = ui2.createImageDataUri(canvas, xml, "png", dpi);
|
|
1901
|
+
} else {
|
|
1902
|
+
dataUrl = canvas.toDataURL("image/png");
|
|
1903
|
+
}
|
|
1904
|
+
const base64Data = dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
1905
|
+
const result = {
|
|
1906
|
+
format: "png",
|
|
1907
|
+
mimeType: "image/png",
|
|
1908
|
+
data: base64Data,
|
|
1909
|
+
width: canvas.width,
|
|
1910
|
+
height: canvas.height
|
|
1911
|
+
};
|
|
1912
|
+
if (base64Data.length > PNG_SIZE_WARNING_THRESHOLD) {
|
|
1913
|
+
const sizeMB = (base64Data.length / (1024 * 1024)).toFixed(1);
|
|
1914
|
+
result.warning = `PNG export is large (${sizeMB} MB). Consider using SVG or reducing scale to reduce size.`;
|
|
1915
|
+
}
|
|
1916
|
+
resolve(result);
|
|
1917
|
+
} catch (err) {
|
|
1918
|
+
reject(err);
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
null,
|
|
1922
|
+
imageCache,
|
|
1923
|
+
background,
|
|
1924
|
+
(err) => reject(err),
|
|
1925
|
+
null,
|
|
1926
|
+
ignoreSelection,
|
|
1927
|
+
scale,
|
|
1928
|
+
background === null,
|
|
1929
|
+
includeShadow,
|
|
1930
|
+
null,
|
|
1931
|
+
null,
|
|
1932
|
+
border,
|
|
1933
|
+
!cropToDiagram,
|
|
1934
|
+
false,
|
|
1935
|
+
null
|
|
1936
|
+
);
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
function extractXmlFromSvg(svgContent) {
|
|
1940
|
+
try {
|
|
1941
|
+
const parser = new DOMParser();
|
|
1942
|
+
const svgDoc = parser.parseFromString(svgContent, "image/svg+xml");
|
|
1943
|
+
const metadata = svgDoc.querySelector("metadata");
|
|
1944
|
+
if (metadata) {
|
|
1945
|
+
const mxfile2 = metadata.querySelector("mxfile");
|
|
1946
|
+
if (mxfile2) {
|
|
1947
|
+
const serializer = new XMLSerializer();
|
|
1948
|
+
return serializer.serializeToString(mxfile2);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
const defs = svgDoc.querySelector("defs");
|
|
1952
|
+
if (defs) {
|
|
1953
|
+
const mxfile2 = defs.querySelector("mxfile");
|
|
1954
|
+
if (mxfile2) {
|
|
1955
|
+
const serializer = new XMLSerializer();
|
|
1956
|
+
return serializer.serializeToString(mxfile2);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
const mxfile = svgDoc.querySelector("mxfile");
|
|
1960
|
+
if (mxfile) {
|
|
1961
|
+
const serializer = new XMLSerializer();
|
|
1962
|
+
return serializer.serializeToString(mxfile);
|
|
1963
|
+
}
|
|
1964
|
+
return null;
|
|
1965
|
+
} catch (error) {
|
|
1966
|
+
console.error("[import-diagram] Error extracting XML from SVG:", error);
|
|
1967
|
+
return null;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
function extractXmlFromPng(base64Png) {
|
|
1971
|
+
try {
|
|
1972
|
+
const binaryString = atob(base64Png);
|
|
1973
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
1974
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
1975
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
1976
|
+
}
|
|
1977
|
+
const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
1978
|
+
for (let i = 0; i < 8; i++) {
|
|
1979
|
+
if (bytes[i] !== pngSignature[i]) {
|
|
1980
|
+
console.error("[import-diagram] Invalid PNG signature");
|
|
1981
|
+
return null;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
let offset = 8;
|
|
1985
|
+
while (offset < bytes.length) {
|
|
1986
|
+
const length = bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
|
|
1987
|
+
const chunkType = String.fromCharCode(
|
|
1988
|
+
bytes[offset + 4],
|
|
1989
|
+
bytes[offset + 5],
|
|
1990
|
+
bytes[offset + 6],
|
|
1991
|
+
bytes[offset + 7]
|
|
1992
|
+
);
|
|
1993
|
+
if (chunkType === "tEXt") {
|
|
1994
|
+
const chunkData = bytes.slice(offset + 8, offset + 8 + length);
|
|
1995
|
+
let nullIndex = -1;
|
|
1996
|
+
for (let i = 0; i < chunkData.length; i++) {
|
|
1997
|
+
if (chunkData[i] === 0) {
|
|
1998
|
+
nullIndex = i;
|
|
1999
|
+
break;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
if (nullIndex !== -1) {
|
|
2003
|
+
const keyword = String.fromCharCode(...chunkData.slice(0, nullIndex));
|
|
2004
|
+
const text = String.fromCharCode(...chunkData.slice(nullIndex + 1));
|
|
2005
|
+
if (keyword === "mxGraphModel" || keyword === "mxfile" || text.includes("<mxfile") || text.includes("<mxGraphModel")) {
|
|
2006
|
+
return text;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
if (chunkType === "zTXt") {
|
|
2011
|
+
const chunkData = bytes.slice(offset + 8, offset + 8 + length);
|
|
2012
|
+
let nullIndex = -1;
|
|
2013
|
+
for (let i = 0; i < chunkData.length; i++) {
|
|
2014
|
+
if (chunkData[i] === 0) {
|
|
2015
|
+
nullIndex = i;
|
|
2016
|
+
break;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
if (nullIndex !== -1) {
|
|
2020
|
+
const keyword = String.fromCharCode(...chunkData.slice(0, nullIndex));
|
|
2021
|
+
const compressionMethod = chunkData[nullIndex + 1];
|
|
2022
|
+
if (compressionMethod === 0) {
|
|
2023
|
+
const compressedData = chunkData.slice(nullIndex + 2);
|
|
2024
|
+
if (typeof DecompressionStream !== "undefined") {
|
|
2025
|
+
console.warn(
|
|
2026
|
+
"[import-diagram] zTXt chunk found but decompression not implemented in sync context"
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
offset += 12 + length;
|
|
2033
|
+
}
|
|
2034
|
+
return null;
|
|
2035
|
+
} catch (error) {
|
|
2036
|
+
console.error("[import-diagram] Error extracting XML from PNG:", error);
|
|
2037
|
+
return null;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
function import_diagram(ui2, options) {
|
|
2041
|
+
const {
|
|
2042
|
+
data,
|
|
2043
|
+
format,
|
|
2044
|
+
mode = "replace",
|
|
2045
|
+
filename
|
|
2046
|
+
} = options;
|
|
2047
|
+
if (!data) {
|
|
2048
|
+
return { success: false, message: "No data provided" };
|
|
2049
|
+
}
|
|
2050
|
+
let xml = null;
|
|
2051
|
+
switch (format) {
|
|
2052
|
+
case "xml":
|
|
2053
|
+
xml = data;
|
|
2054
|
+
break;
|
|
2055
|
+
case "svg":
|
|
2056
|
+
let svgContent = data;
|
|
2057
|
+
if (!data.trim().startsWith("<")) {
|
|
2058
|
+
try {
|
|
2059
|
+
svgContent = atob(data);
|
|
2060
|
+
} catch (e) {
|
|
2061
|
+
return { success: false, message: "Invalid base64 SVG data" };
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
xml = extractXmlFromSvg(svgContent);
|
|
2065
|
+
if (!xml) {
|
|
2066
|
+
return { success: false, message: "Could not extract XML from SVG" };
|
|
2067
|
+
}
|
|
2068
|
+
break;
|
|
2069
|
+
case "png":
|
|
2070
|
+
xml = extractXmlFromPng(data);
|
|
2071
|
+
if (!xml) {
|
|
2072
|
+
return { success: false, message: "Could not extract XML from PNG" };
|
|
2073
|
+
}
|
|
2074
|
+
break;
|
|
2075
|
+
default:
|
|
2076
|
+
return { success: false, message: `Unsupported format: ${format}` };
|
|
2077
|
+
}
|
|
2078
|
+
if (!xml) {
|
|
2079
|
+
return { success: false, message: "Failed to extract XML from input" };
|
|
2080
|
+
}
|
|
2081
|
+
const mxUtils = window.mxUtils;
|
|
2082
|
+
if (!mxUtils) {
|
|
2083
|
+
return { success: false, message: "mxUtils not available" };
|
|
2084
|
+
}
|
|
2085
|
+
try {
|
|
2086
|
+
const doc = mxUtils.parseXml(xml);
|
|
2087
|
+
if (!doc || !doc.documentElement) {
|
|
2088
|
+
return { success: false, message: "Invalid XML structure" };
|
|
2089
|
+
}
|
|
2090
|
+
const root = doc.documentElement;
|
|
2091
|
+
if (root.nodeName !== "mxfile" && root.nodeName !== "mxGraphModel") {
|
|
2092
|
+
return {
|
|
2093
|
+
success: false,
|
|
2094
|
+
message: `Invalid Draw.io XML: expected mxfile or mxGraphModel, got ${root.nodeName}`
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
const { editor } = ui2;
|
|
2098
|
+
const { graph } = editor;
|
|
2099
|
+
const model = graph.getModel();
|
|
2100
|
+
let cellCount = 0;
|
|
2101
|
+
const cells = doc.getElementsByTagName("mxCell");
|
|
2102
|
+
cellCount = cells.length;
|
|
2103
|
+
switch (mode) {
|
|
2104
|
+
case "replace":
|
|
2105
|
+
let graphModelElement = doc.documentElement;
|
|
2106
|
+
if (graphModelElement.nodeName === "mxfile") {
|
|
2107
|
+
const diagram = graphModelElement.querySelector("diagram");
|
|
2108
|
+
if (diagram) {
|
|
2109
|
+
const mxGraphModel = diagram.querySelector("mxGraphModel");
|
|
2110
|
+
if (mxGraphModel) {
|
|
2111
|
+
graphModelElement = mxGraphModel;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
if (editor.setGraphXml) {
|
|
2116
|
+
editor.setGraphXml(graphModelElement);
|
|
2117
|
+
graph.refresh();
|
|
2118
|
+
graph.fit();
|
|
2119
|
+
} else {
|
|
2120
|
+
model.clear();
|
|
2121
|
+
model.setRoot(new window.mxCell());
|
|
2122
|
+
const codec = new window.mxCodec(
|
|
2123
|
+
graphModelElement.ownerDocument
|
|
2124
|
+
);
|
|
2125
|
+
codec.decode(graphModelElement, model);
|
|
2126
|
+
}
|
|
2127
|
+
return {
|
|
2128
|
+
success: true,
|
|
2129
|
+
message: `Diagram replaced successfully${filename ? ` from ${filename}` : ""}`,
|
|
2130
|
+
cells: cellCount
|
|
2131
|
+
};
|
|
2132
|
+
case "add":
|
|
2133
|
+
model.beginUpdate();
|
|
2134
|
+
try {
|
|
2135
|
+
const importedDoc = mxUtils.parseXml(xml);
|
|
2136
|
+
const importedRoot = importedDoc.documentElement;
|
|
2137
|
+
let graphModel = importedRoot;
|
|
2138
|
+
if (importedRoot.nodeName === "mxfile") {
|
|
2139
|
+
const diagram = importedRoot.querySelector("diagram");
|
|
2140
|
+
if (diagram) {
|
|
2141
|
+
graphModel = diagram.querySelector("mxGraphModel") || diagram;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
const importedRootCell = graphModel.querySelector("root");
|
|
2145
|
+
if (importedRootCell) {
|
|
2146
|
+
const defaultParent = graph.getDefaultParent();
|
|
2147
|
+
const importedCells = importedRootCell.querySelectorAll("mxCell");
|
|
2148
|
+
const idMapping = {};
|
|
2149
|
+
importedCells.forEach((cell) => {
|
|
2150
|
+
const oldId = cell.getAttribute("id");
|
|
2151
|
+
if (oldId === "0" || oldId === "1") {
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
const newCell = new window.mxCell();
|
|
2155
|
+
newCell.setId(null);
|
|
2156
|
+
if (cell.hasAttribute("value")) {
|
|
2157
|
+
newCell.setValue(cell.getAttribute("value"));
|
|
2158
|
+
}
|
|
2159
|
+
if (cell.hasAttribute("style")) {
|
|
2160
|
+
newCell.setStyle(cell.getAttribute("style"));
|
|
2161
|
+
}
|
|
2162
|
+
if (cell.hasAttribute("vertex")) {
|
|
2163
|
+
newCell.vertex = cell.getAttribute("vertex") === "1";
|
|
2164
|
+
}
|
|
2165
|
+
if (cell.hasAttribute("edge")) {
|
|
2166
|
+
newCell.edge = cell.getAttribute("edge") === "1";
|
|
2167
|
+
}
|
|
2168
|
+
const geo = cell.querySelector("mxGeometry");
|
|
2169
|
+
if (geo) {
|
|
2170
|
+
const geometry = new window.mxGeometry(
|
|
2171
|
+
parseFloat(geo.getAttribute("x") || "0"),
|
|
2172
|
+
parseFloat(geo.getAttribute("y") || "0"),
|
|
2173
|
+
parseFloat(geo.getAttribute("width") || "0"),
|
|
2174
|
+
parseFloat(geo.getAttribute("height") || "0")
|
|
2175
|
+
);
|
|
2176
|
+
if (geo.getAttribute("relative") === "1") {
|
|
2177
|
+
geometry.relative = true;
|
|
2178
|
+
}
|
|
2179
|
+
newCell.setGeometry(geometry);
|
|
2180
|
+
}
|
|
2181
|
+
model.add(defaultParent, newCell);
|
|
2182
|
+
idMapping[oldId] = newCell.getId();
|
|
2183
|
+
});
|
|
2184
|
+
importedCells.forEach((cell) => {
|
|
2185
|
+
const oldId = cell.getAttribute("id");
|
|
2186
|
+
const newId = idMapping[oldId];
|
|
2187
|
+
if (!newId) return;
|
|
2188
|
+
const newCell = model.getCell(newId);
|
|
2189
|
+
if (!newCell) return;
|
|
2190
|
+
const parentId = cell.getAttribute("parent");
|
|
2191
|
+
if (parentId && idMapping[parentId]) {
|
|
2192
|
+
const parentCell = model.getCell(idMapping[parentId]);
|
|
2193
|
+
if (parentCell) {
|
|
2194
|
+
model.add(parentCell, newCell);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
if (newCell.edge) {
|
|
2198
|
+
const sourceId = cell.getAttribute("source");
|
|
2199
|
+
const targetId = cell.getAttribute("target");
|
|
2200
|
+
if (sourceId && idMapping[sourceId]) {
|
|
2201
|
+
const sourceCell = model.getCell(idMapping[sourceId]);
|
|
2202
|
+
if (sourceCell) {
|
|
2203
|
+
model.setTerminal(newCell, sourceCell, true);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
if (targetId && idMapping[targetId]) {
|
|
2207
|
+
const targetCell = model.getCell(idMapping[targetId]);
|
|
2208
|
+
if (targetCell) {
|
|
2209
|
+
model.setTerminal(newCell, targetCell, false);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
} finally {
|
|
2216
|
+
model.endUpdate();
|
|
2217
|
+
}
|
|
2218
|
+
return {
|
|
2219
|
+
success: true,
|
|
2220
|
+
message: `Diagram imported successfully (added to current diagram)${filename ? ` from ${filename}` : ""}`,
|
|
2221
|
+
cells: cellCount
|
|
2222
|
+
};
|
|
2223
|
+
case "new-page":
|
|
2224
|
+
if (!ui2.pages || !ui2.insertPage) {
|
|
2225
|
+
return {
|
|
2226
|
+
success: false,
|
|
2227
|
+
message: "New page creation not supported in this Draw.io version"
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
const newPage = ui2.insertPage();
|
|
2231
|
+
if (!newPage) {
|
|
2232
|
+
return { success: false, message: "Failed to create new page" };
|
|
2233
|
+
}
|
|
2234
|
+
ui2.selectPage(newPage);
|
|
2235
|
+
model.beginUpdate();
|
|
2236
|
+
try {
|
|
2237
|
+
let pageGraphModel = doc.documentElement;
|
|
2238
|
+
if (pageGraphModel.nodeName === "mxfile") {
|
|
2239
|
+
const diagram = pageGraphModel.querySelector("diagram");
|
|
2240
|
+
if (diagram) {
|
|
2241
|
+
const mxGraphModel = diagram.querySelector("mxGraphModel");
|
|
2242
|
+
if (mxGraphModel) {
|
|
2243
|
+
pageGraphModel = mxGraphModel;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
if (editor.setGraphXml) {
|
|
2248
|
+
editor.setGraphXml(pageGraphModel);
|
|
2249
|
+
graph.refresh();
|
|
2250
|
+
graph.fit();
|
|
2251
|
+
} else {
|
|
2252
|
+
const codec = new window.mxCodec(
|
|
2253
|
+
pageGraphModel.ownerDocument
|
|
2254
|
+
);
|
|
2255
|
+
codec.decode(pageGraphModel, model);
|
|
2256
|
+
}
|
|
2257
|
+
} finally {
|
|
2258
|
+
model.endUpdate();
|
|
2259
|
+
}
|
|
2260
|
+
if (filename && newPage.setName) {
|
|
2261
|
+
const pageName = filename.replace(/\.[^/.]+$/, "");
|
|
2262
|
+
newPage.setName(pageName);
|
|
2263
|
+
}
|
|
2264
|
+
return {
|
|
2265
|
+
success: true,
|
|
2266
|
+
message: `Diagram imported successfully (new page created)${filename ? `: ${filename}` : ""}`,
|
|
2267
|
+
pages: ui2.pages.length,
|
|
2268
|
+
cells: cellCount
|
|
2269
|
+
};
|
|
2270
|
+
default:
|
|
2271
|
+
return { success: false, message: `Unsupported import mode: ${mode}` };
|
|
2272
|
+
}
|
|
2273
|
+
} catch (error) {
|
|
2274
|
+
console.error("[import-diagram] Error importing diagram:", error);
|
|
2275
|
+
return {
|
|
2276
|
+
success: false,
|
|
2277
|
+
message: `Import failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
1537
2281
|
|
|
1538
|
-
// src/plugin
|
|
2282
|
+
// src/plugin.ts
|
|
1539
2283
|
function reply_name(event_name, request_id) {
|
|
1540
2284
|
return `${event_name}.${request_id}`;
|
|
1541
2285
|
}
|
|
@@ -1551,31 +2295,42 @@ var DrawMcp = (() => {
|
|
|
1551
2295
|
},
|
|
1552
2296
|
{}
|
|
1553
2297
|
);
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
const result = executeFunction(ui, options);
|
|
1557
|
-
reply = {
|
|
2298
|
+
const sendReply = (result, success, error) => {
|
|
2299
|
+
const reply = {
|
|
1558
2300
|
__event: reply_name(toolName, request.__request_id),
|
|
1559
2301
|
__request_id: request.__request_id,
|
|
1560
|
-
success
|
|
1561
|
-
result: remove_circular_dependencies(result)
|
|
2302
|
+
success,
|
|
2303
|
+
result: success ? remove_circular_dependencies(result) : void 0,
|
|
2304
|
+
error: !success ? remove_circular_dependencies(error) : void 0
|
|
1562
2305
|
};
|
|
2306
|
+
if (wsManager) {
|
|
2307
|
+
wsManager.send(reply);
|
|
2308
|
+
}
|
|
2309
|
+
return reply;
|
|
2310
|
+
};
|
|
2311
|
+
try {
|
|
2312
|
+
const result = executeFunction(ui, options);
|
|
2313
|
+
if (result instanceof Promise) {
|
|
2314
|
+
result.then(
|
|
2315
|
+
(resolved) => sendReply(resolved, true),
|
|
2316
|
+
(err) => {
|
|
2317
|
+
console.error(
|
|
2318
|
+
`[plugin] Tool ${toolName} failed for request ID ${request.__request_id}:`,
|
|
2319
|
+
err
|
|
2320
|
+
);
|
|
2321
|
+
sendReply(void 0, false, err);
|
|
2322
|
+
}
|
|
2323
|
+
);
|
|
2324
|
+
return void 0;
|
|
2325
|
+
}
|
|
2326
|
+
return sendReply(result, true);
|
|
1563
2327
|
} catch (error) {
|
|
1564
2328
|
console.error(
|
|
1565
2329
|
`[plugin] Tool ${toolName} failed for request ID ${request.__request_id}:`,
|
|
1566
2330
|
error
|
|
1567
2331
|
);
|
|
1568
|
-
|
|
1569
|
-
__event: reply_name(toolName, request.__request_id),
|
|
1570
|
-
__request_id: request.__request_id,
|
|
1571
|
-
success: false,
|
|
1572
|
-
error: remove_circular_dependencies(error)
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
|
-
if (wsManager) {
|
|
1576
|
-
wsManager.send(reply);
|
|
2332
|
+
return sendReply(void 0, false, error);
|
|
1577
2333
|
}
|
|
1578
|
-
return reply;
|
|
1579
2334
|
};
|
|
1580
2335
|
};
|
|
1581
2336
|
var ui;
|
|
@@ -1591,7 +2346,7 @@ var DrawMcp = (() => {
|
|
|
1591
2346
|
},
|
|
1592
2347
|
{
|
|
1593
2348
|
name: "add-rectangle",
|
|
1594
|
-
params: /* @__PURE__ */ new Set(["x", "y", "width", "height", "text", "style"]),
|
|
2349
|
+
params: /* @__PURE__ */ new Set(["x", "y", "width", "height", "text", "style", "parent_id"]),
|
|
1595
2350
|
handler: add_new_rectangle
|
|
1596
2351
|
},
|
|
1597
2352
|
{
|
|
@@ -1601,7 +2356,7 @@ var DrawMcp = (() => {
|
|
|
1601
2356
|
},
|
|
1602
2357
|
{
|
|
1603
2358
|
name: "add-edge",
|
|
1604
|
-
params: /* @__PURE__ */ new Set(["source_id", "target_id", "style", "text"]),
|
|
2359
|
+
params: /* @__PURE__ */ new Set(["source_id", "target_id", "style", "text", "parent_id", "points"]),
|
|
1605
2360
|
handler: add_edge
|
|
1606
2361
|
},
|
|
1607
2362
|
{
|
|
@@ -1621,7 +2376,16 @@ var DrawMcp = (() => {
|
|
|
1621
2376
|
},
|
|
1622
2377
|
{
|
|
1623
2378
|
name: "add-cell-of-shape",
|
|
1624
|
-
params: /* @__PURE__ */ new Set([
|
|
2379
|
+
params: /* @__PURE__ */ new Set([
|
|
2380
|
+
"shape_name",
|
|
2381
|
+
"x",
|
|
2382
|
+
"y",
|
|
2383
|
+
"width",
|
|
2384
|
+
"height",
|
|
2385
|
+
"text",
|
|
2386
|
+
"style",
|
|
2387
|
+
"parent_id"
|
|
2388
|
+
]),
|
|
1625
2389
|
handler: add_cell_of_shape
|
|
1626
2390
|
},
|
|
1627
2391
|
{
|
|
@@ -1633,8 +2397,7 @@ var DrawMcp = (() => {
|
|
|
1633
2397
|
name: "set-cell-data",
|
|
1634
2398
|
params: /* @__PURE__ */ new Set(["cell_id", "key", "value"]),
|
|
1635
2399
|
handler: (ui2, options) => {
|
|
1636
|
-
|
|
1637
|
-
return set_cell_data(mxUtils)(ui2, options);
|
|
2400
|
+
return set_cell_data(ui2, options);
|
|
1638
2401
|
}
|
|
1639
2402
|
},
|
|
1640
2403
|
{
|
|
@@ -1649,7 +2412,7 @@ var DrawMcp = (() => {
|
|
|
1649
2412
|
},
|
|
1650
2413
|
{
|
|
1651
2414
|
name: "edit-edge",
|
|
1652
|
-
params: /* @__PURE__ */ new Set(["cell_id", "text", "source_id", "target_id", "style"]),
|
|
2415
|
+
params: /* @__PURE__ */ new Set(["cell_id", "text", "source_id", "target_id", "style", "points"]),
|
|
1653
2416
|
handler: edit_edge
|
|
1654
2417
|
},
|
|
1655
2418
|
{
|
|
@@ -1667,6 +2430,11 @@ var DrawMcp = (() => {
|
|
|
1667
2430
|
params: /* @__PURE__ */ new Set(["cell_id", "target_layer_id"]),
|
|
1668
2431
|
handler: move_cell_to_layer
|
|
1669
2432
|
},
|
|
2433
|
+
{
|
|
2434
|
+
name: "set-cell-parent",
|
|
2435
|
+
params: /* @__PURE__ */ new Set(["cell_id", "parent_id"]),
|
|
2436
|
+
handler: set_cell_parent
|
|
2437
|
+
},
|
|
1670
2438
|
{
|
|
1671
2439
|
name: "get-active-layer",
|
|
1672
2440
|
params: /* @__PURE__ */ new Set([]),
|
|
@@ -1676,6 +2444,28 @@ var DrawMcp = (() => {
|
|
|
1676
2444
|
name: "create-layer",
|
|
1677
2445
|
params: /* @__PURE__ */ new Set(["name"]),
|
|
1678
2446
|
handler: create_layer
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
name: "export-diagram",
|
|
2450
|
+
params: /* @__PURE__ */ new Set([
|
|
2451
|
+
"format",
|
|
2452
|
+
"scale",
|
|
2453
|
+
"border",
|
|
2454
|
+
"background",
|
|
2455
|
+
"shadow",
|
|
2456
|
+
"crop",
|
|
2457
|
+
"selection_only",
|
|
2458
|
+
"transparent",
|
|
2459
|
+
"dpi",
|
|
2460
|
+
"embed_xml",
|
|
2461
|
+
"size"
|
|
2462
|
+
]),
|
|
2463
|
+
handler: export_diagram
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
name: "import-diagram",
|
|
2467
|
+
params: /* @__PURE__ */ new Set(["data", "format", "mode", "filename"]),
|
|
2468
|
+
handler: import_diagram
|
|
1679
2469
|
}
|
|
1680
2470
|
];
|
|
1681
2471
|
var toolHandlers = /* @__PURE__ */ new Map();
|
|
@@ -1803,6 +2593,9 @@ var DrawMcp = (() => {
|
|
|
1803
2593
|
window.Draw.loadPlugin((drawioUI) => {
|
|
1804
2594
|
console.debug("[plugin] Plugin loaded successfully");
|
|
1805
2595
|
ui = drawioUI;
|
|
2596
|
+
if (window.__DRAWIO_MCP_TEST_HOOKS__ === true) {
|
|
2597
|
+
window.ui = drawioUI;
|
|
2598
|
+
}
|
|
1806
2599
|
toolDefinitions.forEach((def) => {
|
|
1807
2600
|
const handler = createToolHandler(def.name, def.params, def.handler);
|
|
1808
2601
|
toolHandlers.set(def.name, handler);
|