dodraw-mcp-server 0.1.3 → 0.1.4
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/dist/src/create_class_diagram_example.js +136 -0
- package/dist/src/create_termination_reference.js +88 -0
- package/dist/src/tools/diagramTools.js +241 -10
- package/package.json +1 -1
- package/src/create_class_diagram_example.ts +151 -0
- package/src/create_termination_reference.ts +108 -0
- package/src/tools/diagramTools.ts +251 -10
- package/src/types.ts +29 -13
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const diagramTools_1 = require("./tools/diagramTools");
|
|
4
|
+
async function createClassDiagram() {
|
|
5
|
+
const FILE_PATH = "class_diagram_example.3duml";
|
|
6
|
+
console.log(`Creating class diagram example at ${FILE_PATH}...`);
|
|
7
|
+
// 1. Create Diagram
|
|
8
|
+
try {
|
|
9
|
+
await (0, diagramTools_1.handleToolCall)("create_new_diagram", { filePath: FILE_PATH });
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
console.log("File might exist, continuing...");
|
|
13
|
+
}
|
|
14
|
+
// 2. Add Layer
|
|
15
|
+
await (0, diagramTools_1.handleToolCall)("add_layer", { filePath: FILE_PATH, name: "Classes" });
|
|
16
|
+
// 3. Create Classes
|
|
17
|
+
// Abstract Class: GraphicObject
|
|
18
|
+
await (0, diagramTools_1.handleToolCall)("create_uml_class", {
|
|
19
|
+
filePath: FILE_PATH,
|
|
20
|
+
className: "GraphicObject",
|
|
21
|
+
attributes: ["x: int", "y: int"],
|
|
22
|
+
methods: ["move(dx: int, dy: int)", "draw()"],
|
|
23
|
+
x: 0, y: -4,
|
|
24
|
+
id: "GraphicObject"
|
|
25
|
+
});
|
|
26
|
+
// Subclass: Circle
|
|
27
|
+
await (0, diagramTools_1.handleToolCall)("create_uml_class", {
|
|
28
|
+
filePath: FILE_PATH,
|
|
29
|
+
className: "Circle",
|
|
30
|
+
attributes: ["radius: float"],
|
|
31
|
+
methods: ["draw()", "resize(scale: float)"],
|
|
32
|
+
x: -4, y: 0,
|
|
33
|
+
id: "Circle"
|
|
34
|
+
});
|
|
35
|
+
// Subclass: Rectangle
|
|
36
|
+
await (0, diagramTools_1.handleToolCall)("create_uml_class", {
|
|
37
|
+
filePath: FILE_PATH,
|
|
38
|
+
className: "Rectangle",
|
|
39
|
+
attributes: ["width: float", "height: float"],
|
|
40
|
+
methods: ["draw()", "resize(scale: float)"],
|
|
41
|
+
x: 4, y: 0,
|
|
42
|
+
id: "Rectangle"
|
|
43
|
+
});
|
|
44
|
+
// Container: Group
|
|
45
|
+
await (0, diagramTools_1.handleToolCall)("create_uml_class", {
|
|
46
|
+
filePath: FILE_PATH,
|
|
47
|
+
className: "Group",
|
|
48
|
+
attributes: [],
|
|
49
|
+
methods: ["add(obj: GraphicObject)", "remove(obj: GraphicObject)", "draw()"],
|
|
50
|
+
x: 0, y: 4,
|
|
51
|
+
id: "Group"
|
|
52
|
+
});
|
|
53
|
+
// Client: Canvas
|
|
54
|
+
await (0, diagramTools_1.handleToolCall)("create_uml_class", {
|
|
55
|
+
filePath: FILE_PATH,
|
|
56
|
+
className: "Canvas",
|
|
57
|
+
attributes: ["width: int", "height: int", "backgroundColor: string"],
|
|
58
|
+
methods: ["render()", "clear()"],
|
|
59
|
+
x: 8, y: -4,
|
|
60
|
+
id: "Canvas"
|
|
61
|
+
});
|
|
62
|
+
// 4. Create Edges (Relationships)
|
|
63
|
+
// Inheritance: Circle -> GraphicObject
|
|
64
|
+
await (0, diagramTools_1.handleToolCall)("add_edge", {
|
|
65
|
+
filePath: FILE_PATH,
|
|
66
|
+
sourceId: "Circle",
|
|
67
|
+
targetId: "GraphicObject",
|
|
68
|
+
sourcePointIndex: 0, // Top
|
|
69
|
+
targetPointIndex: 3, // Left (or Bottom of GraphicObject depending on layout) -> GraphicObject is at -4 Z (UP). Circle at 0 Z.
|
|
70
|
+
// Circle (0,0) is BELOW GraphicObject (0, -4).
|
|
71
|
+
// So Circle TOP (0) connects to GraphicObject BOTTOM (2).
|
|
72
|
+
// Wait, Z is vertical in 2D top-down view?
|
|
73
|
+
// Logic: Z decreases going UP.
|
|
74
|
+
// GraphicObject (z=-4) is UP. Circle (z=0) is DOWN.
|
|
75
|
+
// Connect Circle Top (0) to GraphicObject Bottom (2).
|
|
76
|
+
terminationEnd: "triangle-empty", // Inheritance arrow on Parent
|
|
77
|
+
terminationStart: "none",
|
|
78
|
+
routingType: "orthogonal",
|
|
79
|
+
label: "extends",
|
|
80
|
+
id: "edge_circle_extends"
|
|
81
|
+
});
|
|
82
|
+
// Inheritance: Rectangle -> GraphicObject
|
|
83
|
+
await (0, diagramTools_1.handleToolCall)("add_edge", {
|
|
84
|
+
filePath: FILE_PATH,
|
|
85
|
+
sourceId: "Rectangle",
|
|
86
|
+
targetId: "GraphicObject",
|
|
87
|
+
sourcePointIndex: 0, // Top
|
|
88
|
+
targetPointIndex: 2, // Bottom
|
|
89
|
+
terminationEnd: "triangle-empty",
|
|
90
|
+
terminationStart: "none",
|
|
91
|
+
routingType: "orthogonal",
|
|
92
|
+
id: "edge_rect_extends"
|
|
93
|
+
});
|
|
94
|
+
// Aggregation: Group has GraphicObjects
|
|
95
|
+
// Group (z=4) is BELOW GraphicObject (z=-4)?
|
|
96
|
+
// No, Group is at z=4 (Down). GraphicObject is at z=-4 (Up).
|
|
97
|
+
// Connection: Group (Top/0) -> GraphicObject (Bottom/2) ??
|
|
98
|
+
// Actually Group "contains" GraphicObject.
|
|
99
|
+
// The Diamond is on the Group end.
|
|
100
|
+
// Source: Group. Target: GraphicObject.
|
|
101
|
+
// Diamond at Source. Arrow/None at Target.
|
|
102
|
+
await (0, diagramTools_1.handleToolCall)("add_edge", {
|
|
103
|
+
filePath: FILE_PATH,
|
|
104
|
+
sourceId: "Group",
|
|
105
|
+
targetId: "GraphicObject",
|
|
106
|
+
sourcePointIndex: 0, // Top
|
|
107
|
+
targetPointIndex: 2, // Bottom
|
|
108
|
+
terminationStart: "diamond-empty", // Aggregation at Source
|
|
109
|
+
terminationEnd: "arrow", // Association at Target (or none)
|
|
110
|
+
routingType: "orthogonal",
|
|
111
|
+
label: "contains",
|
|
112
|
+
id: "edge_group_aggregation"
|
|
113
|
+
});
|
|
114
|
+
// Composition: Canvas owns GraphicObjects?
|
|
115
|
+
// Or maybe Canvas owns a Root Group?
|
|
116
|
+
// Let's say Canvas has a list of Shapes.
|
|
117
|
+
// Canvas (8, -4) -> GraphicObject (0, -4).
|
|
118
|
+
// Canvas Left (3) -> GraphicObject Right (1).
|
|
119
|
+
await (0, diagramTools_1.handleToolCall)("add_edge", {
|
|
120
|
+
filePath: FILE_PATH,
|
|
121
|
+
sourceId: "Canvas",
|
|
122
|
+
targetId: "GraphicObject",
|
|
123
|
+
sourcePointIndex: 3, // Left
|
|
124
|
+
targetPointIndex: 1, // Right
|
|
125
|
+
terminationStart: "diamond-filled", // Composition (Canvas owns Objects)
|
|
126
|
+
terminationEnd: "none",
|
|
127
|
+
routingType: "straight",
|
|
128
|
+
label: "renders",
|
|
129
|
+
id: "edge_canvas_composition"
|
|
130
|
+
});
|
|
131
|
+
console.log("Class Diagram created successfully: " + FILE_PATH);
|
|
132
|
+
}
|
|
133
|
+
createClassDiagram().catch(err => {
|
|
134
|
+
console.error("Error creating class diagram:", err);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const diagramTools_1 = require("./tools/diagramTools");
|
|
4
|
+
async function createReference() {
|
|
5
|
+
const FILE_PATH = "edge_terminations_test.3duml";
|
|
6
|
+
console.log(`Creating diagram at ${FILE_PATH}...`);
|
|
7
|
+
// 1. Create Diagram
|
|
8
|
+
try {
|
|
9
|
+
await (0, diagramTools_1.handleToolCall)("create_new_diagram", { filePath: FILE_PATH });
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
console.log("File might exist, continuing...");
|
|
13
|
+
}
|
|
14
|
+
// 2. Add Layer
|
|
15
|
+
await (0, diagramTools_1.handleToolCall)("add_layer", { filePath: FILE_PATH, name: "Terminations" });
|
|
16
|
+
// 3. Define Types
|
|
17
|
+
const types = [
|
|
18
|
+
{ id: 'none', label: 'None', description: 'No visual marker' },
|
|
19
|
+
{ id: 'arrow', label: 'Arrow (Association)', description: 'Solid cone/V-shape' },
|
|
20
|
+
{ id: 'triangle-empty', label: 'Inheritance', description: 'Hollow triangle' },
|
|
21
|
+
{ id: 'triangle-empty-dashed', label: 'Realization', description: 'Hollow triangle (dashed edge)', isDashed: true },
|
|
22
|
+
{ id: 'diamond-filled', label: 'Composition', description: 'Filled diamond' },
|
|
23
|
+
{ id: 'diamond-empty', label: 'Aggregation', description: 'Hollow diamond' },
|
|
24
|
+
{ id: 'arrow-open', label: 'Dependency', description: 'Open V-shape (dashed)', isDashed: true },
|
|
25
|
+
{ id: 'crows-one', label: 'Crow One', description: 'Perpendicular bar |' },
|
|
26
|
+
{ id: 'crows-many', label: 'Crow Many', description: 'Trident |<' },
|
|
27
|
+
{ id: 'crows-zero-one', label: 'Crow Zero-One', description: 'Circle and bar O|' },
|
|
28
|
+
{ id: 'crows-zero-many', label: 'Crow Zero-Many', description: 'Circle and trident O|<' },
|
|
29
|
+
{ id: 'circle', label: 'Circle', description: 'Hollow circle' },
|
|
30
|
+
{ id: 'circle-plus', label: 'Socket', description: 'Circle with plus (Socket)' }
|
|
31
|
+
];
|
|
32
|
+
let z = 0;
|
|
33
|
+
const SPACING_Z = 3.5;
|
|
34
|
+
// Use manual IDs to avoid importing randomUUID
|
|
35
|
+
let counter = 0;
|
|
36
|
+
for (const t of types) {
|
|
37
|
+
const sourceId = `src_${t.id}`;
|
|
38
|
+
const targetId = `tgt_${t.id}`;
|
|
39
|
+
// Start Node
|
|
40
|
+
await (0, diagramTools_1.handleToolCall)("add_node", {
|
|
41
|
+
filePath: FILE_PATH,
|
|
42
|
+
label: `Start`,
|
|
43
|
+
id: sourceId,
|
|
44
|
+
x: -4, y: z,
|
|
45
|
+
width: 2, height: 1.5,
|
|
46
|
+
color: "#eeeeee"
|
|
47
|
+
});
|
|
48
|
+
// End Node
|
|
49
|
+
await (0, diagramTools_1.handleToolCall)("add_node", {
|
|
50
|
+
filePath: FILE_PATH,
|
|
51
|
+
label: `End`,
|
|
52
|
+
id: targetId,
|
|
53
|
+
x: 4, y: z,
|
|
54
|
+
width: 2, height: 1.5,
|
|
55
|
+
color: "#eeeeee"
|
|
56
|
+
});
|
|
57
|
+
// Description Note (Left side)
|
|
58
|
+
await (0, diagramTools_1.handleToolCall)("add_node", {
|
|
59
|
+
filePath: FILE_PATH,
|
|
60
|
+
label: `${t.label}\n${t.description}`,
|
|
61
|
+
shape: 'note',
|
|
62
|
+
color: '#ffffcc',
|
|
63
|
+
x: -10, y: z,
|
|
64
|
+
width: 4, height: 2,
|
|
65
|
+
id: `note_${t.id}`
|
|
66
|
+
});
|
|
67
|
+
// Edge
|
|
68
|
+
await (0, diagramTools_1.handleToolCall)("add_edge", {
|
|
69
|
+
filePath: FILE_PATH,
|
|
70
|
+
sourceId: sourceId,
|
|
71
|
+
targetId: targetId,
|
|
72
|
+
sourcePointIndex: 1, // Right
|
|
73
|
+
targetPointIndex: 3, // Left
|
|
74
|
+
label: t.id,
|
|
75
|
+
borderStyle: t.isDashed ? 'dashed' : 'solid',
|
|
76
|
+
terminationEnd: t.id,
|
|
77
|
+
terminationStart: 'none',
|
|
78
|
+
id: `edge_${t.id}`
|
|
79
|
+
});
|
|
80
|
+
z += SPACING_Z;
|
|
81
|
+
counter++;
|
|
82
|
+
}
|
|
83
|
+
console.log("Diagram created successfully: " + FILE_PATH);
|
|
84
|
+
}
|
|
85
|
+
createReference().catch(err => {
|
|
86
|
+
console.error("Error creating reference:", err);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
|
@@ -35,14 +35,32 @@ exports.toolDefinitions = [
|
|
|
35
35
|
properties: {
|
|
36
36
|
filePath: { type: "string" },
|
|
37
37
|
label: { type: "string" },
|
|
38
|
-
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table"] },
|
|
38
|
+
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table", "start-state", "end-state", "fork-join"] },
|
|
39
39
|
x: { type: "number", description: "Optional. defaults to auto-placement" },
|
|
40
40
|
y: { type: "number", description: "Optional. defaults to 0 or same as last node" },
|
|
41
41
|
z: { type: "number" },
|
|
42
42
|
width: { type: "number", description: "Default: 2. Min: 0.1, Max: 50" },
|
|
43
43
|
height: { type: "number", description: "Default: 1.5. Min: 0.1, Max: 50" },
|
|
44
44
|
layerId: { type: "string", description: "Optional layer ID, defaults to active layer" },
|
|
45
|
-
color: { type: "string" }
|
|
45
|
+
color: { type: "string" },
|
|
46
|
+
description: { type: "string", description: "Optional description or metadata for the node" },
|
|
47
|
+
tableData: {
|
|
48
|
+
type: "object",
|
|
49
|
+
description: "Required if shape is 'table'",
|
|
50
|
+
properties: {
|
|
51
|
+
rows: { type: "number" },
|
|
52
|
+
columns: { type: "number" },
|
|
53
|
+
hasRowHeader: { type: "boolean" },
|
|
54
|
+
hasColumnHeader: { type: "boolean" },
|
|
55
|
+
cells: {
|
|
56
|
+
type: "object",
|
|
57
|
+
description: "Map of 'row,col' string keys to cell content strings. E.g. {'0,0': 'Header'}"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
id: { type: "string", description: "Optional explicit ID" },
|
|
62
|
+
attributes: { type: "array", items: { type: "string" }, description: "Optional list of attributes for Class nodes" },
|
|
63
|
+
methods: { type: "array", items: { type: "string" }, description: "Optional list of methods for Class nodes" }
|
|
46
64
|
},
|
|
47
65
|
required: ["filePath", "label"]
|
|
48
66
|
}
|
|
@@ -60,7 +78,13 @@ exports.toolDefinitions = [
|
|
|
60
78
|
targetPointIndex: { type: "number", minimum: 0, maximum: 3 },
|
|
61
79
|
label: { type: "string" },
|
|
62
80
|
color: { type: "string", description: "Edge color (hex)" },
|
|
63
|
-
thickness: { type: "number", description: "Default: 0.01. Min: 0.005, Max: 0.5" }
|
|
81
|
+
thickness: { type: "number", description: "Default: 0.01. Min: 0.005, Max: 0.5" },
|
|
82
|
+
style: { type: "string", enum: ["line", "arrow-source", "arrow-target", "arrow-both"], description: "Visual style of arrowheads" },
|
|
83
|
+
routingType: { type: "string", enum: ["straight", "orthogonal"], description: "Type of path routing" },
|
|
84
|
+
borderStyle: { type: "string", enum: ["solid", "dashed", "dotted"], description: "Line pattern style" },
|
|
85
|
+
terminationStart: { type: "string", enum: ['none', 'arrow', 'triangle-empty', 'triangle-filled', 'diamond-empty', 'diamond-filled', 'circle', 'circle-plus', 'crows-one', 'crows-many', 'crows-zero-one', 'crows-zero-many'], description: "Start termination shape" },
|
|
86
|
+
terminationEnd: { type: "string", enum: ['none', 'arrow', 'triangle-empty', 'triangle-filled', 'diamond-empty', 'diamond-filled', 'circle', 'circle-plus', 'crows-one', 'crows-many', 'crows-zero-one', 'crows-zero-many'], description: "End termination shape" },
|
|
87
|
+
id: { type: "string", description: "Optional explicit ID" }
|
|
64
88
|
},
|
|
65
89
|
required: ["filePath", "sourceId", "targetId", "sourcePointIndex", "targetPointIndex"]
|
|
66
90
|
}
|
|
@@ -124,14 +148,76 @@ exports.toolDefinitions = [
|
|
|
124
148
|
sourceNodeId: { type: "string" },
|
|
125
149
|
direction: { type: "string", enum: ["UP", "DOWN", "LEFT", "RIGHT"], description: "Direction to place the new node relative to source." },
|
|
126
150
|
label: { type: "string" },
|
|
127
|
-
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table"] },
|
|
151
|
+
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table", "start-state", "end-state", "fork-join"] },
|
|
128
152
|
width: { type: "number", description: "Default: 2. Min: 0.1, Max: 50" },
|
|
129
153
|
height: { type: "number", description: "Default: 1.5. Min: 0.1, Max: 50" },
|
|
130
154
|
color: { type: "string" },
|
|
131
|
-
edgeLabel: { type: "string", description: "Optional label for the connecting edge" }
|
|
155
|
+
edgeLabel: { type: "string", description: "Optional label for the connecting edge" },
|
|
156
|
+
attributes: { type: "array", items: { type: "string" }, description: "Optional list of attributes for Class nodes" },
|
|
157
|
+
methods: { type: "array", items: { type: "string" }, description: "Optional list of methods for Class nodes" }
|
|
132
158
|
},
|
|
133
159
|
required: ["filePath", "sourceNodeId", "direction", "label"]
|
|
134
160
|
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "create_uml_class",
|
|
164
|
+
description: "Create a UML Class node with methods and attributes",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
filePath: { type: "string" },
|
|
169
|
+
className: { type: "string" },
|
|
170
|
+
attributes: { type: "array", items: { type: "string" }, description: "List of attributes e.g. ['- id: int', '+ name: string']" },
|
|
171
|
+
methods: { type: "array", items: { type: "string" }, description: "List of methods e.g. ['+ getName(): string', '+ save(): void']" },
|
|
172
|
+
x: { type: "number", description: "Optional X position" },
|
|
173
|
+
y: { type: "number", description: "Optional Y position (mapped to Z in 3DUML)" },
|
|
174
|
+
id: { type: "string", description: "Optional explicit ID" }
|
|
175
|
+
},
|
|
176
|
+
required: ["filePath", "className"]
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "create_db_table",
|
|
181
|
+
description: "Create a Database Entity (ERD Table)",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: "object",
|
|
184
|
+
properties: {
|
|
185
|
+
filePath: { type: "string" },
|
|
186
|
+
tableName: { type: "string" },
|
|
187
|
+
columns: {
|
|
188
|
+
type: "array",
|
|
189
|
+
items: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
name: { type: "string" },
|
|
193
|
+
type: { type: "string" },
|
|
194
|
+
isPk: { type: "boolean" },
|
|
195
|
+
isFk: { type: "boolean" }
|
|
196
|
+
},
|
|
197
|
+
required: ["name", "type"]
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
x: { type: "number" },
|
|
201
|
+
y: { type: "number" },
|
|
202
|
+
id: { type: "string", description: "Optional explicit ID" }
|
|
203
|
+
},
|
|
204
|
+
required: ["filePath", "tableName", "columns"]
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "connect_entities",
|
|
209
|
+
description: "Connect two entities semantically (e.g. Inherits, Composes)",
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
filePath: { type: "string" },
|
|
214
|
+
sourceName: { type: "string", description: "Label of source node (e.g. Class Name)" },
|
|
215
|
+
targetName: { type: "string", description: "Label of target node" },
|
|
216
|
+
relationType: { type: "string", enum: ["association", "inheritance", "implementation", "dependency", "aggregation", "composition", "one-to-one", "one-to-many", "many-to-many"] },
|
|
217
|
+
label: { type: "string", description: "Optional edge label" }
|
|
218
|
+
},
|
|
219
|
+
required: ["filePath", "sourceName", "targetName", "relationType"]
|
|
220
|
+
}
|
|
135
221
|
}
|
|
136
222
|
];
|
|
137
223
|
async function handleToolCall(name, args) {
|
|
@@ -200,7 +286,7 @@ async function handleToolCall(name, args) {
|
|
|
200
286
|
}
|
|
201
287
|
}
|
|
202
288
|
const newNode = {
|
|
203
|
-
id: (0, crypto_1.randomUUID)(),
|
|
289
|
+
id: args.id || (0, crypto_1.randomUUID)(),
|
|
204
290
|
x: x,
|
|
205
291
|
y: 0.05,
|
|
206
292
|
z: z,
|
|
@@ -210,6 +296,12 @@ async function handleToolCall(name, args) {
|
|
|
210
296
|
shape: args.shape || "rectangle",
|
|
211
297
|
layerId: args.layerId || state.activeLayerId,
|
|
212
298
|
backgroundColor: args.color,
|
|
299
|
+
description: args.description,
|
|
300
|
+
tableData: args.tableData,
|
|
301
|
+
classData: (args.attributes || args.methods) ? {
|
|
302
|
+
attributes: args.attributes || [],
|
|
303
|
+
methods: args.methods || []
|
|
304
|
+
} : undefined,
|
|
213
305
|
textAlignVertical: 'center',
|
|
214
306
|
textAlignHorizontal: 'center'
|
|
215
307
|
};
|
|
@@ -306,6 +398,10 @@ async function handleToolCall(name, args) {
|
|
|
306
398
|
shape: args.shape || "rectangle",
|
|
307
399
|
layerId: sourceNode.layerId, // Inherit layer
|
|
308
400
|
backgroundColor: args.color || sourceNode.backgroundColor, // Inherit or new
|
|
401
|
+
classData: (args.attributes || args.methods) ? {
|
|
402
|
+
attributes: args.attributes || [],
|
|
403
|
+
methods: args.methods || []
|
|
404
|
+
} : undefined,
|
|
309
405
|
textAlignVertical: 'center',
|
|
310
406
|
textAlignHorizontal: 'center'
|
|
311
407
|
};
|
|
@@ -338,18 +434,20 @@ async function handleToolCall(name, args) {
|
|
|
338
434
|
if (!targetExists)
|
|
339
435
|
throw new Error(`Target node ${args.targetId} not found`);
|
|
340
436
|
const newEdge = {
|
|
341
|
-
id: (0, crypto_1.randomUUID)(),
|
|
437
|
+
id: args.id || (0, crypto_1.randomUUID)(),
|
|
342
438
|
sourceId: args.sourceId,
|
|
343
439
|
targetId: args.targetId,
|
|
344
440
|
sourcePointIndex: Number(args.sourcePointIndex),
|
|
345
441
|
targetPointIndex: Number(args.targetPointIndex),
|
|
346
442
|
label: args.label,
|
|
347
|
-
style: 'line',
|
|
348
|
-
routingType: 'straight',
|
|
443
|
+
style: args.style || 'line',
|
|
444
|
+
routingType: args.routingType || 'straight',
|
|
349
445
|
color: args.color || '#000000',
|
|
350
446
|
thickness: Math.max(0.005, Math.min(0.5, args.thickness ? Number(args.thickness) : 0.01)),
|
|
351
447
|
fontSize: 20,
|
|
352
|
-
borderStyle: 'solid'
|
|
448
|
+
borderStyle: args.borderStyle || 'solid',
|
|
449
|
+
terminationStart: args.terminationStart,
|
|
450
|
+
terminationEnd: args.terminationEnd
|
|
353
451
|
};
|
|
354
452
|
state.edges.push(newEdge);
|
|
355
453
|
await (0, fileHandler_1.saveDiagramFile)(args.filePath, state);
|
|
@@ -420,6 +518,139 @@ async function handleToolCall(name, args) {
|
|
|
420
518
|
await (0, fileHandler_1.saveDiagramFile)(args.filePath, state);
|
|
421
519
|
return { content: [{ type: "text", text: `Added layer '${args.name}' (ID: ${newLayerId}) at Y=${position.y}` }] };
|
|
422
520
|
}
|
|
521
|
+
case "create_uml_class": {
|
|
522
|
+
const state = await (0, fileHandler_1.readDiagramFile)(args.filePath);
|
|
523
|
+
const newNodeId = args.id || (0, crypto_1.randomUUID)();
|
|
524
|
+
// Standard size for class
|
|
525
|
+
const width = 3;
|
|
526
|
+
// dynamic height?
|
|
527
|
+
const attrCount = (args.attributes || []).length;
|
|
528
|
+
const methodCount = (args.methods || []).length;
|
|
529
|
+
const estimatedHeight = 0.8 + (attrCount + methodCount) * 0.4;
|
|
530
|
+
const height = Math.max(2, estimatedHeight);
|
|
531
|
+
const newNode = {
|
|
532
|
+
id: newNodeId,
|
|
533
|
+
x: args.x ? Number(args.x) : 0,
|
|
534
|
+
y: 0,
|
|
535
|
+
z: args.y ? Number(args.y) : 0,
|
|
536
|
+
width: width,
|
|
537
|
+
height: height,
|
|
538
|
+
label: args.className,
|
|
539
|
+
shape: "class",
|
|
540
|
+
layerId: state.layers[0]?.id || (state.nodes.length > 0 ? state.nodes[0].layerId : "default"),
|
|
541
|
+
backgroundColor: "#ffffff",
|
|
542
|
+
classData: {
|
|
543
|
+
attributes: args.attributes || [],
|
|
544
|
+
methods: args.methods || []
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
state.nodes.push(newNode);
|
|
548
|
+
await (0, fileHandler_1.saveDiagramFile)(args.filePath, state);
|
|
549
|
+
return { content: [{ type: "text", text: `Created UML Class '${args.className}'` }] };
|
|
550
|
+
}
|
|
551
|
+
case "create_db_table": {
|
|
552
|
+
const state = await (0, fileHandler_1.readDiagramFile)(args.filePath);
|
|
553
|
+
const newNodeId = args.id || (0, crypto_1.randomUUID)();
|
|
554
|
+
const cols = args.columns || [];
|
|
555
|
+
// Header + items
|
|
556
|
+
const estimatedHeight = 0.8 + cols.length * 0.4;
|
|
557
|
+
const height = Math.max(2, estimatedHeight);
|
|
558
|
+
const width = 3;
|
|
559
|
+
const newNode = {
|
|
560
|
+
id: newNodeId,
|
|
561
|
+
x: args.x ? Number(args.x) : 0,
|
|
562
|
+
y: 0,
|
|
563
|
+
z: args.y ? Number(args.y) : 0,
|
|
564
|
+
width: width,
|
|
565
|
+
height: height,
|
|
566
|
+
label: args.tableName,
|
|
567
|
+
shape: "db-table",
|
|
568
|
+
layerId: state.layers[0]?.id || (state.nodes.length > 0 ? state.nodes[0].layerId : "default"),
|
|
569
|
+
backgroundColor: "#ffffff",
|
|
570
|
+
dbTableData: {
|
|
571
|
+
columns: cols.map((c) => ({
|
|
572
|
+
name: c.name,
|
|
573
|
+
type: c.type,
|
|
574
|
+
isPk: !!c.isPk,
|
|
575
|
+
isFk: !!c.isFk
|
|
576
|
+
}))
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
state.nodes.push(newNode);
|
|
580
|
+
await (0, fileHandler_1.saveDiagramFile)(args.filePath, state);
|
|
581
|
+
return { content: [{ type: "text", text: `Created DB Table '${args.tableName}'` }] };
|
|
582
|
+
}
|
|
583
|
+
case "connect_entities": {
|
|
584
|
+
const state = await (0, fileHandler_1.readDiagramFile)(args.filePath);
|
|
585
|
+
// Find nodes by Label (tolerant search?)
|
|
586
|
+
const sourceNode = state.nodes.find(n => n.label === args.sourceName);
|
|
587
|
+
const targetNode = state.nodes.find(n => n.label === args.targetName);
|
|
588
|
+
if (!sourceNode)
|
|
589
|
+
throw new Error(`Source entity '${args.sourceName}' not found`);
|
|
590
|
+
if (!targetNode)
|
|
591
|
+
throw new Error(`Target entity '${args.targetName}' not found`);
|
|
592
|
+
let terminationStart = 'none';
|
|
593
|
+
let terminationEnd = 'none';
|
|
594
|
+
let style = 'line';
|
|
595
|
+
let borderStyle = 'solid';
|
|
596
|
+
// Map relationship to terminations
|
|
597
|
+
switch (args.relationType) {
|
|
598
|
+
case 'inheritance':
|
|
599
|
+
terminationEnd = 'triangle-empty';
|
|
600
|
+
break;
|
|
601
|
+
case 'implementation':
|
|
602
|
+
terminationEnd = 'triangle-empty';
|
|
603
|
+
borderStyle = 'dashed';
|
|
604
|
+
break;
|
|
605
|
+
case 'dependency':
|
|
606
|
+
terminationEnd = 'arrow';
|
|
607
|
+
borderStyle = 'dashed';
|
|
608
|
+
break;
|
|
609
|
+
case 'association':
|
|
610
|
+
terminationEnd = 'none'; // simple line? or arrow? normally association is simple line OR arrow
|
|
611
|
+
// Let's use arrow for directed association if implied?
|
|
612
|
+
// Usually "connect A to B" implies direction.
|
|
613
|
+
terminationEnd = 'arrow';
|
|
614
|
+
break;
|
|
615
|
+
case 'aggregation':
|
|
616
|
+
terminationStart = 'diamond-empty';
|
|
617
|
+
break;
|
|
618
|
+
case 'composition':
|
|
619
|
+
terminationStart = 'diamond-filled';
|
|
620
|
+
break;
|
|
621
|
+
case 'one-to-many':
|
|
622
|
+
terminationStart = 'crows-one';
|
|
623
|
+
terminationEnd = 'crows-many';
|
|
624
|
+
break;
|
|
625
|
+
case 'many-to-many':
|
|
626
|
+
terminationStart = 'crows-many';
|
|
627
|
+
terminationEnd = 'crows-many';
|
|
628
|
+
break;
|
|
629
|
+
case 'one-to-one':
|
|
630
|
+
terminationStart = 'crows-one';
|
|
631
|
+
terminationEnd = 'crows-one';
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
const newEdge = {
|
|
635
|
+
id: (0, crypto_1.randomUUID)(),
|
|
636
|
+
sourceId: sourceNode.id,
|
|
637
|
+
targetId: targetNode.id,
|
|
638
|
+
sourcePointIndex: 2, // Bottom of source
|
|
639
|
+
targetPointIndex: 0, // Top of target (simple default)
|
|
640
|
+
label: args.label,
|
|
641
|
+
style: style,
|
|
642
|
+
routingType: 'orthogonal', // Semantic diagrams usually look better with orthogonal
|
|
643
|
+
color: '#000000',
|
|
644
|
+
thickness: 0.01,
|
|
645
|
+
fontSize: 20,
|
|
646
|
+
borderStyle: borderStyle,
|
|
647
|
+
terminationStart: terminationStart,
|
|
648
|
+
terminationEnd: terminationEnd
|
|
649
|
+
};
|
|
650
|
+
state.edges.push(newEdge);
|
|
651
|
+
await (0, fileHandler_1.saveDiagramFile)(args.filePath, state);
|
|
652
|
+
return { content: [{ type: "text", text: `Connected '${args.sourceName}' to '${args.targetName}' via ${args.relationType}` }] };
|
|
653
|
+
}
|
|
423
654
|
default:
|
|
424
655
|
throw new Error(`Unknown tool: ${name}`);
|
|
425
656
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
|
|
2
|
+
import { handleToolCall } from './tools/diagramTools';
|
|
3
|
+
|
|
4
|
+
async function createClassDiagram() {
|
|
5
|
+
const FILE_PATH = "class_diagram_example.3duml";
|
|
6
|
+
|
|
7
|
+
console.log(`Creating class diagram example at ${FILE_PATH}...`);
|
|
8
|
+
|
|
9
|
+
// 1. Create Diagram
|
|
10
|
+
try {
|
|
11
|
+
await handleToolCall("create_new_diagram", { filePath: FILE_PATH });
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.log("File might exist, continuing...");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 2. Add Layer
|
|
17
|
+
await handleToolCall("add_layer", { filePath: FILE_PATH, name: "Classes" });
|
|
18
|
+
|
|
19
|
+
// 3. Create Classes
|
|
20
|
+
|
|
21
|
+
// Abstract Class: GraphicObject
|
|
22
|
+
await handleToolCall("create_uml_class", {
|
|
23
|
+
filePath: FILE_PATH,
|
|
24
|
+
className: "GraphicObject",
|
|
25
|
+
attributes: ["x: int", "y: int"],
|
|
26
|
+
methods: ["move(dx: int, dy: int)", "draw()"],
|
|
27
|
+
x: 0, y: -4,
|
|
28
|
+
id: "GraphicObject"
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Subclass: Circle
|
|
32
|
+
await handleToolCall("create_uml_class", {
|
|
33
|
+
filePath: FILE_PATH,
|
|
34
|
+
className: "Circle",
|
|
35
|
+
attributes: ["radius: float"],
|
|
36
|
+
methods: ["draw()", "resize(scale: float)"],
|
|
37
|
+
x: -4, y: 0,
|
|
38
|
+
id: "Circle"
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Subclass: Rectangle
|
|
42
|
+
await handleToolCall("create_uml_class", {
|
|
43
|
+
filePath: FILE_PATH,
|
|
44
|
+
className: "Rectangle",
|
|
45
|
+
attributes: ["width: float", "height: float"],
|
|
46
|
+
methods: ["draw()", "resize(scale: float)"],
|
|
47
|
+
x: 4, y: 0,
|
|
48
|
+
id: "Rectangle"
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Container: Group
|
|
52
|
+
await handleToolCall("create_uml_class", {
|
|
53
|
+
filePath: FILE_PATH,
|
|
54
|
+
className: "Group",
|
|
55
|
+
attributes: [],
|
|
56
|
+
methods: ["add(obj: GraphicObject)", "remove(obj: GraphicObject)", "draw()"],
|
|
57
|
+
x: 0, y: 4,
|
|
58
|
+
id: "Group"
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Client: Canvas
|
|
62
|
+
await handleToolCall("create_uml_class", {
|
|
63
|
+
filePath: FILE_PATH,
|
|
64
|
+
className: "Canvas",
|
|
65
|
+
attributes: ["width: int", "height: int", "backgroundColor: string"],
|
|
66
|
+
methods: ["render()", "clear()"],
|
|
67
|
+
x: 8, y: -4,
|
|
68
|
+
id: "Canvas"
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 4. Create Edges (Relationships)
|
|
72
|
+
|
|
73
|
+
// Inheritance: Circle -> GraphicObject
|
|
74
|
+
await handleToolCall("add_edge", {
|
|
75
|
+
filePath: FILE_PATH,
|
|
76
|
+
sourceId: "Circle",
|
|
77
|
+
targetId: "GraphicObject",
|
|
78
|
+
sourcePointIndex: 0, // Top
|
|
79
|
+
targetPointIndex: 3, // Left (or Bottom of GraphicObject depending on layout) -> GraphicObject is at -4 Z (UP). Circle at 0 Z.
|
|
80
|
+
// Circle (0,0) is BELOW GraphicObject (0, -4).
|
|
81
|
+
// So Circle TOP (0) connects to GraphicObject BOTTOM (2).
|
|
82
|
+
// Wait, Z is vertical in 2D top-down view?
|
|
83
|
+
// Logic: Z decreases going UP.
|
|
84
|
+
// GraphicObject (z=-4) is UP. Circle (z=0) is DOWN.
|
|
85
|
+
// Connect Circle Top (0) to GraphicObject Bottom (2).
|
|
86
|
+
terminationEnd: "triangle-empty", // Inheritance arrow on Parent
|
|
87
|
+
terminationStart: "none",
|
|
88
|
+
routingType: "orthogonal",
|
|
89
|
+
label: "extends",
|
|
90
|
+
id: "edge_circle_extends"
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Inheritance: Rectangle -> GraphicObject
|
|
94
|
+
await handleToolCall("add_edge", {
|
|
95
|
+
filePath: FILE_PATH,
|
|
96
|
+
sourceId: "Rectangle",
|
|
97
|
+
targetId: "GraphicObject",
|
|
98
|
+
sourcePointIndex: 0, // Top
|
|
99
|
+
targetPointIndex: 2, // Bottom
|
|
100
|
+
terminationEnd: "triangle-empty",
|
|
101
|
+
terminationStart: "none",
|
|
102
|
+
routingType: "orthogonal",
|
|
103
|
+
id: "edge_rect_extends"
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Aggregation: Group has GraphicObjects
|
|
107
|
+
// Group (z=4) is BELOW GraphicObject (z=-4)?
|
|
108
|
+
// No, Group is at z=4 (Down). GraphicObject is at z=-4 (Up).
|
|
109
|
+
// Connection: Group (Top/0) -> GraphicObject (Bottom/2) ??
|
|
110
|
+
// Actually Group "contains" GraphicObject.
|
|
111
|
+
// The Diamond is on the Group end.
|
|
112
|
+
// Source: Group. Target: GraphicObject.
|
|
113
|
+
// Diamond at Source. Arrow/None at Target.
|
|
114
|
+
await handleToolCall("add_edge", {
|
|
115
|
+
filePath: FILE_PATH,
|
|
116
|
+
sourceId: "Group",
|
|
117
|
+
targetId: "GraphicObject",
|
|
118
|
+
sourcePointIndex: 0, // Top
|
|
119
|
+
targetPointIndex: 2, // Bottom
|
|
120
|
+
terminationStart: "diamond-empty", // Aggregation at Source
|
|
121
|
+
terminationEnd: "arrow", // Association at Target (or none)
|
|
122
|
+
routingType: "orthogonal",
|
|
123
|
+
label: "contains",
|
|
124
|
+
id: "edge_group_aggregation"
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Composition: Canvas owns GraphicObjects?
|
|
128
|
+
// Or maybe Canvas owns a Root Group?
|
|
129
|
+
// Let's say Canvas has a list of Shapes.
|
|
130
|
+
// Canvas (8, -4) -> GraphicObject (0, -4).
|
|
131
|
+
// Canvas Left (3) -> GraphicObject Right (1).
|
|
132
|
+
await handleToolCall("add_edge", {
|
|
133
|
+
filePath: FILE_PATH,
|
|
134
|
+
sourceId: "Canvas",
|
|
135
|
+
targetId: "GraphicObject",
|
|
136
|
+
sourcePointIndex: 3, // Left
|
|
137
|
+
targetPointIndex: 1, // Right
|
|
138
|
+
terminationStart: "diamond-filled", // Composition (Canvas owns Objects)
|
|
139
|
+
terminationEnd: "none",
|
|
140
|
+
routingType: "straight",
|
|
141
|
+
label: "renders",
|
|
142
|
+
id: "edge_canvas_composition"
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log("Class Diagram created successfully: " + FILE_PATH);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
createClassDiagram().catch(err => {
|
|
149
|
+
console.error("Error creating class diagram:", err);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
|
|
2
|
+
import { handleToolCall } from './tools/diagramTools';
|
|
3
|
+
|
|
4
|
+
interface TerminationType {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
description: string;
|
|
8
|
+
isDashed?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function createReference() {
|
|
12
|
+
const FILE_PATH = "edge_terminations_test.3duml";
|
|
13
|
+
|
|
14
|
+
console.log(`Creating diagram at ${FILE_PATH}...`);
|
|
15
|
+
|
|
16
|
+
// 1. Create Diagram
|
|
17
|
+
try {
|
|
18
|
+
await handleToolCall("create_new_diagram", { filePath: FILE_PATH });
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.log("File might exist, continuing...");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. Add Layer
|
|
24
|
+
await handleToolCall("add_layer", { filePath: FILE_PATH, name: "Terminations" });
|
|
25
|
+
|
|
26
|
+
// 3. Define Types
|
|
27
|
+
const types: TerminationType[] = [
|
|
28
|
+
{ id: 'none', label: 'None', description: 'No visual marker' },
|
|
29
|
+
{ id: 'arrow', label: 'Arrow (Association)', description: 'Solid cone/V-shape' },
|
|
30
|
+
{ id: 'triangle-empty', label: 'Inheritance', description: 'Hollow triangle' },
|
|
31
|
+
{ id: 'triangle-empty-dashed', label: 'Realization', description: 'Hollow triangle (dashed edge)', isDashed: true },
|
|
32
|
+
{ id: 'diamond-filled', label: 'Composition', description: 'Filled diamond' },
|
|
33
|
+
{ id: 'diamond-empty', label: 'Aggregation', description: 'Hollow diamond' },
|
|
34
|
+
{ id: 'arrow-open', label: 'Dependency', description: 'Open V-shape (dashed)', isDashed: true },
|
|
35
|
+
{ id: 'crows-one', label: 'Crow One', description: 'Perpendicular bar |' },
|
|
36
|
+
{ id: 'crows-many', label: 'Crow Many', description: 'Trident |<' },
|
|
37
|
+
{ id: 'crows-zero-one', label: 'Crow Zero-One', description: 'Circle and bar O|' },
|
|
38
|
+
{ id: 'crows-zero-many', label: 'Crow Zero-Many', description: 'Circle and trident O|<' },
|
|
39
|
+
{ id: 'circle', label: 'Circle', description: 'Hollow circle' },
|
|
40
|
+
{ id: 'circle-plus', label: 'Socket', description: 'Circle with plus (Socket)' }
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
let z = 0;
|
|
44
|
+
const SPACING_Z = 3.5;
|
|
45
|
+
|
|
46
|
+
// Use manual IDs to avoid importing randomUUID
|
|
47
|
+
let counter = 0;
|
|
48
|
+
|
|
49
|
+
for (const t of types) {
|
|
50
|
+
const sourceId = `src_${t.id}`;
|
|
51
|
+
const targetId = `tgt_${t.id}`;
|
|
52
|
+
|
|
53
|
+
// Start Node
|
|
54
|
+
await handleToolCall("add_node", {
|
|
55
|
+
filePath: FILE_PATH,
|
|
56
|
+
label: `Start`,
|
|
57
|
+
id: sourceId,
|
|
58
|
+
x: -4, y: z,
|
|
59
|
+
width: 2, height: 1.5,
|
|
60
|
+
color: "#eeeeee"
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// End Node
|
|
64
|
+
await handleToolCall("add_node", {
|
|
65
|
+
filePath: FILE_PATH,
|
|
66
|
+
label: `End`,
|
|
67
|
+
id: targetId,
|
|
68
|
+
x: 4, y: z,
|
|
69
|
+
width: 2, height: 1.5,
|
|
70
|
+
color: "#eeeeee"
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Description Note (Left side)
|
|
74
|
+
await handleToolCall("add_node", {
|
|
75
|
+
filePath: FILE_PATH,
|
|
76
|
+
label: `${t.label}\n${t.description}`,
|
|
77
|
+
shape: 'note',
|
|
78
|
+
color: '#ffffcc',
|
|
79
|
+
x: -10, y: z,
|
|
80
|
+
width: 4, height: 2,
|
|
81
|
+
id: `note_${t.id}`
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Edge
|
|
85
|
+
await handleToolCall("add_edge", {
|
|
86
|
+
filePath: FILE_PATH,
|
|
87
|
+
sourceId: sourceId,
|
|
88
|
+
targetId: targetId,
|
|
89
|
+
sourcePointIndex: 1, // Right
|
|
90
|
+
targetPointIndex: 3, // Left
|
|
91
|
+
label: t.id,
|
|
92
|
+
borderStyle: t.isDashed ? 'dashed' : 'solid',
|
|
93
|
+
terminationEnd: t.id,
|
|
94
|
+
terminationStart: 'none',
|
|
95
|
+
id: `edge_${t.id}`
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
z += SPACING_Z;
|
|
99
|
+
counter++;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log("Diagram created successfully: " + FILE_PATH);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
createReference().catch(err => {
|
|
106
|
+
console.error("Error creating reference:", err);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
});
|
|
@@ -35,14 +35,32 @@ export const toolDefinitions: Tool[] = [
|
|
|
35
35
|
properties: {
|
|
36
36
|
filePath: { type: "string" },
|
|
37
37
|
label: { type: "string" },
|
|
38
|
-
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table"] },
|
|
38
|
+
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table", "start-state", "end-state", "fork-join"] },
|
|
39
39
|
x: { type: "number", description: "Optional. defaults to auto-placement" },
|
|
40
40
|
y: { type: "number", description: "Optional. defaults to 0 or same as last node" },
|
|
41
41
|
z: { type: "number" },
|
|
42
42
|
width: { type: "number", description: "Default: 2. Min: 0.1, Max: 50" },
|
|
43
43
|
height: { type: "number", description: "Default: 1.5. Min: 0.1, Max: 50" },
|
|
44
44
|
layerId: { type: "string", description: "Optional layer ID, defaults to active layer" },
|
|
45
|
-
color: { type: "string" }
|
|
45
|
+
color: { type: "string" },
|
|
46
|
+
description: { type: "string", description: "Optional description or metadata for the node" },
|
|
47
|
+
tableData: {
|
|
48
|
+
type: "object",
|
|
49
|
+
description: "Required if shape is 'table'",
|
|
50
|
+
properties: {
|
|
51
|
+
rows: { type: "number" },
|
|
52
|
+
columns: { type: "number" },
|
|
53
|
+
hasRowHeader: { type: "boolean" },
|
|
54
|
+
hasColumnHeader: { type: "boolean" },
|
|
55
|
+
cells: {
|
|
56
|
+
type: "object",
|
|
57
|
+
description: "Map of 'row,col' string keys to cell content strings. E.g. {'0,0': 'Header'}"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
id: { type: "string", description: "Optional explicit ID" },
|
|
62
|
+
attributes: { type: "array", items: { type: "string" }, description: "Optional list of attributes for Class nodes" },
|
|
63
|
+
methods: { type: "array", items: { type: "string" }, description: "Optional list of methods for Class nodes" }
|
|
46
64
|
},
|
|
47
65
|
required: ["filePath", "label"]
|
|
48
66
|
}
|
|
@@ -60,7 +78,13 @@ export const toolDefinitions: Tool[] = [
|
|
|
60
78
|
targetPointIndex: { type: "number", minimum: 0, maximum: 3 },
|
|
61
79
|
label: { type: "string" },
|
|
62
80
|
color: { type: "string", description: "Edge color (hex)" },
|
|
63
|
-
thickness: { type: "number", description: "Default: 0.01. Min: 0.005, Max: 0.5" }
|
|
81
|
+
thickness: { type: "number", description: "Default: 0.01. Min: 0.005, Max: 0.5" },
|
|
82
|
+
style: { type: "string", enum: ["line", "arrow-source", "arrow-target", "arrow-both"], description: "Visual style of arrowheads" },
|
|
83
|
+
routingType: { type: "string", enum: ["straight", "orthogonal"], description: "Type of path routing" },
|
|
84
|
+
borderStyle: { type: "string", enum: ["solid", "dashed", "dotted"], description: "Line pattern style" },
|
|
85
|
+
terminationStart: { type: "string", enum: ['none', 'arrow', 'triangle-empty', 'triangle-filled', 'diamond-empty', 'diamond-filled', 'circle', 'circle-plus', 'crows-one', 'crows-many', 'crows-zero-one', 'crows-zero-many'], description: "Start termination shape" },
|
|
86
|
+
terminationEnd: { type: "string", enum: ['none', 'arrow', 'triangle-empty', 'triangle-filled', 'diamond-empty', 'diamond-filled', 'circle', 'circle-plus', 'crows-one', 'crows-many', 'crows-zero-one', 'crows-zero-many'], description: "End termination shape" },
|
|
87
|
+
id: { type: "string", description: "Optional explicit ID" }
|
|
64
88
|
},
|
|
65
89
|
required: ["filePath", "sourceId", "targetId", "sourcePointIndex", "targetPointIndex"]
|
|
66
90
|
}
|
|
@@ -124,14 +148,76 @@ export const toolDefinitions: Tool[] = [
|
|
|
124
148
|
sourceNodeId: { type: "string" },
|
|
125
149
|
direction: { type: "string", enum: ["UP", "DOWN", "LEFT", "RIGHT"], description: "Direction to place the new node relative to source." },
|
|
126
150
|
label: { type: "string" },
|
|
127
|
-
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table"] },
|
|
151
|
+
shape: { type: "string", enum: ["rectangle", "rounded", "ellipse", "diamond", "actor", "cylinder", "note", "cloud", "class", "text", "table", "start-state", "end-state", "fork-join"] },
|
|
128
152
|
width: { type: "number", description: "Default: 2. Min: 0.1, Max: 50" },
|
|
129
153
|
height: { type: "number", description: "Default: 1.5. Min: 0.1, Max: 50" },
|
|
130
154
|
color: { type: "string" },
|
|
131
|
-
edgeLabel: { type: "string", description: "Optional label for the connecting edge" }
|
|
155
|
+
edgeLabel: { type: "string", description: "Optional label for the connecting edge" },
|
|
156
|
+
attributes: { type: "array", items: { type: "string" }, description: "Optional list of attributes for Class nodes" },
|
|
157
|
+
methods: { type: "array", items: { type: "string" }, description: "Optional list of methods for Class nodes" }
|
|
132
158
|
},
|
|
133
159
|
required: ["filePath", "sourceNodeId", "direction", "label"]
|
|
134
160
|
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "create_uml_class",
|
|
164
|
+
description: "Create a UML Class node with methods and attributes",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
filePath: { type: "string" },
|
|
169
|
+
className: { type: "string" },
|
|
170
|
+
attributes: { type: "array", items: { type: "string" }, description: "List of attributes e.g. ['- id: int', '+ name: string']" },
|
|
171
|
+
methods: { type: "array", items: { type: "string" }, description: "List of methods e.g. ['+ getName(): string', '+ save(): void']" },
|
|
172
|
+
x: { type: "number", description: "Optional X position" },
|
|
173
|
+
y: { type: "number", description: "Optional Y position (mapped to Z in 3DUML)" },
|
|
174
|
+
id: { type: "string", description: "Optional explicit ID" }
|
|
175
|
+
},
|
|
176
|
+
required: ["filePath", "className"]
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "create_db_table",
|
|
181
|
+
description: "Create a Database Entity (ERD Table)",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: "object",
|
|
184
|
+
properties: {
|
|
185
|
+
filePath: { type: "string" },
|
|
186
|
+
tableName: { type: "string" },
|
|
187
|
+
columns: {
|
|
188
|
+
type: "array",
|
|
189
|
+
items: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
name: { type: "string" },
|
|
193
|
+
type: { type: "string" },
|
|
194
|
+
isPk: { type: "boolean" },
|
|
195
|
+
isFk: { type: "boolean" }
|
|
196
|
+
},
|
|
197
|
+
required: ["name", "type"]
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
x: { type: "number" },
|
|
201
|
+
y: { type: "number" },
|
|
202
|
+
id: { type: "string", description: "Optional explicit ID" }
|
|
203
|
+
},
|
|
204
|
+
required: ["filePath", "tableName", "columns"]
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "connect_entities",
|
|
209
|
+
description: "Connect two entities semantically (e.g. Inherits, Composes)",
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
filePath: { type: "string" },
|
|
214
|
+
sourceName: { type: "string", description: "Label of source node (e.g. Class Name)" },
|
|
215
|
+
targetName: { type: "string", description: "Label of target node" },
|
|
216
|
+
relationType: { type: "string", enum: ["association", "inheritance", "implementation", "dependency", "aggregation", "composition", "one-to-one", "one-to-many", "many-to-many"] },
|
|
217
|
+
label: { type: "string", description: "Optional edge label" }
|
|
218
|
+
},
|
|
219
|
+
required: ["filePath", "sourceName", "targetName", "relationType"]
|
|
220
|
+
}
|
|
135
221
|
}
|
|
136
222
|
];
|
|
137
223
|
|
|
@@ -205,7 +291,7 @@ export async function handleToolCall(name: string, args: any): Promise<any> {
|
|
|
205
291
|
}
|
|
206
292
|
|
|
207
293
|
const newNode: NodeData = {
|
|
208
|
-
id: randomUUID(),
|
|
294
|
+
id: args.id || randomUUID(),
|
|
209
295
|
x: x,
|
|
210
296
|
y: 0.05,
|
|
211
297
|
z: z,
|
|
@@ -215,6 +301,12 @@ export async function handleToolCall(name: string, args: any): Promise<any> {
|
|
|
215
301
|
shape: args.shape || "rectangle",
|
|
216
302
|
layerId: args.layerId || state.activeLayerId,
|
|
217
303
|
backgroundColor: args.color,
|
|
304
|
+
description: args.description,
|
|
305
|
+
tableData: args.tableData,
|
|
306
|
+
classData: (args.attributes || args.methods) ? {
|
|
307
|
+
attributes: args.attributes || [],
|
|
308
|
+
methods: args.methods || []
|
|
309
|
+
} : undefined,
|
|
218
310
|
textAlignVertical: 'center',
|
|
219
311
|
textAlignHorizontal: 'center'
|
|
220
312
|
};
|
|
@@ -321,6 +413,10 @@ export async function handleToolCall(name: string, args: any): Promise<any> {
|
|
|
321
413
|
shape: args.shape || "rectangle",
|
|
322
414
|
layerId: sourceNode.layerId, // Inherit layer
|
|
323
415
|
backgroundColor: args.color || sourceNode.backgroundColor, // Inherit or new
|
|
416
|
+
classData: (args.attributes || args.methods) ? {
|
|
417
|
+
attributes: args.attributes || [],
|
|
418
|
+
methods: args.methods || []
|
|
419
|
+
} : undefined,
|
|
324
420
|
textAlignVertical: 'center',
|
|
325
421
|
textAlignHorizontal: 'center'
|
|
326
422
|
};
|
|
@@ -355,18 +451,20 @@ export async function handleToolCall(name: string, args: any): Promise<any> {
|
|
|
355
451
|
if (!targetExists) throw new Error(`Target node ${args.targetId} not found`);
|
|
356
452
|
|
|
357
453
|
const newEdge: EdgeData = {
|
|
358
|
-
id: randomUUID(),
|
|
454
|
+
id: args.id || randomUUID(),
|
|
359
455
|
sourceId: args.sourceId,
|
|
360
456
|
targetId: args.targetId,
|
|
361
457
|
sourcePointIndex: Number(args.sourcePointIndex),
|
|
362
458
|
targetPointIndex: Number(args.targetPointIndex),
|
|
363
459
|
label: args.label,
|
|
364
|
-
style: 'line',
|
|
365
|
-
routingType: 'straight',
|
|
460
|
+
style: args.style || 'line',
|
|
461
|
+
routingType: args.routingType || 'straight',
|
|
366
462
|
color: args.color || '#000000',
|
|
367
463
|
thickness: Math.max(0.005, Math.min(0.5, args.thickness ? Number(args.thickness) : 0.01)),
|
|
368
464
|
fontSize: 20,
|
|
369
|
-
borderStyle: 'solid'
|
|
465
|
+
borderStyle: args.borderStyle || 'solid',
|
|
466
|
+
terminationStart: args.terminationStart,
|
|
467
|
+
terminationEnd: args.terminationEnd
|
|
370
468
|
};
|
|
371
469
|
state.edges.push(newEdge);
|
|
372
470
|
await saveDiagramFile(args.filePath, state);
|
|
@@ -434,6 +532,149 @@ export async function handleToolCall(name: string, args: any): Promise<any> {
|
|
|
434
532
|
await saveDiagramFile(args.filePath, state);
|
|
435
533
|
return { content: [{ type: "text", text: `Added layer '${args.name}' (ID: ${newLayerId}) at Y=${position.y}` }] };
|
|
436
534
|
}
|
|
535
|
+
case "create_uml_class": {
|
|
536
|
+
const state = await readDiagramFile(args.filePath);
|
|
537
|
+
const newNodeId = args.id || randomUUID();
|
|
538
|
+
|
|
539
|
+
// Standard size for class
|
|
540
|
+
const width = 3;
|
|
541
|
+
// dynamic height?
|
|
542
|
+
const attrCount = (args.attributes || []).length;
|
|
543
|
+
const methodCount = (args.methods || []).length;
|
|
544
|
+
const estimatedHeight = 0.8 + (attrCount + methodCount) * 0.4;
|
|
545
|
+
const height = Math.max(2, estimatedHeight);
|
|
546
|
+
|
|
547
|
+
const newNode: NodeData = {
|
|
548
|
+
id: newNodeId,
|
|
549
|
+
x: args.x ? Number(args.x) : 0,
|
|
550
|
+
y: 0,
|
|
551
|
+
z: args.y ? Number(args.y) : 0,
|
|
552
|
+
width: width,
|
|
553
|
+
height: height,
|
|
554
|
+
label: args.className,
|
|
555
|
+
shape: "class",
|
|
556
|
+
layerId: state.layers[0]?.id || (state.nodes.length > 0 ? state.nodes[0].layerId : "default"),
|
|
557
|
+
backgroundColor: "#ffffff",
|
|
558
|
+
classData: {
|
|
559
|
+
attributes: args.attributes || [],
|
|
560
|
+
methods: args.methods || []
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
state.nodes.push(newNode);
|
|
565
|
+
await saveDiagramFile(args.filePath, state);
|
|
566
|
+
return { content: [{ type: "text", text: `Created UML Class '${args.className}'` }] };
|
|
567
|
+
}
|
|
568
|
+
case "create_db_table": {
|
|
569
|
+
const state = await readDiagramFile(args.filePath);
|
|
570
|
+
const newNodeId = args.id || randomUUID();
|
|
571
|
+
|
|
572
|
+
const cols = args.columns || [];
|
|
573
|
+
// Header + items
|
|
574
|
+
const estimatedHeight = 0.8 + cols.length * 0.4;
|
|
575
|
+
const height = Math.max(2, estimatedHeight);
|
|
576
|
+
const width = 3;
|
|
577
|
+
|
|
578
|
+
const newNode: NodeData = {
|
|
579
|
+
id: newNodeId,
|
|
580
|
+
x: args.x ? Number(args.x) : 0,
|
|
581
|
+
y: 0,
|
|
582
|
+
z: args.y ? Number(args.y) : 0,
|
|
583
|
+
width: width,
|
|
584
|
+
height: height,
|
|
585
|
+
label: args.tableName,
|
|
586
|
+
shape: "db-table",
|
|
587
|
+
layerId: state.layers[0]?.id || (state.nodes.length > 0 ? state.nodes[0].layerId : "default"),
|
|
588
|
+
backgroundColor: "#ffffff",
|
|
589
|
+
dbTableData: {
|
|
590
|
+
columns: cols.map((c: any) => ({
|
|
591
|
+
name: c.name,
|
|
592
|
+
type: c.type,
|
|
593
|
+
isPk: !!c.isPk,
|
|
594
|
+
isFk: !!c.isFk
|
|
595
|
+
}))
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
state.nodes.push(newNode);
|
|
600
|
+
await saveDiagramFile(args.filePath, state);
|
|
601
|
+
return { content: [{ type: "text", text: `Created DB Table '${args.tableName}'` }] };
|
|
602
|
+
}
|
|
603
|
+
case "connect_entities": {
|
|
604
|
+
const state = await readDiagramFile(args.filePath);
|
|
605
|
+
|
|
606
|
+
// Find nodes by Label (tolerant search?)
|
|
607
|
+
const sourceNode = state.nodes.find(n => n.label === args.sourceName);
|
|
608
|
+
const targetNode = state.nodes.find(n => n.label === args.targetName);
|
|
609
|
+
|
|
610
|
+
if (!sourceNode) throw new Error(`Source entity '${args.sourceName}' not found`);
|
|
611
|
+
if (!targetNode) throw new Error(`Target entity '${args.targetName}' not found`);
|
|
612
|
+
|
|
613
|
+
let terminationStart = 'none';
|
|
614
|
+
let terminationEnd = 'none';
|
|
615
|
+
let style: 'line' | 'arrow-source' | 'arrow-target' | 'arrow-both' = 'line';
|
|
616
|
+
let borderStyle = 'solid';
|
|
617
|
+
|
|
618
|
+
// Map relationship to terminations
|
|
619
|
+
switch (args.relationType) {
|
|
620
|
+
case 'inheritance':
|
|
621
|
+
terminationEnd = 'triangle-empty';
|
|
622
|
+
break;
|
|
623
|
+
case 'implementation':
|
|
624
|
+
terminationEnd = 'triangle-empty';
|
|
625
|
+
borderStyle = 'dashed';
|
|
626
|
+
break;
|
|
627
|
+
case 'dependency':
|
|
628
|
+
terminationEnd = 'arrow';
|
|
629
|
+
borderStyle = 'dashed';
|
|
630
|
+
break;
|
|
631
|
+
case 'association':
|
|
632
|
+
terminationEnd = 'none'; // simple line? or arrow? normally association is simple line OR arrow
|
|
633
|
+
// Let's use arrow for directed association if implied?
|
|
634
|
+
// Usually "connect A to B" implies direction.
|
|
635
|
+
terminationEnd = 'arrow';
|
|
636
|
+
break;
|
|
637
|
+
case 'aggregation':
|
|
638
|
+
terminationStart = 'diamond-empty';
|
|
639
|
+
break;
|
|
640
|
+
case 'composition':
|
|
641
|
+
terminationStart = 'diamond-filled';
|
|
642
|
+
break;
|
|
643
|
+
case 'one-to-many':
|
|
644
|
+
terminationStart = 'crows-one';
|
|
645
|
+
terminationEnd = 'crows-many';
|
|
646
|
+
break;
|
|
647
|
+
case 'many-to-many':
|
|
648
|
+
terminationStart = 'crows-many';
|
|
649
|
+
terminationEnd = 'crows-many';
|
|
650
|
+
break;
|
|
651
|
+
case 'one-to-one':
|
|
652
|
+
terminationStart = 'crows-one';
|
|
653
|
+
terminationEnd = 'crows-one';
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const newEdge: EdgeData = {
|
|
658
|
+
id: randomUUID(),
|
|
659
|
+
sourceId: sourceNode.id,
|
|
660
|
+
targetId: targetNode.id,
|
|
661
|
+
sourcePointIndex: 2, // Bottom of source
|
|
662
|
+
targetPointIndex: 0, // Top of target (simple default)
|
|
663
|
+
label: args.label,
|
|
664
|
+
style: style,
|
|
665
|
+
routingType: 'orthogonal', // Semantic diagrams usually look better with orthogonal
|
|
666
|
+
color: '#000000',
|
|
667
|
+
thickness: 0.01,
|
|
668
|
+
fontSize: 20,
|
|
669
|
+
borderStyle: borderStyle as any,
|
|
670
|
+
terminationStart: terminationStart as any,
|
|
671
|
+
terminationEnd: terminationEnd as any
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
state.edges.push(newEdge);
|
|
675
|
+
await saveDiagramFile(args.filePath, state);
|
|
676
|
+
return { content: [{ type: "text", text: `Connected '${args.sourceName}' to '${args.targetName}' via ${args.relationType}` }] };
|
|
677
|
+
}
|
|
437
678
|
default:
|
|
438
679
|
throw new Error(`Unknown tool: ${name}`);
|
|
439
680
|
}
|
package/src/types.ts
CHANGED
|
@@ -6,32 +6,46 @@ export interface NodeData {
|
|
|
6
6
|
z: number;
|
|
7
7
|
width: number;
|
|
8
8
|
height: number;
|
|
9
|
-
layerId: string;
|
|
10
9
|
label: string;
|
|
11
|
-
shape?: 'rectangle' | 'rounded' | 'ellipse' | 'diamond' | 'actor' | 'cylinder' | 'note' | 'cloud' | 'class' | 'text' | 'table';
|
|
12
|
-
|
|
10
|
+
shape?: 'rectangle' | 'rounded' | 'ellipse' | 'diamond' | 'actor' | 'cylinder' | 'note' | 'cloud' | 'class' | 'text' | 'table' | 'start-state' | 'end-state' | 'fork-join' | 'db-table';
|
|
11
|
+
layerId?: string;
|
|
12
|
+
backgroundColor?: string;
|
|
13
13
|
borderColor?: string;
|
|
14
14
|
borderWidth?: number;
|
|
15
15
|
borderStyle?: 'solid' | 'dashed' | 'dotted';
|
|
16
|
-
|
|
16
|
+
textColor?: string;
|
|
17
|
+
fontSize?: number;
|
|
17
18
|
opacity?: number;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
parentId?: string;
|
|
21
|
-
imageUrl?: string;
|
|
19
|
+
|
|
20
|
+
// Font styles
|
|
22
21
|
isBold?: boolean;
|
|
23
22
|
isItalic?: boolean;
|
|
24
23
|
isUnderline?: boolean;
|
|
25
24
|
isStrikethrough?: boolean;
|
|
26
25
|
isVerticalText?: boolean;
|
|
26
|
+
textAlignment?: 'left' | 'center' | 'right';
|
|
27
|
+
textAlignHorizontal?: 'left' | 'center' | 'right';
|
|
28
|
+
textVerticalAlignment?: 'top' | 'center' | 'bottom';
|
|
29
|
+
textAlignVertical?: 'top' | 'center' | 'bottom';
|
|
30
|
+
|
|
27
31
|
description?: string;
|
|
28
32
|
url?: string;
|
|
33
|
+
imageUrl?: string;
|
|
34
|
+
parentId?: string;
|
|
35
|
+
|
|
29
36
|
tableData?: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
rows: number;
|
|
38
|
+
columns: number;
|
|
39
|
+
hasRowHeader: boolean;
|
|
40
|
+
hasColumnHeader: boolean;
|
|
41
|
+
cells?: Record<string, string>;
|
|
42
|
+
};
|
|
43
|
+
classData?: {
|
|
44
|
+
methods: string[];
|
|
45
|
+
attributes: string[];
|
|
46
|
+
};
|
|
47
|
+
dbTableData?: {
|
|
48
|
+
columns: { name: string; type: string; isPk: boolean; isFk: boolean }[];
|
|
35
49
|
};
|
|
36
50
|
}
|
|
37
51
|
|
|
@@ -54,6 +68,8 @@ export interface EdgeData {
|
|
|
54
68
|
isItalic?: boolean;
|
|
55
69
|
isUnderline?: boolean;
|
|
56
70
|
isStrikethrough?: boolean;
|
|
71
|
+
terminationStart?: 'none' | 'arrow' | 'triangle-empty' | 'triangle-filled' | 'diamond-empty' | 'diamond-filled' | 'circle' | 'circle-plus' | 'crows-one' | 'crows-many' | 'crows-zero-one' | 'crows-zero-many';
|
|
72
|
+
terminationEnd?: 'none' | 'arrow' | 'triangle-empty' | 'triangle-filled' | 'diamond-empty' | 'diamond-filled' | 'circle' | 'circle-plus' | 'crows-one' | 'crows-many' | 'crows-zero-one' | 'crows-zero-many';
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
export interface LayerData {
|