drawio-mcp-server 1.7.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 +96 -425
- package/build/assets/downloader.js +96 -0
- package/build/assets/index.js +2 -0
- package/build/assets/manager.js +31 -0
- package/build/config.js +45 -0
- package/build/config.test.js +54 -0
- package/build/emitter_bus.js +2 -3
- package/build/index.js +305 -337
- package/build/plugin/mcp-plugin.js +2618 -0
- 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 +29 -22
|
@@ -0,0 +1,2618 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var DrawMcp = (() => {
|
|
3
|
+
// src/shape-library.ts
|
|
4
|
+
var shapeLibrary = {
|
|
5
|
+
rectangle: {
|
|
6
|
+
category: "general",
|
|
7
|
+
style: "rounded=0;whiteSpace=wrap;html=1;"
|
|
8
|
+
},
|
|
9
|
+
rounded_rectangle: {
|
|
10
|
+
category: "general",
|
|
11
|
+
style: "rounded=1;whiteSpace=wrap;html=1;"
|
|
12
|
+
},
|
|
13
|
+
text: {
|
|
14
|
+
category: "general",
|
|
15
|
+
style: "text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;"
|
|
16
|
+
},
|
|
17
|
+
ellipse: {
|
|
18
|
+
category: "general",
|
|
19
|
+
style: "ellipse;whiteSpace=wrap;html=1;"
|
|
20
|
+
},
|
|
21
|
+
square: {
|
|
22
|
+
category: "general",
|
|
23
|
+
style: "whiteSpace=wrap;html=1;aspect=fixed;"
|
|
24
|
+
},
|
|
25
|
+
circle: {
|
|
26
|
+
category: "general",
|
|
27
|
+
style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;"
|
|
28
|
+
},
|
|
29
|
+
process: {
|
|
30
|
+
category: "general",
|
|
31
|
+
style: "shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;"
|
|
32
|
+
},
|
|
33
|
+
diamond: {
|
|
34
|
+
category: "general",
|
|
35
|
+
style: "rhombus;whiteSpace=wrap;html=1;"
|
|
36
|
+
},
|
|
37
|
+
parallelogram: {
|
|
38
|
+
category: "general",
|
|
39
|
+
style: "shape=parallelogram;perimeter=parallelogramPerimeter;whiteSpace=wrap;html=1;fixedSize=1;"
|
|
40
|
+
},
|
|
41
|
+
hexagon: {
|
|
42
|
+
category: "general",
|
|
43
|
+
style: "shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;"
|
|
44
|
+
},
|
|
45
|
+
triangle: {
|
|
46
|
+
category: "general",
|
|
47
|
+
style: "triangle;whiteSpace=wrap;html=1;"
|
|
48
|
+
},
|
|
49
|
+
cylinder3: {
|
|
50
|
+
category: "general",
|
|
51
|
+
style: "shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;"
|
|
52
|
+
},
|
|
53
|
+
cloud: {
|
|
54
|
+
category: "general",
|
|
55
|
+
style: "ellipse;shape=cloud;whiteSpace=wrap;html=1;"
|
|
56
|
+
},
|
|
57
|
+
document: {
|
|
58
|
+
category: "general",
|
|
59
|
+
style: "shape=document;whiteSpace=wrap;html=1;boundedLbl=1;"
|
|
60
|
+
},
|
|
61
|
+
internalStorage: {
|
|
62
|
+
category: "general",
|
|
63
|
+
style: "shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;"
|
|
64
|
+
},
|
|
65
|
+
cube: {
|
|
66
|
+
category: "general",
|
|
67
|
+
style: "shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;darkOpacity2=0.1;"
|
|
68
|
+
},
|
|
69
|
+
step: {
|
|
70
|
+
category: "general",
|
|
71
|
+
style: "shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;"
|
|
72
|
+
},
|
|
73
|
+
trapezoid: {
|
|
74
|
+
category: "general",
|
|
75
|
+
style: "shape=trapezoid;perimeter=trapezoidPerimeter;whiteSpace=wrap;html=1;fixedSize=1;"
|
|
76
|
+
},
|
|
77
|
+
tape: {
|
|
78
|
+
category: "general",
|
|
79
|
+
style: "shape=tape;whiteSpace=wrap;html=1;"
|
|
80
|
+
},
|
|
81
|
+
note: {
|
|
82
|
+
category: "general",
|
|
83
|
+
style: "shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;"
|
|
84
|
+
},
|
|
85
|
+
card: {
|
|
86
|
+
category: "general",
|
|
87
|
+
style: "shape=card;whiteSpace=wrap;html=1;"
|
|
88
|
+
},
|
|
89
|
+
callout: {
|
|
90
|
+
category: "general",
|
|
91
|
+
style: "shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;"
|
|
92
|
+
},
|
|
93
|
+
umlActor: {
|
|
94
|
+
category: "general",
|
|
95
|
+
style: "shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;"
|
|
96
|
+
},
|
|
97
|
+
xor: {
|
|
98
|
+
category: "general",
|
|
99
|
+
style: "shape=xor;whiteSpace=wrap;html=1;"
|
|
100
|
+
},
|
|
101
|
+
or: {
|
|
102
|
+
category: "general",
|
|
103
|
+
style: "shape=or;whiteSpace=wrap;html=1;"
|
|
104
|
+
},
|
|
105
|
+
dataStorage: {
|
|
106
|
+
category: "general",
|
|
107
|
+
style: "shape=dataStorage;whiteSpace=wrap;html=1;fixedSize=1;"
|
|
108
|
+
},
|
|
109
|
+
swimlane: {
|
|
110
|
+
category: "general",
|
|
111
|
+
title: "Container",
|
|
112
|
+
style: "swimlane;startSize=0;"
|
|
113
|
+
},
|
|
114
|
+
verticalContainer: {
|
|
115
|
+
category: "general",
|
|
116
|
+
title: "Vertical Container",
|
|
117
|
+
style: "swimlane;whiteSpace=wrap;html=1;"
|
|
118
|
+
},
|
|
119
|
+
horizontalContainer: {
|
|
120
|
+
category: "general",
|
|
121
|
+
title: "Horizontal Container",
|
|
122
|
+
style: "swimlane;horizontal=0;whiteSpace=wrap;html=1;"
|
|
123
|
+
},
|
|
124
|
+
list: {
|
|
125
|
+
category: "general",
|
|
126
|
+
title: "List",
|
|
127
|
+
style: "swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;"
|
|
128
|
+
},
|
|
129
|
+
listItem: {
|
|
130
|
+
category: "general",
|
|
131
|
+
title: "List Item",
|
|
132
|
+
style: "text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;"
|
|
133
|
+
},
|
|
134
|
+
curve: {
|
|
135
|
+
category: "general",
|
|
136
|
+
style: "curved=1;endArrow=classic;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;"
|
|
137
|
+
},
|
|
138
|
+
flexArrow: {
|
|
139
|
+
category: "general",
|
|
140
|
+
title: "Bidirectional Arrow",
|
|
141
|
+
style: "shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;curved=1;"
|
|
142
|
+
},
|
|
143
|
+
arrow: {
|
|
144
|
+
category: "general",
|
|
145
|
+
title: "Arrow",
|
|
146
|
+
style: "shape=flexArrow;endArrow=classic;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;curved=1;"
|
|
147
|
+
},
|
|
148
|
+
dashedLine: {
|
|
149
|
+
category: "general",
|
|
150
|
+
style: "endArrow=none;dashed=1;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;curved=1;"
|
|
151
|
+
},
|
|
152
|
+
dottedLine: {
|
|
153
|
+
category: "general",
|
|
154
|
+
style: "endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;fontSize=12;startSize=8;endSize=8;"
|
|
155
|
+
},
|
|
156
|
+
line: {
|
|
157
|
+
category: "general",
|
|
158
|
+
style: "endArrow=none;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;curved=1;"
|
|
159
|
+
},
|
|
160
|
+
bidirectionalConnector: {
|
|
161
|
+
category: "general",
|
|
162
|
+
style: "endArrow=classic;startArrow=classic;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;curved=1;"
|
|
163
|
+
},
|
|
164
|
+
directionalConnector: {
|
|
165
|
+
category: "general",
|
|
166
|
+
style: "endArrow=classic;html=1;rounded=0;fontSize=12;startSize=8;endSize=8;curved=1;"
|
|
167
|
+
},
|
|
168
|
+
// AWS / Analytics
|
|
169
|
+
"mxgraph.aws4.athena": {
|
|
170
|
+
category: "mxgraph.aws4.analytics",
|
|
171
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.athena;"
|
|
172
|
+
},
|
|
173
|
+
"mxgraph.aws4.elasticsearch_service": {
|
|
174
|
+
category: "mxgraph.aws4.analytics",
|
|
175
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.elasticsearch_service;"
|
|
176
|
+
},
|
|
177
|
+
"mxgraph.aws4.kinesis": {
|
|
178
|
+
category: "mxgraph.aws4.analytics",
|
|
179
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.kinesis;"
|
|
180
|
+
},
|
|
181
|
+
"mxgraph.aws4.kinesis_data_analytics": {
|
|
182
|
+
category: "mxgraph.aws4.analytics",
|
|
183
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.kinesis_data_analytics;"
|
|
184
|
+
},
|
|
185
|
+
"mxgraph.aws4.kinesis_data_firehose": {
|
|
186
|
+
category: "mxgraph.aws4.analytics",
|
|
187
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.kinesis_data_firehose;"
|
|
188
|
+
},
|
|
189
|
+
"mxgraph.aws4.kinesis_data_streams": {
|
|
190
|
+
category: "mxgraph.aws4.analytics",
|
|
191
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.kinesis_data_streams;"
|
|
192
|
+
},
|
|
193
|
+
"mxgraph.aws4.managed_service_for_apache_flink": {
|
|
194
|
+
category: "mxgraph.aws4.analytics",
|
|
195
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.managed_service_for_apache_flink;"
|
|
196
|
+
},
|
|
197
|
+
"mxgraph.aws4.quicksight": {
|
|
198
|
+
category: "mxgraph.aws4.analytics",
|
|
199
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.quicksight;"
|
|
200
|
+
},
|
|
201
|
+
"mxgraph.aws4.managed_streaming_for_kafka": {
|
|
202
|
+
category: "mxgraph.aws4.analytics",
|
|
203
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.managed_streaming_for_kafka;"
|
|
204
|
+
},
|
|
205
|
+
"mxgraph.aws4.glue": {
|
|
206
|
+
category: "mxgraph.aws4.analytics",
|
|
207
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.glue;"
|
|
208
|
+
},
|
|
209
|
+
// AWS / Compute
|
|
210
|
+
"mxgraph.aws4.compute": {
|
|
211
|
+
category: "mxgraph.aws4.compute",
|
|
212
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.compute;"
|
|
213
|
+
},
|
|
214
|
+
"mxgraph.aws4.ec2": {
|
|
215
|
+
category: "mxgraph.aws4.compute",
|
|
216
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;"
|
|
217
|
+
},
|
|
218
|
+
"mxgraph.aws4.lambda": {
|
|
219
|
+
category: "mxgraph.aws4.compute",
|
|
220
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.lambda;"
|
|
221
|
+
},
|
|
222
|
+
// AWS / Containers
|
|
223
|
+
"mxgraph.aws4.containers": {
|
|
224
|
+
category: "mxgraph.aws4.containers",
|
|
225
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.containers;"
|
|
226
|
+
},
|
|
227
|
+
"mxgraph.aws4.eks": {
|
|
228
|
+
category: "mxgraph.aws4.containers",
|
|
229
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.eks;"
|
|
230
|
+
},
|
|
231
|
+
"mxgraph.aws4.ecr": {
|
|
232
|
+
category: "mxgraph.aws4.containers",
|
|
233
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ecr;"
|
|
234
|
+
},
|
|
235
|
+
"mxgraph.aws4.ecs": {
|
|
236
|
+
category: "mxgraph.aws4.containers",
|
|
237
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ecs;"
|
|
238
|
+
},
|
|
239
|
+
"mxgraph.aws4.fargate": {
|
|
240
|
+
category: "mxgraph.aws4.containers",
|
|
241
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.fargate;"
|
|
242
|
+
},
|
|
243
|
+
// AWS / Network & Content Delivery
|
|
244
|
+
"mxgraph.aws4.api_gateway": {
|
|
245
|
+
category: "mxgraph.aws4.network",
|
|
246
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.api_gateway;"
|
|
247
|
+
},
|
|
248
|
+
"mxgraph.aws4.cloudfront": {
|
|
249
|
+
category: "mxgraph.aws4.network",
|
|
250
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.cloudfront;"
|
|
251
|
+
},
|
|
252
|
+
"mxgraph.aws4.route_53": {
|
|
253
|
+
category: "mxgraph.aws4.network",
|
|
254
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.route_53;"
|
|
255
|
+
},
|
|
256
|
+
"mxgraph.aws4.vpc_privatelink": {
|
|
257
|
+
category: "mxgraph.aws4.network",
|
|
258
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.vpc_privatelink;"
|
|
259
|
+
},
|
|
260
|
+
"mxgraph.aws4.vpc": {
|
|
261
|
+
category: "mxgraph.aws4.network",
|
|
262
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.vpc;"
|
|
263
|
+
},
|
|
264
|
+
"mxgraph.aws4.elastic_load_balancing": {
|
|
265
|
+
category: "mxgraph.aws4.network",
|
|
266
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#8C4FFF;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.elastic_load_balancing;"
|
|
267
|
+
},
|
|
268
|
+
"mxgraph.aws4.endpoint": {
|
|
269
|
+
category: "mxgraph.aws4.network",
|
|
270
|
+
style: "sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#8C4FFF;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.endpoint;"
|
|
271
|
+
},
|
|
272
|
+
"mxgraph.aws4.customer_gateway": {
|
|
273
|
+
category: "mxgraph.aws4.network",
|
|
274
|
+
style: "sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#8C4FFF;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.customer_gateway;"
|
|
275
|
+
},
|
|
276
|
+
"mxgraph.aws4.application_load_balancer": {
|
|
277
|
+
category: "mxgraph.aws4.network",
|
|
278
|
+
style: "sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#8C4FFF;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.application_load_balancer;"
|
|
279
|
+
},
|
|
280
|
+
"mxgraph.aws4.network_load_balancer": {
|
|
281
|
+
category: "mxgraph.aws4.network",
|
|
282
|
+
style: "sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#8C4FFF;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.network_load_balancer;"
|
|
283
|
+
},
|
|
284
|
+
// AWS / Application Integration
|
|
285
|
+
"mxgraph.aws4.sns": {
|
|
286
|
+
category: "mxgraph.aws4.application_integration",
|
|
287
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#E7157B;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.sns;"
|
|
288
|
+
},
|
|
289
|
+
"mxgraph.aws4.sqs": {
|
|
290
|
+
category: "mxgraph.aws4.application_integration",
|
|
291
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#E7157B;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.sqs;"
|
|
292
|
+
},
|
|
293
|
+
"mxgraph.aws4.eventbridge": {
|
|
294
|
+
category: "mxgraph.aws4.application_integration",
|
|
295
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#E7157B;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.eventbridge;"
|
|
296
|
+
},
|
|
297
|
+
"mxgraph.aws4.managed_workflows_for_apache_airflow": {
|
|
298
|
+
category: "mxgraph.aws4.application_integration",
|
|
299
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#E7157B;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.managed_workflows_for_apache_airflow;"
|
|
300
|
+
},
|
|
301
|
+
// AWS / Database
|
|
302
|
+
"mxgraph.aws4.database": {
|
|
303
|
+
category: "mxgraph.aws4.database",
|
|
304
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.database;"
|
|
305
|
+
},
|
|
306
|
+
"mxgraph.aws4.aurora": {
|
|
307
|
+
category: "mxgraph.aws4.database",
|
|
308
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.aurora;"
|
|
309
|
+
},
|
|
310
|
+
"mxgraph.aws4.documentdb_with_mongodb_compatibility": {
|
|
311
|
+
category: "mxgraph.aws4.database",
|
|
312
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.documentdb_with_mongodb_compatibility;"
|
|
313
|
+
},
|
|
314
|
+
"mxgraph.aws4.dynamodb": {
|
|
315
|
+
category: "mxgraph.aws4.database",
|
|
316
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.dynamodb;"
|
|
317
|
+
},
|
|
318
|
+
"mxgraph.aws4.elasticache": {
|
|
319
|
+
category: "mxgraph.aws4.database",
|
|
320
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.elasticache;"
|
|
321
|
+
},
|
|
322
|
+
"mxgraph.aws4.neptune": {
|
|
323
|
+
category: "mxgraph.aws4.database",
|
|
324
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.neptune;"
|
|
325
|
+
},
|
|
326
|
+
"mxgraph.aws4.rds": {
|
|
327
|
+
category: "mxgraph.aws4.database",
|
|
328
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.rds;"
|
|
329
|
+
},
|
|
330
|
+
"mxgraph.aws4.redshift": {
|
|
331
|
+
category: "mxgraph.aws4.database",
|
|
332
|
+
style: "sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#C925D1;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.redshift;"
|
|
333
|
+
},
|
|
334
|
+
"mxgraph.aws4.": {
|
|
335
|
+
category: "mxgraph.aws4.database",
|
|
336
|
+
style: ""
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// src/websocketManager.ts
|
|
341
|
+
function createWebSocketManager(options) {
|
|
342
|
+
let socket = null;
|
|
343
|
+
let reconnectAttempts = 0;
|
|
344
|
+
let currentState = "disconnected";
|
|
345
|
+
let messageHandler = null;
|
|
346
|
+
let pingIntervalId = null;
|
|
347
|
+
let reconnectTimeoutId = null;
|
|
348
|
+
const {
|
|
349
|
+
url,
|
|
350
|
+
maxReconnectAttempts = 5,
|
|
351
|
+
reconnectDelay = 3e3,
|
|
352
|
+
pingInterval = 3e4
|
|
353
|
+
} = options;
|
|
354
|
+
const updateState = (newState) => {
|
|
355
|
+
if (currentState !== newState) {
|
|
356
|
+
console.debug(
|
|
357
|
+
`[websocketManager] State change: ${currentState} \u2192 ${newState}`
|
|
358
|
+
);
|
|
359
|
+
currentState = newState;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const connect = () => {
|
|
363
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
364
|
+
console.debug("[websocketManager] Already connected");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
updateState("connecting");
|
|
368
|
+
try {
|
|
369
|
+
socket = new WebSocket(url);
|
|
370
|
+
socket.addEventListener("open", () => {
|
|
371
|
+
console.debug("[websocketManager] WebSocket connection established");
|
|
372
|
+
reconnectAttempts = 0;
|
|
373
|
+
updateState("connected");
|
|
374
|
+
startPing();
|
|
375
|
+
});
|
|
376
|
+
socket.addEventListener("message", (event) => {
|
|
377
|
+
try {
|
|
378
|
+
const parsedMessage = JSON.parse(event.data);
|
|
379
|
+
console.debug("[websocketManager] Received message:", parsedMessage);
|
|
380
|
+
if (messageHandler) {
|
|
381
|
+
messageHandler(parsedMessage);
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error(
|
|
385
|
+
"[websocketManager] Failed to parse WebSocket message:",
|
|
386
|
+
error
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
socket.addEventListener("close", () => {
|
|
391
|
+
console.debug("[websocketManager] WebSocket connection closed");
|
|
392
|
+
updateState("disconnected");
|
|
393
|
+
stopPing();
|
|
394
|
+
attemptReconnect();
|
|
395
|
+
});
|
|
396
|
+
socket.addEventListener("error", (event) => {
|
|
397
|
+
console.error("[websocketManager] WebSocket error:", event);
|
|
398
|
+
updateState("disconnected");
|
|
399
|
+
});
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.error(
|
|
402
|
+
"[websocketManager] Failed to create WebSocket connection:",
|
|
403
|
+
error
|
|
404
|
+
);
|
|
405
|
+
updateState("disconnected");
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const disconnect = () => {
|
|
409
|
+
if (reconnectTimeoutId) {
|
|
410
|
+
clearTimeout(reconnectTimeoutId);
|
|
411
|
+
reconnectTimeoutId = null;
|
|
412
|
+
}
|
|
413
|
+
stopPing();
|
|
414
|
+
reconnectAttempts = 0;
|
|
415
|
+
if (socket) {
|
|
416
|
+
console.debug("[websocketManager] Closing WebSocket connection");
|
|
417
|
+
socket.close();
|
|
418
|
+
socket = null;
|
|
419
|
+
}
|
|
420
|
+
updateState("disconnected");
|
|
421
|
+
};
|
|
422
|
+
const send = (message) => {
|
|
423
|
+
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
|
424
|
+
console.warn(
|
|
425
|
+
"[websocketManager] Cannot send message: WebSocket not connected"
|
|
426
|
+
);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const serialized = JSON.stringify(message);
|
|
431
|
+
console.debug("[websocketManager] Sending message:", message);
|
|
432
|
+
socket.send(serialized);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.error("[websocketManager] Failed to send message:", error);
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const getState = () => currentState;
|
|
438
|
+
const onMessage = (handler) => {
|
|
439
|
+
messageHandler = handler;
|
|
440
|
+
};
|
|
441
|
+
const attemptReconnect = () => {
|
|
442
|
+
if (reconnectAttempts >= maxReconnectAttempts) {
|
|
443
|
+
console.error(
|
|
444
|
+
"[websocketManager] Max reconnection attempts reached. Giving up."
|
|
445
|
+
);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
reconnectAttempts++;
|
|
449
|
+
const delay = reconnectDelay * Math.pow(1.5, reconnectAttempts - 1);
|
|
450
|
+
console.log(
|
|
451
|
+
`[websocketManager] Attempting to reconnect in ${Math.round(delay / 1e3)} seconds... (attempt ${reconnectAttempts}/${maxReconnectAttempts})`
|
|
452
|
+
);
|
|
453
|
+
reconnectTimeoutId = window.setTimeout(() => {
|
|
454
|
+
reconnectTimeoutId = null;
|
|
455
|
+
connect();
|
|
456
|
+
}, delay);
|
|
457
|
+
};
|
|
458
|
+
const startPing = () => {
|
|
459
|
+
stopPing();
|
|
460
|
+
pingIntervalId = window.setInterval(() => {
|
|
461
|
+
if (getState() === "connected") {
|
|
462
|
+
send({ type: "PING" });
|
|
463
|
+
}
|
|
464
|
+
}, pingInterval);
|
|
465
|
+
};
|
|
466
|
+
const stopPing = () => {
|
|
467
|
+
if (pingIntervalId !== null) {
|
|
468
|
+
clearInterval(pingIntervalId);
|
|
469
|
+
pingIntervalId = null;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const ping = () => {
|
|
473
|
+
return new Promise((resolve) => {
|
|
474
|
+
if (getState() !== "connected") {
|
|
475
|
+
resolve(false);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const timeoutId = setTimeout(() => {
|
|
479
|
+
resolve(false);
|
|
480
|
+
}, 5e3);
|
|
481
|
+
send({ type: "PING", message: "Ping from plugin" });
|
|
482
|
+
setTimeout(() => {
|
|
483
|
+
clearTimeout(timeoutId);
|
|
484
|
+
resolve(getState() === "connected");
|
|
485
|
+
}, 1e3);
|
|
486
|
+
});
|
|
487
|
+
};
|
|
488
|
+
return {
|
|
489
|
+
connect,
|
|
490
|
+
disconnect,
|
|
491
|
+
send,
|
|
492
|
+
getState,
|
|
493
|
+
onMessage,
|
|
494
|
+
ping
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/pluginConfig.ts
|
|
499
|
+
var DEFAULT_PLUGIN_CONFIG = {
|
|
500
|
+
websocketPort: 3333,
|
|
501
|
+
serverUrl: ""
|
|
502
|
+
};
|
|
503
|
+
var PLUGIN_CONFIG_KEY = "drawio-mcp-plugin-config";
|
|
504
|
+
var cachedConfig = null;
|
|
505
|
+
async function fetchPluginConfig() {
|
|
506
|
+
if (cachedConfig) {
|
|
507
|
+
return cachedConfig;
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
const response = await fetch("/api/config");
|
|
511
|
+
if (response.ok) {
|
|
512
|
+
const serverConfig = await response.json();
|
|
513
|
+
cachedConfig = {
|
|
514
|
+
websocketPort: serverConfig.websocketPort || DEFAULT_PLUGIN_CONFIG.websocketPort,
|
|
515
|
+
serverUrl: serverConfig.serverUrl || window.location.origin
|
|
516
|
+
};
|
|
517
|
+
writePluginConfig(cachedConfig);
|
|
518
|
+
return cachedConfig;
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.warn("[pluginConfig] Failed to fetch config from server:", error);
|
|
522
|
+
}
|
|
523
|
+
return readPluginConfig();
|
|
524
|
+
}
|
|
525
|
+
function readPluginConfig() {
|
|
526
|
+
try {
|
|
527
|
+
const stored = localStorage.getItem(PLUGIN_CONFIG_KEY);
|
|
528
|
+
if (stored) {
|
|
529
|
+
const parsed = JSON.parse(stored);
|
|
530
|
+
const validated = validatePluginConfig(parsed);
|
|
531
|
+
if (validated.isValid && validated.config) {
|
|
532
|
+
return validated.config;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} catch (error) {
|
|
536
|
+
console.warn("[pluginConfig] Failed to read from localStorage:", error);
|
|
537
|
+
}
|
|
538
|
+
return { ...DEFAULT_PLUGIN_CONFIG };
|
|
539
|
+
}
|
|
540
|
+
function writePluginConfig(config) {
|
|
541
|
+
try {
|
|
542
|
+
localStorage.setItem(PLUGIN_CONFIG_KEY, JSON.stringify(config));
|
|
543
|
+
cachedConfig = config;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
console.error("[pluginConfig] Failed to write to localStorage:", error);
|
|
546
|
+
throw error;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
function validatePluginConfig(config) {
|
|
550
|
+
if (!config || typeof config !== "object") {
|
|
551
|
+
return { isValid: false, error: "Configuration must be an object" };
|
|
552
|
+
}
|
|
553
|
+
const cfg = config;
|
|
554
|
+
if (typeof cfg.websocketPort !== "number") {
|
|
555
|
+
return { isValid: false, error: "websocketPort must be a number" };
|
|
556
|
+
}
|
|
557
|
+
if (cfg.websocketPort < 1024 || cfg.websocketPort > 65535) {
|
|
558
|
+
return {
|
|
559
|
+
isValid: false,
|
|
560
|
+
error: "websocketPort must be between 1024 and 65535"
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
isValid: true,
|
|
565
|
+
config: {
|
|
566
|
+
websocketPort: cfg.websocketPort,
|
|
567
|
+
serverUrl: cfg.serverUrl || ""
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function buildWebSocketUrl(config) {
|
|
572
|
+
const host = window.location.hostname;
|
|
573
|
+
return `ws://${host}:${config.websocketPort}`;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/settingsDialog.ts
|
|
577
|
+
function createDialogContainer() {
|
|
578
|
+
const container = document.createElement("div");
|
|
579
|
+
container.id = "mcp-settings-dialog";
|
|
580
|
+
container.style.cssText = `
|
|
581
|
+
position: fixed;
|
|
582
|
+
top: 0;
|
|
583
|
+
left: 0;
|
|
584
|
+
width: 100%;
|
|
585
|
+
height: 100%;
|
|
586
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
587
|
+
display: flex;
|
|
588
|
+
justify-content: center;
|
|
589
|
+
align-items: center;
|
|
590
|
+
z-index: 10000;
|
|
591
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
592
|
+
`;
|
|
593
|
+
container.addEventListener("click", (e) => {
|
|
594
|
+
if (e.target === container) {
|
|
595
|
+
e.stopPropagation();
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
return container;
|
|
599
|
+
}
|
|
600
|
+
function createDialogContent() {
|
|
601
|
+
const content = document.createElement("div");
|
|
602
|
+
content.className = "dialog-content";
|
|
603
|
+
content.style.cssText = `
|
|
604
|
+
background: white;
|
|
605
|
+
border-radius: 8px;
|
|
606
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
607
|
+
max-width: 500px;
|
|
608
|
+
width: 90%;
|
|
609
|
+
max-height: 90vh;
|
|
610
|
+
overflow-y: auto;
|
|
611
|
+
position: relative;
|
|
612
|
+
`;
|
|
613
|
+
return content;
|
|
614
|
+
}
|
|
615
|
+
function createDialogHeader(title, onClose) {
|
|
616
|
+
const header = document.createElement("div");
|
|
617
|
+
header.className = "dialog-header";
|
|
618
|
+
header.style.cssText = `
|
|
619
|
+
padding: 16px 20px;
|
|
620
|
+
border-bottom: 1px solid #e0e0e0;
|
|
621
|
+
display: flex;
|
|
622
|
+
justify-content: space-between;
|
|
623
|
+
align-items: center;
|
|
624
|
+
`;
|
|
625
|
+
const titleEl = document.createElement("h2");
|
|
626
|
+
titleEl.textContent = title;
|
|
627
|
+
titleEl.style.cssText = `
|
|
628
|
+
margin: 0;
|
|
629
|
+
font-size: 18px;
|
|
630
|
+
font-weight: 600;
|
|
631
|
+
color: #333;
|
|
632
|
+
`;
|
|
633
|
+
const closeBtn = document.createElement("button");
|
|
634
|
+
closeBtn.textContent = "\xD7";
|
|
635
|
+
closeBtn.style.cssText = `
|
|
636
|
+
background: none;
|
|
637
|
+
border: none;
|
|
638
|
+
font-size: 24px;
|
|
639
|
+
cursor: pointer;
|
|
640
|
+
padding: 0;
|
|
641
|
+
width: 24px;
|
|
642
|
+
height: 24px;
|
|
643
|
+
display: flex;
|
|
644
|
+
align-items: center;
|
|
645
|
+
justify-content: center;
|
|
646
|
+
color: #666;
|
|
647
|
+
border-radius: 4px;
|
|
648
|
+
`;
|
|
649
|
+
closeBtn.title = "Close";
|
|
650
|
+
closeBtn.addEventListener("mouseenter", () => {
|
|
651
|
+
closeBtn.style.backgroundColor = "#f5f5f5";
|
|
652
|
+
});
|
|
653
|
+
closeBtn.addEventListener("mouseleave", () => {
|
|
654
|
+
closeBtn.style.backgroundColor = "transparent";
|
|
655
|
+
});
|
|
656
|
+
closeBtn.addEventListener("click", onClose);
|
|
657
|
+
header.appendChild(titleEl);
|
|
658
|
+
header.appendChild(closeBtn);
|
|
659
|
+
return header;
|
|
660
|
+
}
|
|
661
|
+
function createDialogBody(state, actions) {
|
|
662
|
+
const body = document.createElement("div");
|
|
663
|
+
body.className = "dialog-body";
|
|
664
|
+
body.style.cssText = `
|
|
665
|
+
padding: 20px;
|
|
666
|
+
`;
|
|
667
|
+
const statusSection = createStatusSection(state);
|
|
668
|
+
body.appendChild(statusSection);
|
|
669
|
+
const formSection = createFormSection(state, actions);
|
|
670
|
+
body.appendChild(formSection);
|
|
671
|
+
return body;
|
|
672
|
+
}
|
|
673
|
+
function createStatusSection(state) {
|
|
674
|
+
const section = document.createElement("div");
|
|
675
|
+
section.className = "status-section";
|
|
676
|
+
section.style.cssText = `
|
|
677
|
+
margin-bottom: 24px;
|
|
678
|
+
padding: 16px;
|
|
679
|
+
background: #f8f9fa;
|
|
680
|
+
border-radius: 6px;
|
|
681
|
+
border: 1px solid #e9ecef;
|
|
682
|
+
`;
|
|
683
|
+
const title = document.createElement("div");
|
|
684
|
+
title.style.cssText = `
|
|
685
|
+
font-weight: 600;
|
|
686
|
+
margin-bottom: 8px;
|
|
687
|
+
color: #333;
|
|
688
|
+
`;
|
|
689
|
+
title.textContent = "Connection Status";
|
|
690
|
+
const statusIndicator = document.createElement("div");
|
|
691
|
+
statusIndicator.className = "status-indicator";
|
|
692
|
+
statusIndicator.style.cssText = `
|
|
693
|
+
display: flex;
|
|
694
|
+
align-items: center;
|
|
695
|
+
gap: 8px;
|
|
696
|
+
margin-bottom: 4px;
|
|
697
|
+
`;
|
|
698
|
+
const indicator = document.createElement("div");
|
|
699
|
+
indicator.style.cssText = `
|
|
700
|
+
width: 12px;
|
|
701
|
+
height: 12px;
|
|
702
|
+
border-radius: 50%;
|
|
703
|
+
flex-shrink: 0;
|
|
704
|
+
`;
|
|
705
|
+
switch (state.connectionState) {
|
|
706
|
+
case "connected":
|
|
707
|
+
indicator.style.backgroundColor = "#28a745";
|
|
708
|
+
break;
|
|
709
|
+
case "connecting":
|
|
710
|
+
indicator.style.backgroundColor = "#ffc107";
|
|
711
|
+
break;
|
|
712
|
+
case "disconnected":
|
|
713
|
+
indicator.style.backgroundColor = "#dc3545";
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
const statusText = document.createElement("span");
|
|
717
|
+
statusText.textContent = state.connectionState.charAt(0).toUpperCase() + state.connectionState.slice(1);
|
|
718
|
+
statusText.style.fontWeight = "500";
|
|
719
|
+
statusIndicator.appendChild(indicator);
|
|
720
|
+
statusIndicator.appendChild(statusText);
|
|
721
|
+
const portDisplay = document.createElement("div");
|
|
722
|
+
portDisplay.style.cssText = `
|
|
723
|
+
font-size: 14px;
|
|
724
|
+
color: #666;
|
|
725
|
+
`;
|
|
726
|
+
portDisplay.textContent = `Port: ${state.config.websocketPort}`;
|
|
727
|
+
section.appendChild(title);
|
|
728
|
+
section.appendChild(statusIndicator);
|
|
729
|
+
section.appendChild(portDisplay);
|
|
730
|
+
return section;
|
|
731
|
+
}
|
|
732
|
+
function createFormSection(state, actions) {
|
|
733
|
+
const section = document.createElement("div");
|
|
734
|
+
section.className = "form-section";
|
|
735
|
+
const portGroup = document.createElement("div");
|
|
736
|
+
portGroup.className = "form-group";
|
|
737
|
+
portGroup.style.cssText = `
|
|
738
|
+
margin-bottom: 16px;
|
|
739
|
+
`;
|
|
740
|
+
const label = document.createElement("label");
|
|
741
|
+
label.textContent = "WebSocket Port";
|
|
742
|
+
label.htmlFor = "mcp-port-input";
|
|
743
|
+
label.style.cssText = `
|
|
744
|
+
display: block;
|
|
745
|
+
margin-bottom: 6px;
|
|
746
|
+
font-weight: 500;
|
|
747
|
+
color: #333;
|
|
748
|
+
`;
|
|
749
|
+
const input = document.createElement("input");
|
|
750
|
+
input.id = "mcp-port-input";
|
|
751
|
+
input.type = "number";
|
|
752
|
+
input.min = "1024";
|
|
753
|
+
input.max = "65535";
|
|
754
|
+
input.value = state.formData.port;
|
|
755
|
+
input.style.cssText = `
|
|
756
|
+
width: 100%;
|
|
757
|
+
padding: 8px 12px;
|
|
758
|
+
border: 1px solid #ddd;
|
|
759
|
+
border-radius: 4px;
|
|
760
|
+
font-size: 14px;
|
|
761
|
+
box-sizing: border-box;
|
|
762
|
+
`;
|
|
763
|
+
if (state.errors.port) {
|
|
764
|
+
input.style.borderColor = "#dc3545";
|
|
765
|
+
}
|
|
766
|
+
input.addEventListener("input", (e) => {
|
|
767
|
+
const value = e.target.value;
|
|
768
|
+
window.dispatchEvent(
|
|
769
|
+
new CustomEvent("mcp-settings-message", {
|
|
770
|
+
detail: { type: "UPDATE_PORT", port: value }
|
|
771
|
+
})
|
|
772
|
+
);
|
|
773
|
+
});
|
|
774
|
+
const hint = document.createElement("div");
|
|
775
|
+
hint.textContent = "Port must be between 1024 and 65535";
|
|
776
|
+
hint.style.cssText = `
|
|
777
|
+
font-size: 12px;
|
|
778
|
+
color: #666;
|
|
779
|
+
margin-top: 4px;
|
|
780
|
+
`;
|
|
781
|
+
if (state.errors.port) {
|
|
782
|
+
const error = document.createElement("div");
|
|
783
|
+
error.textContent = state.errors.port;
|
|
784
|
+
error.style.cssText = `
|
|
785
|
+
font-size: 12px;
|
|
786
|
+
color: #dc3545;
|
|
787
|
+
margin-top: 4px;
|
|
788
|
+
`;
|
|
789
|
+
portGroup.appendChild(error);
|
|
790
|
+
}
|
|
791
|
+
portGroup.appendChild(label);
|
|
792
|
+
portGroup.appendChild(input);
|
|
793
|
+
portGroup.appendChild(hint);
|
|
794
|
+
section.appendChild(portGroup);
|
|
795
|
+
return section;
|
|
796
|
+
}
|
|
797
|
+
function createDialogFooter(state, actions) {
|
|
798
|
+
const footer = document.createElement("div");
|
|
799
|
+
footer.className = "dialog-footer";
|
|
800
|
+
footer.style.cssText = `
|
|
801
|
+
padding: 16px 20px;
|
|
802
|
+
border-top: 1px solid #e0e0e0;
|
|
803
|
+
display: flex;
|
|
804
|
+
justify-content: space-between;
|
|
805
|
+
gap: 12px;
|
|
806
|
+
flex-wrap: wrap;
|
|
807
|
+
`;
|
|
808
|
+
const leftActions = document.createElement("div");
|
|
809
|
+
leftActions.style.cssText = `
|
|
810
|
+
display: flex;
|
|
811
|
+
gap: 8px;
|
|
812
|
+
flex-wrap: wrap;
|
|
813
|
+
`;
|
|
814
|
+
const rightActions = document.createElement("div");
|
|
815
|
+
rightActions.style.cssText = `
|
|
816
|
+
display: flex;
|
|
817
|
+
gap: 8px;
|
|
818
|
+
`;
|
|
819
|
+
const cancelBtn = createButton("Cancel", "ghost", () => {
|
|
820
|
+
window.dispatchEvent(
|
|
821
|
+
new CustomEvent("mcp-settings-message", {
|
|
822
|
+
detail: { type: "CLOSE" }
|
|
823
|
+
})
|
|
824
|
+
);
|
|
825
|
+
});
|
|
826
|
+
const saveBtn = createButton(
|
|
827
|
+
state.isSaving ? "Saving..." : "Save",
|
|
828
|
+
"primary",
|
|
829
|
+
() => {
|
|
830
|
+
window.dispatchEvent(
|
|
831
|
+
new CustomEvent("mcp-settings-message", {
|
|
832
|
+
detail: { type: "SAVE" }
|
|
833
|
+
})
|
|
834
|
+
);
|
|
835
|
+
},
|
|
836
|
+
state.isSaving
|
|
837
|
+
);
|
|
838
|
+
rightActions.appendChild(cancelBtn);
|
|
839
|
+
rightActions.appendChild(saveBtn);
|
|
840
|
+
footer.appendChild(leftActions);
|
|
841
|
+
footer.appendChild(rightActions);
|
|
842
|
+
return footer;
|
|
843
|
+
}
|
|
844
|
+
function createButton(text, variant, onClick, disabled = false) {
|
|
845
|
+
const btn = document.createElement("button");
|
|
846
|
+
btn.textContent = text;
|
|
847
|
+
btn.disabled = disabled;
|
|
848
|
+
btn.style.cssText = `
|
|
849
|
+
padding: 8px 16px;
|
|
850
|
+
border-radius: 4px;
|
|
851
|
+
font-size: 14px;
|
|
852
|
+
cursor: pointer;
|
|
853
|
+
border: 1px solid transparent;
|
|
854
|
+
transition: all 0.2s;
|
|
855
|
+
`;
|
|
856
|
+
switch (variant) {
|
|
857
|
+
case "primary":
|
|
858
|
+
btn.style.cssText += `
|
|
859
|
+
background-color: #007bff;
|
|
860
|
+
color: white;
|
|
861
|
+
border-color: #007bff;
|
|
862
|
+
`;
|
|
863
|
+
btn.addEventListener("mouseenter", () => {
|
|
864
|
+
if (!disabled) btn.style.backgroundColor = "#0056b3";
|
|
865
|
+
});
|
|
866
|
+
btn.addEventListener("mouseleave", () => {
|
|
867
|
+
if (!disabled) btn.style.backgroundColor = "#007bff";
|
|
868
|
+
});
|
|
869
|
+
break;
|
|
870
|
+
case "secondary":
|
|
871
|
+
btn.style.cssText += `
|
|
872
|
+
background-color: #6c757d;
|
|
873
|
+
color: white;
|
|
874
|
+
border-color: #6c757d;
|
|
875
|
+
`;
|
|
876
|
+
btn.addEventListener("mouseenter", () => {
|
|
877
|
+
if (!disabled) btn.style.backgroundColor = "#545b62";
|
|
878
|
+
});
|
|
879
|
+
btn.addEventListener("mouseleave", () => {
|
|
880
|
+
if (!disabled) btn.style.backgroundColor = "#6c757d";
|
|
881
|
+
});
|
|
882
|
+
break;
|
|
883
|
+
case "ghost":
|
|
884
|
+
btn.style.cssText += `
|
|
885
|
+
background-color: transparent;
|
|
886
|
+
color: #6c757d;
|
|
887
|
+
border-color: #6c757d;
|
|
888
|
+
`;
|
|
889
|
+
btn.addEventListener("mouseenter", () => {
|
|
890
|
+
if (!disabled) {
|
|
891
|
+
btn.style.backgroundColor = "#f8f9fa";
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
btn.addEventListener("mouseleave", () => {
|
|
895
|
+
if (!disabled) btn.style.backgroundColor = "transparent";
|
|
896
|
+
});
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
if (disabled) {
|
|
900
|
+
btn.style.cssText += `
|
|
901
|
+
opacity: 0.6;
|
|
902
|
+
cursor: not-allowed;
|
|
903
|
+
`;
|
|
904
|
+
}
|
|
905
|
+
btn.addEventListener("click", onClick);
|
|
906
|
+
return btn;
|
|
907
|
+
}
|
|
908
|
+
function createSettingsDialog(initialState, actions) {
|
|
909
|
+
let currentState = { ...initialState };
|
|
910
|
+
let dialogElement = null;
|
|
911
|
+
let isVisible = false;
|
|
912
|
+
const update = (newState) => {
|
|
913
|
+
currentState = { ...newState };
|
|
914
|
+
if (dialogElement && isVisible) {
|
|
915
|
+
render();
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
const render = () => {
|
|
919
|
+
if (dialogElement && dialogElement.parentNode) {
|
|
920
|
+
dialogElement.parentNode.removeChild(dialogElement);
|
|
921
|
+
}
|
|
922
|
+
const container = createDialogContainer();
|
|
923
|
+
const content = createDialogContent();
|
|
924
|
+
const header = createDialogHeader("MCP Settings", () => actions.onClose());
|
|
925
|
+
const body = createDialogBody(currentState, actions);
|
|
926
|
+
const footer = createDialogFooter(currentState, actions);
|
|
927
|
+
content.appendChild(header);
|
|
928
|
+
content.appendChild(body);
|
|
929
|
+
content.appendChild(footer);
|
|
930
|
+
container.appendChild(content);
|
|
931
|
+
dialogElement = container;
|
|
932
|
+
isVisible = true;
|
|
933
|
+
setupMessageListeners(container, actions);
|
|
934
|
+
return container;
|
|
935
|
+
};
|
|
936
|
+
const setupMessageListeners = (container, actions2) => {
|
|
937
|
+
const handleMessage = (event) => {
|
|
938
|
+
const message = event.detail;
|
|
939
|
+
switch (message.type) {
|
|
940
|
+
case "UPDATE_PORT":
|
|
941
|
+
currentState.formData.port = message.port;
|
|
942
|
+
delete currentState.errors.port;
|
|
943
|
+
break;
|
|
944
|
+
case "SAVE":
|
|
945
|
+
if (!validateAndSave()) {
|
|
946
|
+
update(currentState);
|
|
947
|
+
}
|
|
948
|
+
break;
|
|
949
|
+
case "CLOSE":
|
|
950
|
+
actions2.onClose();
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
window.addEventListener(
|
|
955
|
+
"mcp-settings-message",
|
|
956
|
+
handleMessage
|
|
957
|
+
);
|
|
958
|
+
const observer = new MutationObserver((mutations) => {
|
|
959
|
+
mutations.forEach((mutation) => {
|
|
960
|
+
mutation.removedNodes.forEach((node) => {
|
|
961
|
+
if (node === container) {
|
|
962
|
+
window.removeEventListener(
|
|
963
|
+
"mcp-settings-message",
|
|
964
|
+
handleMessage
|
|
965
|
+
);
|
|
966
|
+
observer.disconnect();
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
if (container.parentNode) {
|
|
972
|
+
observer.observe(container.parentNode, { childList: true });
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
const validateAndSave = () => {
|
|
976
|
+
const portNum = parseInt(currentState.formData.port, 10);
|
|
977
|
+
if (isNaN(portNum)) {
|
|
978
|
+
currentState.errors.port = "Port must be a number";
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
if (portNum < 1024 || portNum > 65535) {
|
|
982
|
+
currentState.errors.port = "Port must be between 1024 and 65535";
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
currentState.errors = {};
|
|
986
|
+
currentState.isSaving = true;
|
|
987
|
+
const newConfig = {
|
|
988
|
+
websocketPort: portNum,
|
|
989
|
+
serverUrl: currentState.config.serverUrl
|
|
990
|
+
};
|
|
991
|
+
actions.onSave(newConfig);
|
|
992
|
+
return true;
|
|
993
|
+
};
|
|
994
|
+
const dialogProxy = {
|
|
995
|
+
get element() {
|
|
996
|
+
if (!dialogElement) {
|
|
997
|
+
render();
|
|
998
|
+
}
|
|
999
|
+
return dialogElement;
|
|
1000
|
+
},
|
|
1001
|
+
update
|
|
1002
|
+
};
|
|
1003
|
+
return dialogProxy;
|
|
1004
|
+
}
|
|
1005
|
+
function showSettingsDialog(dialog) {
|
|
1006
|
+
if (!dialog.element.parentNode) {
|
|
1007
|
+
document.body.appendChild(dialog.element);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
function hideSettingsDialog(dialog) {
|
|
1011
|
+
if (dialog.element.parentNode) {
|
|
1012
|
+
dialog.element.parentNode.removeChild(dialog.element);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// src/drawio-tools.ts
|
|
1017
|
+
function transform_NamedNodeMap_to_record(attributes) {
|
|
1018
|
+
const tstr = Object.prototype.toString.call(attributes);
|
|
1019
|
+
if (tstr !== "[object NamedNodeMap]") {
|
|
1020
|
+
return attributes;
|
|
1021
|
+
}
|
|
1022
|
+
if (attributes.length === void 0) {
|
|
1023
|
+
return attributes;
|
|
1024
|
+
}
|
|
1025
|
+
let transformed_attributes = {};
|
|
1026
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
1027
|
+
const attr = attributes[i];
|
|
1028
|
+
if (attr && attr.name && attr.value !== void 0) {
|
|
1029
|
+
transformed_attributes[attr.name] = attr.value;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return transformed_attributes;
|
|
1033
|
+
}
|
|
1034
|
+
function transform_cells_NamedNodeMap_to_attributes(cell) {
|
|
1035
|
+
if (cell.value.attributes && typeof cell.value.attributes === "object") {
|
|
1036
|
+
const attributes = cell.value.attributes;
|
|
1037
|
+
return transform_NamedNodeMap_to_record(attributes);
|
|
1038
|
+
}
|
|
1039
|
+
return {};
|
|
1040
|
+
}
|
|
1041
|
+
function remove_circular_dependencies(obj, visited = /* @__PURE__ */ new WeakSet(), path = []) {
|
|
1042
|
+
if (obj === null || typeof obj !== "object") {
|
|
1043
|
+
return obj;
|
|
1044
|
+
}
|
|
1045
|
+
if (Array.isArray(obj)) {
|
|
1046
|
+
if (visited.has(obj)) {
|
|
1047
|
+
return `[Circular ${path.join(".")}]`;
|
|
1048
|
+
}
|
|
1049
|
+
visited.add(obj);
|
|
1050
|
+
return obj.map(
|
|
1051
|
+
(item, index) => remove_circular_dependencies(item, visited, [...path, `[${index}]`])
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
const tstr = Object.prototype.toString.call(obj);
|
|
1055
|
+
if (tstr === "[object NamedNodeMap]") {
|
|
1056
|
+
return transform_NamedNodeMap_to_record(obj);
|
|
1057
|
+
}
|
|
1058
|
+
if (tstr !== "[object Object]" && tstr !== "[object Element]") {
|
|
1059
|
+
return obj;
|
|
1060
|
+
}
|
|
1061
|
+
if (visited.has(obj)) {
|
|
1062
|
+
return `[Circular ${path.join(".")}]`;
|
|
1063
|
+
}
|
|
1064
|
+
visited.add(obj);
|
|
1065
|
+
const result = {};
|
|
1066
|
+
for (const key in obj) {
|
|
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" && 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
|
+
let stripped_value = {};
|
|
1070
|
+
if ((key === "parent" || key === "source" || key === "target") && value !== void 0 && value !== null) {
|
|
1071
|
+
stripped_value = {
|
|
1072
|
+
id: value.id
|
|
1073
|
+
};
|
|
1074
|
+
} else {
|
|
1075
|
+
stripped_value = value;
|
|
1076
|
+
}
|
|
1077
|
+
result[key] = remove_circular_dependencies(stripped_value, visited, [
|
|
1078
|
+
...path,
|
|
1079
|
+
key
|
|
1080
|
+
]);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return result;
|
|
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
|
+
}
|
|
1201
|
+
function add_new_rectangle(ui2, options) {
|
|
1202
|
+
const { editor } = ui2;
|
|
1203
|
+
const { graph } = editor;
|
|
1204
|
+
const x = options.x || 100;
|
|
1205
|
+
const y = options.y || 100;
|
|
1206
|
+
const width = options.width || 120;
|
|
1207
|
+
const height = options.height || 60;
|
|
1208
|
+
const text = options.text || "";
|
|
1209
|
+
const style = options.style || "whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;";
|
|
1210
|
+
const parent = resolve_parent(graph, options.parent_id);
|
|
1211
|
+
graph.getModel().beginUpdate();
|
|
1212
|
+
try {
|
|
1213
|
+
const vertex = graph.insertVertex(
|
|
1214
|
+
parent,
|
|
1215
|
+
null,
|
|
1216
|
+
text,
|
|
1217
|
+
x,
|
|
1218
|
+
y,
|
|
1219
|
+
width,
|
|
1220
|
+
height,
|
|
1221
|
+
style
|
|
1222
|
+
);
|
|
1223
|
+
return vertex;
|
|
1224
|
+
} finally {
|
|
1225
|
+
graph.getModel().endUpdate();
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
function delete_cell_by_id(ui2, options) {
|
|
1229
|
+
const { editor } = ui2;
|
|
1230
|
+
const { graph } = editor;
|
|
1231
|
+
const cell_id = options.cell_id;
|
|
1232
|
+
const cell = graph.getModel().getCell(cell_id);
|
|
1233
|
+
if (!cell) {
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
graph.getModel().beginUpdate();
|
|
1237
|
+
try {
|
|
1238
|
+
graph.removeCells([cell]);
|
|
1239
|
+
return true;
|
|
1240
|
+
} finally {
|
|
1241
|
+
graph.getModel().endUpdate();
|
|
1242
|
+
}
|
|
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;";
|
|
1245
|
+
function add_edge(ui2, options) {
|
|
1246
|
+
const { editor } = ui2;
|
|
1247
|
+
const { graph } = editor;
|
|
1248
|
+
const model = graph.getModel();
|
|
1249
|
+
const source = model.getCell(options.source_id);
|
|
1250
|
+
const target = model.getCell(options.target_id);
|
|
1251
|
+
if (!source || !target) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
1254
|
+
const isSelfConnector = options.source_id === options.target_id;
|
|
1255
|
+
const defaultStyle = isSelfConnector ? SELF_CONNECTOR_DEFAULT_STYLE : "endArrow=classic;html=1;rounded=0;";
|
|
1256
|
+
const style = options.style || defaultStyle;
|
|
1257
|
+
const text = options.text || "";
|
|
1258
|
+
const parent = resolve_parent(graph, options.parent_id);
|
|
1259
|
+
model.beginUpdate();
|
|
1260
|
+
try {
|
|
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
|
+
}
|
|
1268
|
+
return edge;
|
|
1269
|
+
} finally {
|
|
1270
|
+
model.endUpdate();
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function edit_cell(ui2, options) {
|
|
1274
|
+
const { editor } = ui2;
|
|
1275
|
+
const { graph } = editor;
|
|
1276
|
+
const model = graph.getModel();
|
|
1277
|
+
const cell_id = options.cell_id;
|
|
1278
|
+
if (!cell_id) {
|
|
1279
|
+
throw new Error("edit_cell requires a cell_id");
|
|
1280
|
+
}
|
|
1281
|
+
const cell = model.getCell(cell_id);
|
|
1282
|
+
if (!cell) {
|
|
1283
|
+
throw new Error(`edit_cell could not find cell with id '${cell_id}'`);
|
|
1284
|
+
}
|
|
1285
|
+
model.beginUpdate();
|
|
1286
|
+
try {
|
|
1287
|
+
const has_position_change = options.x !== void 0 || options.y !== void 0;
|
|
1288
|
+
const has_size_change = options.width !== void 0 || options.height !== void 0;
|
|
1289
|
+
if (has_position_change || has_size_change) {
|
|
1290
|
+
const geometry = cell.geometry ? cell.geometry.clone() : null;
|
|
1291
|
+
if (!geometry) {
|
|
1292
|
+
throw new Error(
|
|
1293
|
+
`Cell '${cell_id}' does not support geometry updates (missing geometry)`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
if (options.x !== void 0) geometry.x = options.x;
|
|
1297
|
+
if (options.y !== void 0) geometry.y = options.y;
|
|
1298
|
+
if (options.width !== void 0) geometry.width = options.width;
|
|
1299
|
+
if (options.height !== void 0)
|
|
1300
|
+
geometry.height = options.height;
|
|
1301
|
+
model.setGeometry(cell, geometry);
|
|
1302
|
+
}
|
|
1303
|
+
if (options.text !== void 0) {
|
|
1304
|
+
graph.cellLabelChanged(cell, options.text, false);
|
|
1305
|
+
}
|
|
1306
|
+
if (options.style !== void 0) {
|
|
1307
|
+
graph.setCellStyle(options.style, [cell]);
|
|
1308
|
+
}
|
|
1309
|
+
} finally {
|
|
1310
|
+
model.endUpdate();
|
|
1311
|
+
}
|
|
1312
|
+
return cell;
|
|
1313
|
+
}
|
|
1314
|
+
function edit_edge(ui2, options) {
|
|
1315
|
+
const { editor } = ui2;
|
|
1316
|
+
const { graph } = editor;
|
|
1317
|
+
const model = graph.getModel();
|
|
1318
|
+
const cell_id = options.cell_id;
|
|
1319
|
+
if (!cell_id) {
|
|
1320
|
+
throw new Error("edit_edge requires a cell_id");
|
|
1321
|
+
}
|
|
1322
|
+
const edge = model.getCell(cell_id);
|
|
1323
|
+
if (!edge) {
|
|
1324
|
+
throw new Error(`edit_edge could not find edge with id '${cell_id}'`);
|
|
1325
|
+
}
|
|
1326
|
+
if (!edge.edge) {
|
|
1327
|
+
throw new Error(`Cell '${cell_id}' is not an edge`);
|
|
1328
|
+
}
|
|
1329
|
+
model.beginUpdate();
|
|
1330
|
+
try {
|
|
1331
|
+
if (options.source_id !== void 0) {
|
|
1332
|
+
const newSource = model.getCell(options.source_id);
|
|
1333
|
+
if (!newSource) {
|
|
1334
|
+
throw new Error(
|
|
1335
|
+
`edit_edge could not find source cell '${options.source_id}'`
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
model.setTerminal(edge, newSource, true);
|
|
1339
|
+
}
|
|
1340
|
+
if (options.target_id !== void 0) {
|
|
1341
|
+
const newTarget = model.getCell(options.target_id);
|
|
1342
|
+
if (!newTarget) {
|
|
1343
|
+
throw new Error(
|
|
1344
|
+
`edit_edge could not find target cell '${options.target_id}'`
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
model.setTerminal(edge, newTarget, false);
|
|
1348
|
+
}
|
|
1349
|
+
if (options.text !== void 0) {
|
|
1350
|
+
graph.cellLabelChanged(edge, options.text, false);
|
|
1351
|
+
}
|
|
1352
|
+
if (options.style !== void 0) {
|
|
1353
|
+
graph.setCellStyle(options.style, [edge]);
|
|
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
|
+
}
|
|
1361
|
+
} finally {
|
|
1362
|
+
model.endUpdate();
|
|
1363
|
+
}
|
|
1364
|
+
return edge;
|
|
1365
|
+
}
|
|
1366
|
+
function set_cell_shape(ui2, options) {
|
|
1367
|
+
const { editor } = ui2;
|
|
1368
|
+
const { graph } = editor;
|
|
1369
|
+
const model = graph.getModel();
|
|
1370
|
+
const cell_id = options.cell_id;
|
|
1371
|
+
const shape_name = options.shape_name;
|
|
1372
|
+
if (!cell_id) {
|
|
1373
|
+
throw new Error("set_cell_shape requires a cell_id");
|
|
1374
|
+
}
|
|
1375
|
+
if (!shape_name) {
|
|
1376
|
+
throw new Error("set_cell_shape requires a shape_name");
|
|
1377
|
+
}
|
|
1378
|
+
const cell = model.getCell(cell_id);
|
|
1379
|
+
if (!cell) {
|
|
1380
|
+
throw new Error(`set_cell_shape could not find cell with id '${cell_id}'`);
|
|
1381
|
+
}
|
|
1382
|
+
if (cell.edge) {
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`Cell '${cell_id}' is an edge; set_cell_shape expects a vertex`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
const style = shapeLibrary?.[shape_name]?.style;
|
|
1388
|
+
if (!style) {
|
|
1389
|
+
throw new Error(
|
|
1390
|
+
`set_cell_shape could not find a shape named '${shape_name}'`
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
model.beginUpdate();
|
|
1394
|
+
try {
|
|
1395
|
+
apply_style_to_cell(graph, cell, style);
|
|
1396
|
+
} finally {
|
|
1397
|
+
model.endUpdate();
|
|
1398
|
+
}
|
|
1399
|
+
return cell;
|
|
1400
|
+
}
|
|
1401
|
+
function set_cell_data(ui2, options) {
|
|
1402
|
+
const { editor } = ui2;
|
|
1403
|
+
const { graph } = editor;
|
|
1404
|
+
const model = graph.getModel();
|
|
1405
|
+
const mxUtils = window.mxUtils;
|
|
1406
|
+
const cell_id = options.cell_id;
|
|
1407
|
+
const key = options.key;
|
|
1408
|
+
const value = options.value;
|
|
1409
|
+
if (!cell_id) {
|
|
1410
|
+
throw new Error("set_cell_data requires a cell_id");
|
|
1411
|
+
}
|
|
1412
|
+
if (!key) {
|
|
1413
|
+
throw new Error("set_cell_data requires a key");
|
|
1414
|
+
}
|
|
1415
|
+
if (value === void 0) {
|
|
1416
|
+
throw new Error("set_cell_data requires a value");
|
|
1417
|
+
}
|
|
1418
|
+
const cell = model.getCell(cell_id);
|
|
1419
|
+
if (!cell) {
|
|
1420
|
+
throw new Error(`set_cell_data could not find cell with id '${cell_id}'`);
|
|
1421
|
+
}
|
|
1422
|
+
model.beginUpdate();
|
|
1423
|
+
try {
|
|
1424
|
+
let d = graph.getModel().getValue(cell);
|
|
1425
|
+
if (!mxUtils.isNode(d)) {
|
|
1426
|
+
var h = mxUtils.createXmlDocument().createElement("object");
|
|
1427
|
+
h.setAttribute("label", d || "");
|
|
1428
|
+
d = h;
|
|
1429
|
+
}
|
|
1430
|
+
d = d.cloneNode(true);
|
|
1431
|
+
d.setAttribute(key, value);
|
|
1432
|
+
graph.getModel().setValue(cell, d);
|
|
1433
|
+
} catch (e) {
|
|
1434
|
+
console.error(`[set-cell-data] error`, e);
|
|
1435
|
+
} finally {
|
|
1436
|
+
model.endUpdate();
|
|
1437
|
+
}
|
|
1438
|
+
return cell;
|
|
1439
|
+
}
|
|
1440
|
+
function get_shape_categories(ui2) {
|
|
1441
|
+
const shapes = shapeLibrary;
|
|
1442
|
+
const categories = /* @__PURE__ */ new Set();
|
|
1443
|
+
for (const shape of Object.values(shapes)) {
|
|
1444
|
+
categories.add(shape.category || "General");
|
|
1445
|
+
}
|
|
1446
|
+
return [...categories];
|
|
1447
|
+
}
|
|
1448
|
+
function get_shapes_in_category(ui2, options) {
|
|
1449
|
+
const shapes = shapeLibrary;
|
|
1450
|
+
return Object.entries(shapes).filter(([_, shape]) => {
|
|
1451
|
+
return shape.category === options.category_id;
|
|
1452
|
+
}).map(([shape_key, shape_value]) => {
|
|
1453
|
+
return {
|
|
1454
|
+
id: shape_key,
|
|
1455
|
+
title: shape_value.title || shape_key
|
|
1456
|
+
};
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
function get_shape_by_name(ui2, options) {
|
|
1460
|
+
const shapes = shapeLibrary;
|
|
1461
|
+
const shape = shapes[options.shape_name];
|
|
1462
|
+
if (!shape) return null;
|
|
1463
|
+
return {
|
|
1464
|
+
id: options.shape_name,
|
|
1465
|
+
...shape
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function add_cell_of_shape(ui2, options) {
|
|
1469
|
+
const { editor } = ui2;
|
|
1470
|
+
const { graph } = editor;
|
|
1471
|
+
const shape_name = options.shape_name;
|
|
1472
|
+
if (!shape_name) {
|
|
1473
|
+
throw new Error("add_cell_of_shape requires a shape_name");
|
|
1474
|
+
}
|
|
1475
|
+
const x = options.x || 100;
|
|
1476
|
+
const y = options.y || 100;
|
|
1477
|
+
const width = options.width || 120;
|
|
1478
|
+
const height = options.height || 80;
|
|
1479
|
+
const text = options.text || "";
|
|
1480
|
+
const style = options.style || "";
|
|
1481
|
+
const parent = resolve_parent(graph, options.parent_id);
|
|
1482
|
+
const shape_entry = get_shape_by_name(ui2, { shape_name });
|
|
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
|
+
);
|
|
1488
|
+
graph.getModel().beginUpdate();
|
|
1489
|
+
try {
|
|
1490
|
+
const cell = create_shape_vertex(graph, {
|
|
1491
|
+
parent,
|
|
1492
|
+
text,
|
|
1493
|
+
x,
|
|
1494
|
+
y,
|
|
1495
|
+
width,
|
|
1496
|
+
height,
|
|
1497
|
+
style: mergedStyle
|
|
1498
|
+
});
|
|
1499
|
+
return cell;
|
|
1500
|
+
} finally {
|
|
1501
|
+
graph.getModel().endUpdate();
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
function list_paged_model(ui2, options = {}) {
|
|
1505
|
+
const { editor } = ui2;
|
|
1506
|
+
const { graph } = editor;
|
|
1507
|
+
const model = graph.getModel();
|
|
1508
|
+
const cells = model.cells;
|
|
1509
|
+
if (!cells) {
|
|
1510
|
+
return [];
|
|
1511
|
+
}
|
|
1512
|
+
function parse_style_attributes(style) {
|
|
1513
|
+
const attributes = {};
|
|
1514
|
+
if (!style) return attributes;
|
|
1515
|
+
const pairs = style.split(";");
|
|
1516
|
+
for (const pair of pairs) {
|
|
1517
|
+
const [key, ...valueParts] = pair.split("=");
|
|
1518
|
+
if (key && valueParts.length > 0) {
|
|
1519
|
+
attributes[key.trim()] = valueParts.join("=").trim();
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return attributes;
|
|
1523
|
+
}
|
|
1524
|
+
function extract_cell_attributes(cell) {
|
|
1525
|
+
const attributes = {};
|
|
1526
|
+
attributes.id = cell.id || "";
|
|
1527
|
+
attributes.edge = cell.edge || false;
|
|
1528
|
+
if (cell.style) {
|
|
1529
|
+
Object.assign(attributes, parse_style_attributes(cell.style));
|
|
1530
|
+
}
|
|
1531
|
+
if (cell.value && typeof cell.value === "object" && cell.value.attributes) {
|
|
1532
|
+
const transformed_attributes = transform_cells_NamedNodeMap_to_attributes(cell);
|
|
1533
|
+
Object.assign(attributes, transformed_attributes);
|
|
1534
|
+
}
|
|
1535
|
+
if (cell.value && typeof cell.value === "string") {
|
|
1536
|
+
attributes.text = cell.value;
|
|
1537
|
+
}
|
|
1538
|
+
return attributes;
|
|
1539
|
+
}
|
|
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) {
|
|
1563
|
+
switch (cell_type) {
|
|
1564
|
+
case "edge":
|
|
1565
|
+
return cell.edge === true || cell.edge === 1;
|
|
1566
|
+
case "vertex":
|
|
1567
|
+
return cell.edge === false;
|
|
1568
|
+
case "object":
|
|
1569
|
+
return cell.value?.nodeName === "object";
|
|
1570
|
+
case "group":
|
|
1571
|
+
return cell.style === "group";
|
|
1572
|
+
case "layer":
|
|
1573
|
+
return isLayer(cell);
|
|
1574
|
+
default:
|
|
1575
|
+
return true;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
let filtered_cells = Object.values(cells);
|
|
1579
|
+
if (options.filter) {
|
|
1580
|
+
const filter = options.filter;
|
|
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
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
const page = Math.max(0, options.page || 0);
|
|
1614
|
+
const page_size = Math.max(1, options.page_size || 50);
|
|
1615
|
+
const start_index = page * page_size;
|
|
1616
|
+
const cell_ids = filtered_cells.map((cell) => cell.id);
|
|
1617
|
+
const paginated_ids = cell_ids.slice(start_index, start_index + page_size);
|
|
1618
|
+
const transformed_cells = [];
|
|
1619
|
+
for (const cell_id of paginated_ids) {
|
|
1620
|
+
const cell = cells[cell_id];
|
|
1621
|
+
if (cell) {
|
|
1622
|
+
const sanitized_cell = remove_circular_dependencies(cell);
|
|
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
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
return transformed_cells;
|
|
1638
|
+
}
|
|
1639
|
+
function list_layers(ui2) {
|
|
1640
|
+
const { editor } = ui2;
|
|
1641
|
+
const { graph } = editor;
|
|
1642
|
+
const model = graph.getModel();
|
|
1643
|
+
const root = model.getRoot();
|
|
1644
|
+
const layers = [];
|
|
1645
|
+
for (let i = 0; i < model.getChildCount(root); i++) {
|
|
1646
|
+
const layer = model.getChildAt(root, i);
|
|
1647
|
+
if (layer) {
|
|
1648
|
+
layers.push({
|
|
1649
|
+
id: layer.getId(),
|
|
1650
|
+
name: layer.getValue() || `Layer ${i}`,
|
|
1651
|
+
visible: layer.isVisible(),
|
|
1652
|
+
locked: !layer.isConnectable()
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return layers;
|
|
1657
|
+
}
|
|
1658
|
+
function set_active_layer(ui2, options) {
|
|
1659
|
+
const { editor } = ui2;
|
|
1660
|
+
const { graph } = editor;
|
|
1661
|
+
const model = graph.getModel();
|
|
1662
|
+
const layer = model.getCell(options.layer_id);
|
|
1663
|
+
if (!layer) {
|
|
1664
|
+
throw new Error(`Layer with ID ${options.layer_id} not found`);
|
|
1665
|
+
}
|
|
1666
|
+
graph.setDefaultParent(layer);
|
|
1667
|
+
return {
|
|
1668
|
+
id: layer.getId(),
|
|
1669
|
+
name: layer.getValue() || "Unnamed Layer"
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
function move_cell_to_layer(ui2, options) {
|
|
1673
|
+
const { editor } = ui2;
|
|
1674
|
+
const { graph } = editor;
|
|
1675
|
+
const model = graph.getModel();
|
|
1676
|
+
const cell = model.getCell(options.cell_id);
|
|
1677
|
+
const targetLayer = model.getCell(options.target_layer_id);
|
|
1678
|
+
if (!cell) {
|
|
1679
|
+
throw new Error(`Cell with ID ${options.cell_id} not found`);
|
|
1680
|
+
}
|
|
1681
|
+
if (!targetLayer) {
|
|
1682
|
+
throw new Error(
|
|
1683
|
+
`Target layer with ID ${options.target_layer_id} not found`
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
model.beginUpdate();
|
|
1687
|
+
try {
|
|
1688
|
+
model.add(targetLayer, cell);
|
|
1689
|
+
} finally {
|
|
1690
|
+
model.endUpdate();
|
|
1691
|
+
}
|
|
1692
|
+
return {
|
|
1693
|
+
moved_cell: options.cell_id,
|
|
1694
|
+
to_layer: options.target_layer_id
|
|
1695
|
+
};
|
|
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
|
+
}
|
|
1720
|
+
function get_active_layer(ui2) {
|
|
1721
|
+
const { editor } = ui2;
|
|
1722
|
+
const { graph } = editor;
|
|
1723
|
+
const activeLayer = graph.getDefaultParent();
|
|
1724
|
+
return {
|
|
1725
|
+
id: activeLayer.getId(),
|
|
1726
|
+
name: activeLayer.getValue() || "Default Layer"
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
function create_layer(ui2, options) {
|
|
1730
|
+
const { editor } = ui2;
|
|
1731
|
+
const { graph } = editor;
|
|
1732
|
+
const model = graph.getModel();
|
|
1733
|
+
const root = model.getRoot();
|
|
1734
|
+
model.beginUpdate();
|
|
1735
|
+
let newLayer;
|
|
1736
|
+
try {
|
|
1737
|
+
newLayer = new window.mxCell(options.name);
|
|
1738
|
+
newLayer.setId(null);
|
|
1739
|
+
model.add(root, newLayer);
|
|
1740
|
+
} finally {
|
|
1741
|
+
model.endUpdate();
|
|
1742
|
+
}
|
|
1743
|
+
return {
|
|
1744
|
+
id: newLayer.getId(),
|
|
1745
|
+
name: options.name
|
|
1746
|
+
};
|
|
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
|
+
}
|
|
2281
|
+
|
|
2282
|
+
// src/plugin.ts
|
|
2283
|
+
function reply_name(event_name, request_id) {
|
|
2284
|
+
return `${event_name}.${request_id}`;
|
|
2285
|
+
}
|
|
2286
|
+
var createToolHandler = (toolName, parameterKeys, executeFunction) => {
|
|
2287
|
+
return (request) => {
|
|
2288
|
+
const optionEntries = Object.entries(request).filter(([key, _value]) => {
|
|
2289
|
+
return parameterKeys.has(key);
|
|
2290
|
+
});
|
|
2291
|
+
const options = optionEntries.reduce(
|
|
2292
|
+
(acc, [key, value]) => {
|
|
2293
|
+
acc[key] = value;
|
|
2294
|
+
return acc;
|
|
2295
|
+
},
|
|
2296
|
+
{}
|
|
2297
|
+
);
|
|
2298
|
+
const sendReply = (result, success, error) => {
|
|
2299
|
+
const reply = {
|
|
2300
|
+
__event: reply_name(toolName, request.__request_id),
|
|
2301
|
+
__request_id: request.__request_id,
|
|
2302
|
+
success,
|
|
2303
|
+
result: success ? remove_circular_dependencies(result) : void 0,
|
|
2304
|
+
error: !success ? remove_circular_dependencies(error) : void 0
|
|
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);
|
|
2327
|
+
} catch (error) {
|
|
2328
|
+
console.error(
|
|
2329
|
+
`[plugin] Tool ${toolName} failed for request ID ${request.__request_id}:`,
|
|
2330
|
+
error
|
|
2331
|
+
);
|
|
2332
|
+
return sendReply(void 0, false, error);
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
};
|
|
2336
|
+
var ui;
|
|
2337
|
+
var wsManager = null;
|
|
2338
|
+
var settingsDialog = null;
|
|
2339
|
+
var toolDefinitions = [
|
|
2340
|
+
{
|
|
2341
|
+
name: "get-selected-cell",
|
|
2342
|
+
params: /* @__PURE__ */ new Set([]),
|
|
2343
|
+
handler: (ui2, _options) => {
|
|
2344
|
+
return ui2.editor.graph.getSelectionCell() || "no cell selected";
|
|
2345
|
+
}
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
name: "add-rectangle",
|
|
2349
|
+
params: /* @__PURE__ */ new Set(["x", "y", "width", "height", "text", "style", "parent_id"]),
|
|
2350
|
+
handler: add_new_rectangle
|
|
2351
|
+
},
|
|
2352
|
+
{
|
|
2353
|
+
name: "delete-cell-by-id",
|
|
2354
|
+
params: /* @__PURE__ */ new Set(["cell_id"]),
|
|
2355
|
+
handler: delete_cell_by_id
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
name: "add-edge",
|
|
2359
|
+
params: /* @__PURE__ */ new Set(["source_id", "target_id", "style", "text", "parent_id", "points"]),
|
|
2360
|
+
handler: add_edge
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
name: "get-shape-categories",
|
|
2364
|
+
params: /* @__PURE__ */ new Set([]),
|
|
2365
|
+
handler: get_shape_categories
|
|
2366
|
+
},
|
|
2367
|
+
{
|
|
2368
|
+
name: "get-shapes-in-category",
|
|
2369
|
+
params: /* @__PURE__ */ new Set(["category_id"]),
|
|
2370
|
+
handler: get_shapes_in_category
|
|
2371
|
+
},
|
|
2372
|
+
{
|
|
2373
|
+
name: "get-shape-by-name",
|
|
2374
|
+
params: /* @__PURE__ */ new Set(["shape_name"]),
|
|
2375
|
+
handler: get_shape_by_name
|
|
2376
|
+
},
|
|
2377
|
+
{
|
|
2378
|
+
name: "add-cell-of-shape",
|
|
2379
|
+
params: /* @__PURE__ */ new Set([
|
|
2380
|
+
"shape_name",
|
|
2381
|
+
"x",
|
|
2382
|
+
"y",
|
|
2383
|
+
"width",
|
|
2384
|
+
"height",
|
|
2385
|
+
"text",
|
|
2386
|
+
"style",
|
|
2387
|
+
"parent_id"
|
|
2388
|
+
]),
|
|
2389
|
+
handler: add_cell_of_shape
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
name: "set-cell-shape",
|
|
2393
|
+
params: /* @__PURE__ */ new Set(["cell_id", "shape_name"]),
|
|
2394
|
+
handler: set_cell_shape
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
name: "set-cell-data",
|
|
2398
|
+
params: /* @__PURE__ */ new Set(["cell_id", "key", "value"]),
|
|
2399
|
+
handler: (ui2, options) => {
|
|
2400
|
+
return set_cell_data(ui2, options);
|
|
2401
|
+
}
|
|
2402
|
+
},
|
|
2403
|
+
{
|
|
2404
|
+
name: "list-paged-model",
|
|
2405
|
+
params: /* @__PURE__ */ new Set(["page", "page_size", "filter"]),
|
|
2406
|
+
handler: list_paged_model
|
|
2407
|
+
},
|
|
2408
|
+
{
|
|
2409
|
+
name: "edit-cell",
|
|
2410
|
+
params: /* @__PURE__ */ new Set(["cell_id", "text", "x", "y", "width", "height", "style"]),
|
|
2411
|
+
handler: edit_cell
|
|
2412
|
+
},
|
|
2413
|
+
{
|
|
2414
|
+
name: "edit-edge",
|
|
2415
|
+
params: /* @__PURE__ */ new Set(["cell_id", "text", "source_id", "target_id", "style", "points"]),
|
|
2416
|
+
handler: edit_edge
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
name: "list-layers",
|
|
2420
|
+
params: /* @__PURE__ */ new Set([]),
|
|
2421
|
+
handler: list_layers
|
|
2422
|
+
},
|
|
2423
|
+
{
|
|
2424
|
+
name: "set-active-layer",
|
|
2425
|
+
params: /* @__PURE__ */ new Set(["layer_id"]),
|
|
2426
|
+
handler: set_active_layer
|
|
2427
|
+
},
|
|
2428
|
+
{
|
|
2429
|
+
name: "move-cell-to-layer",
|
|
2430
|
+
params: /* @__PURE__ */ new Set(["cell_id", "target_layer_id"]),
|
|
2431
|
+
handler: move_cell_to_layer
|
|
2432
|
+
},
|
|
2433
|
+
{
|
|
2434
|
+
name: "set-cell-parent",
|
|
2435
|
+
params: /* @__PURE__ */ new Set(["cell_id", "parent_id"]),
|
|
2436
|
+
handler: set_cell_parent
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
name: "get-active-layer",
|
|
2440
|
+
params: /* @__PURE__ */ new Set([]),
|
|
2441
|
+
handler: get_active_layer
|
|
2442
|
+
},
|
|
2443
|
+
{
|
|
2444
|
+
name: "create-layer",
|
|
2445
|
+
params: /* @__PURE__ */ new Set(["name"]),
|
|
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
|
|
2469
|
+
}
|
|
2470
|
+
];
|
|
2471
|
+
var toolHandlers = /* @__PURE__ */ new Map();
|
|
2472
|
+
var handleWebSocketMessage = (message) => {
|
|
2473
|
+
console.debug("[plugin] Received WebSocket message:", message);
|
|
2474
|
+
if (message.__event && toolHandlers.has(message.__event)) {
|
|
2475
|
+
const handler = toolHandlers.get(message.__event);
|
|
2476
|
+
if (handler) {
|
|
2477
|
+
handler(message);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
var initializeWebSocket = async () => {
|
|
2482
|
+
const config = await fetchPluginConfig();
|
|
2483
|
+
const wsUrl = buildWebSocketUrl(config);
|
|
2484
|
+
wsManager = createWebSocketManager({
|
|
2485
|
+
url: wsUrl,
|
|
2486
|
+
maxReconnectAttempts: 5,
|
|
2487
|
+
reconnectDelay: 3e3,
|
|
2488
|
+
pingInterval: 3e4
|
|
2489
|
+
});
|
|
2490
|
+
wsManager.onMessage(handleWebSocketMessage);
|
|
2491
|
+
wsManager.connect();
|
|
2492
|
+
console.log(`[plugin] WebSocket initialized with URL: ${wsUrl}`);
|
|
2493
|
+
};
|
|
2494
|
+
var initializeSettingsDialog = () => {
|
|
2495
|
+
const config = readPluginConfig();
|
|
2496
|
+
const connectionState = wsManager ? wsManager.getState() : "disconnected";
|
|
2497
|
+
const initialState = {
|
|
2498
|
+
config: { ...config },
|
|
2499
|
+
connectionState,
|
|
2500
|
+
isSaving: false,
|
|
2501
|
+
formData: {
|
|
2502
|
+
port: config.websocketPort.toString()
|
|
2503
|
+
},
|
|
2504
|
+
errors: {}
|
|
2505
|
+
};
|
|
2506
|
+
const actions = {
|
|
2507
|
+
onSave: (newConfig) => {
|
|
2508
|
+
writePluginConfig(newConfig);
|
|
2509
|
+
if (wsManager) {
|
|
2510
|
+
wsManager.disconnect();
|
|
2511
|
+
}
|
|
2512
|
+
initializeWebSocket();
|
|
2513
|
+
if (settingsDialog) {
|
|
2514
|
+
settingsDialog.update({
|
|
2515
|
+
...initialState,
|
|
2516
|
+
config: { ...newConfig },
|
|
2517
|
+
connectionState: wsManager ? wsManager.getState() : "disconnected",
|
|
2518
|
+
isSaving: false
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
console.log("[plugin] Configuration saved and WebSocket reinitialized");
|
|
2522
|
+
},
|
|
2523
|
+
onClose: () => {
|
|
2524
|
+
if (settingsDialog) {
|
|
2525
|
+
hideSettingsDialog(settingsDialog);
|
|
2526
|
+
}
|
|
2527
|
+
},
|
|
2528
|
+
onPing: async () => {
|
|
2529
|
+
if (wsManager) {
|
|
2530
|
+
return await wsManager.ping();
|
|
2531
|
+
}
|
|
2532
|
+
return false;
|
|
2533
|
+
},
|
|
2534
|
+
onReconnect: () => {
|
|
2535
|
+
if (wsManager) {
|
|
2536
|
+
wsManager.disconnect();
|
|
2537
|
+
wsManager.connect();
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
settingsDialog = createSettingsDialog(initialState, actions);
|
|
2542
|
+
};
|
|
2543
|
+
var showSettings = () => {
|
|
2544
|
+
if (!settingsDialog) {
|
|
2545
|
+
initializeSettingsDialog();
|
|
2546
|
+
}
|
|
2547
|
+
if (settingsDialog) {
|
|
2548
|
+
const config = readPluginConfig();
|
|
2549
|
+
const connectionState = wsManager ? wsManager.getState() : "disconnected";
|
|
2550
|
+
settingsDialog.update({
|
|
2551
|
+
config: { ...config },
|
|
2552
|
+
connectionState,
|
|
2553
|
+
isSaving: false,
|
|
2554
|
+
formData: {
|
|
2555
|
+
port: config.websocketPort.toString()
|
|
2556
|
+
},
|
|
2557
|
+
errors: {}
|
|
2558
|
+
});
|
|
2559
|
+
showSettingsDialog(settingsDialog);
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
var addMenuItem = (ui2) => {
|
|
2563
|
+
if (!ui2.menus) {
|
|
2564
|
+
console.warn(
|
|
2565
|
+
"[plugin] Menu bar not available, cannot add MCP Settings menu item"
|
|
2566
|
+
);
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
const menubar = ui2.menus;
|
|
2570
|
+
try {
|
|
2571
|
+
let targetMenu = menubar.get("extras") || menubar.get("file") || menubar.get("edit");
|
|
2572
|
+
if (!targetMenu) {
|
|
2573
|
+
console.warn("[plugin] Could not find suitable menu to add MCP Settings");
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
ui2.actions.addAction("Draw.io MCP", function() {
|
|
2577
|
+
showSettings();
|
|
2578
|
+
});
|
|
2579
|
+
var oldFunct = targetMenu.funct;
|
|
2580
|
+
targetMenu.funct = function(targetMenu2, parent) {
|
|
2581
|
+
oldFunct.apply(this, arguments);
|
|
2582
|
+
ui2.menus.addMenuItems(targetMenu2, ["Draw.io MCP"], parent);
|
|
2583
|
+
};
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
console.error("[plugin] Failed to add menu item:", error);
|
|
2586
|
+
}
|
|
2587
|
+
};
|
|
2588
|
+
function initPlugin() {
|
|
2589
|
+
console.debug("[plugin] Loading Draw.io MCP Plugin...");
|
|
2590
|
+
const checkInterval = setInterval(() => {
|
|
2591
|
+
if (window.Draw) {
|
|
2592
|
+
clearInterval(checkInterval);
|
|
2593
|
+
window.Draw.loadPlugin((drawioUI) => {
|
|
2594
|
+
console.debug("[plugin] Plugin loaded successfully");
|
|
2595
|
+
ui = drawioUI;
|
|
2596
|
+
if (window.__DRAWIO_MCP_TEST_HOOKS__ === true) {
|
|
2597
|
+
window.ui = drawioUI;
|
|
2598
|
+
}
|
|
2599
|
+
toolDefinitions.forEach((def) => {
|
|
2600
|
+
const handler = createToolHandler(def.name, def.params, def.handler);
|
|
2601
|
+
toolHandlers.set(def.name, handler);
|
|
2602
|
+
});
|
|
2603
|
+
initializeWebSocket();
|
|
2604
|
+
initializeSettingsDialog();
|
|
2605
|
+
addMenuItem(ui);
|
|
2606
|
+
console.info("[plugin] MCP Plugin fully initialized");
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
}, 1e3);
|
|
2610
|
+
}
|
|
2611
|
+
if (typeof window !== "undefined") {
|
|
2612
|
+
if (document.readyState === "loading") {
|
|
2613
|
+
document.addEventListener("DOMContentLoaded", initPlugin);
|
|
2614
|
+
} else {
|
|
2615
|
+
initPlugin();
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
})();
|