ts-visio 1.0.0
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/LICENSE +21 -0
- package/README.md +343 -0
- package/dist/Layer.d.ts +12 -0
- package/dist/Layer.js +35 -0
- package/dist/Page.d.ts +30 -0
- package/dist/Page.js +169 -0
- package/dist/PageManager.d.ts +8 -0
- package/dist/PageManager.js +35 -0
- package/dist/SchemaDiagram.d.ts +22 -0
- package/dist/SchemaDiagram.js +36 -0
- package/dist/Shape.d.ts +68 -0
- package/dist/Shape.js +203 -0
- package/dist/ShapeModifier.d.ts +66 -0
- package/dist/ShapeModifier.js +889 -0
- package/dist/ShapeReader.d.ts +9 -0
- package/dist/ShapeReader.js +51 -0
- package/dist/VisioDocument.d.ts +21 -0
- package/dist/VisioDocument.js +119 -0
- package/dist/VisioPackage.d.ts +10 -0
- package/dist/VisioPackage.js +112 -0
- package/dist/core/MasterManager.d.ts +15 -0
- package/dist/core/MasterManager.js +43 -0
- package/dist/core/MediaConstants.d.ts +5 -0
- package/dist/core/MediaConstants.js +16 -0
- package/dist/core/MediaManager.d.ts +13 -0
- package/dist/core/MediaManager.js +88 -0
- package/dist/core/PageManager.d.ts +28 -0
- package/dist/core/PageManager.js +244 -0
- package/dist/core/RelsManager.d.ts +11 -0
- package/dist/core/RelsManager.js +81 -0
- package/dist/core/VisioConstants.d.ts +38 -0
- package/dist/core/VisioConstants.js +41 -0
- package/dist/core/VisioValidator.d.ts +27 -0
- package/dist/core/VisioValidator.js +362 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +32 -0
- package/dist/shapes/ConnectorBuilder.d.ts +37 -0
- package/dist/shapes/ConnectorBuilder.js +173 -0
- package/dist/shapes/ContainerBuilder.d.ts +6 -0
- package/dist/shapes/ContainerBuilder.js +103 -0
- package/dist/shapes/ForeignShapeBuilder.d.ts +4 -0
- package/dist/shapes/ForeignShapeBuilder.js +47 -0
- package/dist/shapes/ShapeBuilder.d.ts +4 -0
- package/dist/shapes/ShapeBuilder.js +68 -0
- package/dist/templates/MinimalVsdx.d.ts +10 -0
- package/dist/templates/MinimalVsdx.js +66 -0
- package/dist/types/VisioTypes.d.ts +85 -0
- package/dist/types/VisioTypes.js +14 -0
- package/dist/utils/StubHelpers.d.ts +7 -0
- package/dist/utils/StubHelpers.js +16 -0
- package/dist/utils/StyleHelpers.d.ts +30 -0
- package/dist/utils/StyleHelpers.js +95 -0
- package/dist/utils/VisioParsers.d.ts +6 -0
- package/dist/utils/VisioParsers.js +45 -0
- package/package.json +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Walt Zimmerman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# ts-visio
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> **Under Construction**
|
|
5
|
+
> This library is currently being developed with heavy assistance from AI and is primarily an experimental project. Use with caution.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
A Node.js library to strict-type interact with Visio (`.vsdx`) files.
|
|
9
|
+
Built using specific schema-level abstractions to handle the complex internal structure of Visio documents (ShapeSheets, Pages, Masters).
|
|
10
|
+
|
|
11
|
+
> **Status**: Work In Progress (TDD).
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Read VSDX**: Open and parse `.vsdx` files (zippped XML).
|
|
16
|
+
- **Strict Typing**: Interact with `VisioPage`, `VisioShape`, and `VisioConnect` objects.
|
|
17
|
+
- **ShapeSheet Access**: Read `Cells`, `Rows`, and `Sections` directly.
|
|
18
|
+
- **Connections**: Analyze connectivity between shapes.
|
|
19
|
+
- **Modular Architecture**: Use specialized components for loading, page management, shape reading, and modification.
|
|
20
|
+
- **Modify Content**: Update text content of shapes.
|
|
21
|
+
- **Create Shapes**: Add new rectangular shapes with text to pages.
|
|
22
|
+
- **Connect Shapes**: Create dynamic connectors between shapes.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install ts-visio
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### 1. Create or Load a Document
|
|
35
|
+
The `VisioDocument` class is the main entry point.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { VisioDocument } from 'ts-visio';
|
|
39
|
+
|
|
40
|
+
// Create a new blank document
|
|
41
|
+
const doc = await VisioDocument.create();
|
|
42
|
+
|
|
43
|
+
// OR Load an existing file
|
|
44
|
+
// const doc = await VisioDocument.load('diagram.vsdx');
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### 2. Access Pages
|
|
48
|
+
Access pages through the `pages` property.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const page = doc.pages[0];
|
|
52
|
+
console.log(`Editing Page: ${page.name}`);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### 3. Add & Modify Shapes
|
|
56
|
+
Add new shapes or modify existing ones without dealing with XML.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Add a new rectangle shape
|
|
60
|
+
const shape = await page.addShape({
|
|
61
|
+
text: "Hello World",
|
|
62
|
+
x: 1,
|
|
63
|
+
y: 1,
|
|
64
|
+
width: 3,
|
|
65
|
+
height: 1,
|
|
66
|
+
fillColor: "#ff0000", // Option hexadecimal fill color
|
|
67
|
+
fontColor: "#ffffff",
|
|
68
|
+
bold: true
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Modify text
|
|
72
|
+
await shape.setText("Updated Text");
|
|
73
|
+
|
|
74
|
+
console.log(`Shape ID: ${shape.id}`);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
#### 4. Groups & Nesting
|
|
80
|
+
Create Group shapes and add children to them.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// 1. Create a Group container
|
|
84
|
+
const group = await page.addShape({
|
|
85
|
+
text: "Group",
|
|
86
|
+
x: 5,
|
|
87
|
+
y: 5,
|
|
88
|
+
width: 4,
|
|
89
|
+
height: 4,
|
|
90
|
+
type: 'Group'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// 2. Add child shape directly to the group
|
|
94
|
+
// Coordinates are relative to the Group's bottom-left corner
|
|
95
|
+
await page.addShape({
|
|
96
|
+
text: "Child",
|
|
97
|
+
x: 1, // Relative X
|
|
98
|
+
y: 1, // Relative Y
|
|
99
|
+
width: 1,
|
|
100
|
+
height: 1
|
|
101
|
+
}, group.id);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
#### 6. Automatic Layout
|
|
106
|
+
Easily position shapes relative to each other.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const shape1 = await page.addShape({ text: "Step 1", x: 2, y: 5, width: 2, height: 1 });
|
|
110
|
+
const shape2 = await page.addShape({ text: "Step 2", x: 0, y: 0, width: 2, height: 1 }); // X,Y ignored if we place it next
|
|
111
|
+
|
|
112
|
+
// Place Shape 2 to the right of Shape 1 with a 1-inch gap
|
|
113
|
+
await shape2.placeRightOf(shape1, { gap: 1 });
|
|
114
|
+
|
|
115
|
+
// Chain placement
|
|
116
|
+
const shape3 = await page.addShape({ text: "Step 3", x: 0, y: 0, width: 2, height: 1 });
|
|
117
|
+
await shape3.placeRightOf(shape2);
|
|
118
|
+
|
|
119
|
+
// Vertical stacking
|
|
120
|
+
const shape4 = await page.addShape({ text: "Below", x: 0, y: 0, width: 2, height: 1 });
|
|
121
|
+
await shape4.placeBelow(shape1, { gap: 0.5 });
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### 7. Fluent API & Chaining
|
|
125
|
+
Combine creation, styling, and connection in a clean syntax.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const shape1 = await page.addShape({ text: "Start", x: 2, y: 4, width: 2, height: 1 });
|
|
129
|
+
const shape2 = await page.addShape({ text: "End", x: 6, y: 4, width: 2, height: 1 });
|
|
130
|
+
|
|
131
|
+
// Fluent Connection
|
|
132
|
+
await shape1.connectTo(shape2);
|
|
133
|
+
|
|
134
|
+
// Chaining Styles & Connections
|
|
135
|
+
await shape1.setStyle({ fillColor: '#00FF00' })
|
|
136
|
+
.connectTo(shape2);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### 5. Advanced Connections
|
|
140
|
+
Use specific arrowheads (Crow's Foot, etc.)
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { ArrowHeads } from 'ts-visio/utils/StyleHelpers';
|
|
144
|
+
|
|
145
|
+
await page.connectShapes(shape1, shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);
|
|
146
|
+
// OR
|
|
147
|
+
await shape1.connectTo(shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### 6. Database Tables
|
|
151
|
+
Create a compound stacked shape for database tables.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const tableShape = await page.addTable(
|
|
155
|
+
5,
|
|
156
|
+
5,
|
|
157
|
+
"Users",
|
|
158
|
+
["ID: int", "Name: varchar", "Email: varchar"]
|
|
159
|
+
);
|
|
160
|
+
console.log(tableShape.id); // Access ID
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### 8. Typed Schema Builder (Facade)
|
|
164
|
+
For ER diagrams, use the `SchemaDiagram` wrapper for simplified semantics.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
import { VisioDocument, SchemaDiagram } from 'ts-visio';
|
|
168
|
+
|
|
169
|
+
const doc = await VisioDocument.create();
|
|
170
|
+
const page = doc.pages[0];
|
|
171
|
+
const schema = new SchemaDiagram(page);
|
|
172
|
+
|
|
173
|
+
// Add Tables
|
|
174
|
+
const users = await schema.addTable('Users', ['id', 'email'], 0, 0);
|
|
175
|
+
const posts = await schema.addTable('Posts', ['id', 'user_id'], 5, 0);
|
|
176
|
+
|
|
177
|
+
// Add Relations (1:N maps to Crow's Foot arrow)
|
|
178
|
+
await schema.addRelation(users, posts, '1:N');
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### 9. Save the Document
|
|
182
|
+
Save the modified document back to disk.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
await doc.save('updated_diagram.vsdx');
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### 10. Using Stencils (Masters)
|
|
189
|
+
Load a template that already contains stencils (like "Network" or "Flowchart") and drop shapes by their Master ID.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// 1. Load a template with stencils
|
|
193
|
+
const doc = await VisioDocument.load('template_with_masters.vsdx');
|
|
194
|
+
const page = doc.pages[0];
|
|
195
|
+
|
|
196
|
+
// 2. Drop a shape using a Master ID
|
|
197
|
+
// (You can find IDs in visio/masters/masters.xml of the template)
|
|
198
|
+
await page.addShape({
|
|
199
|
+
text: "Router 1",
|
|
200
|
+
x: 2,
|
|
201
|
+
y: 2,
|
|
202
|
+
width: 1,
|
|
203
|
+
height: 1,
|
|
204
|
+
masterId: "5" // ID of the 'Router' master in the template
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// The library automatically handles the relationships so the file stays valid.
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### 11. Multi-Page Documents
|
|
211
|
+
Create multiple pages and organize your diagram across them.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const doc = await VisioDocument.create();
|
|
215
|
+
|
|
216
|
+
// Page 1 (Default)
|
|
217
|
+
const page1 = doc.pages[0];
|
|
218
|
+
await page1.addShape({ text: "Home", x: 1, y: 1 });
|
|
219
|
+
|
|
220
|
+
// Page 2 (New)
|
|
221
|
+
const page2 = await doc.addPage("Architecture Diagram");
|
|
222
|
+
await page2.addShape({ text: "Server", x: 4, y: 4 });
|
|
223
|
+
|
|
224
|
+
await doc.save("multipage.vsdx");
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### 12. Shape Data (Custom Properties)
|
|
228
|
+
Add metadata to shapes, which is crucial for "Smart" diagrams like visual databases or inventories.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const shape = await page.addShape({ text: "Server DB-01", x: 2, y: 2 });
|
|
232
|
+
|
|
233
|
+
// Synchronous Fluent API
|
|
234
|
+
shape.addData("IP", { value: "192.168.1.10", label: "IP Address" })
|
|
235
|
+
.addData("Status", { value: "Active" })
|
|
236
|
+
.addData("LastRefreshed", { value: new Date() }) // Auto-serialized as Visio Date
|
|
237
|
+
.addData("ConfigID", { value: 1024, hidden: true }); // Invisible to user
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### 13. Image Embedding
|
|
241
|
+
Embed PNG or JPEG images directly into the diagram.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import * as fs from 'fs';
|
|
245
|
+
|
|
246
|
+
const buffer = fs.readFileSync('logo.png');
|
|
247
|
+
const page = doc.pages[0];
|
|
248
|
+
|
|
249
|
+
// Add image at (x=2, y=5) with width=3, height=2
|
|
250
|
+
await page.addImage(buffer, 'logo.png', 2, 5, 3, 2);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### 14. Containers
|
|
254
|
+
Create visual grouping containers (Classic Visio style).
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// Create a Container
|
|
258
|
+
const container = await page.addContainer({
|
|
259
|
+
text: "Network Zone",
|
|
260
|
+
x: 1, y: 1,
|
|
261
|
+
width: 6, height: 4
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Add shapes "inside" the container area
|
|
265
|
+
// (Visio treats them as members if they are spatially within)
|
|
266
|
+
const shape = await page.addShape({ text: "Server A", x: 2, y: 2, width: 1, height: 1 });
|
|
267
|
+
|
|
268
|
+
// Explicitly link the shape to the container (so they move together)
|
|
269
|
+
await container.addMember(shape);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### 15. Lists (Stacking Containers)
|
|
273
|
+
Create ordered lists that automatically stack items either vertically (Tables) or horizontally (Timelines).
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// Vertical List (e.g. Database Table)
|
|
277
|
+
const list = await page.addList({
|
|
278
|
+
text: "Users",
|
|
279
|
+
x: 1, y: 10,
|
|
280
|
+
width: 3, height: 1
|
|
281
|
+
}, 'vertical'); // 'vertical' or 'horizontal'
|
|
282
|
+
|
|
283
|
+
// Items stack automatically
|
|
284
|
+
await list.addListItem(item1);
|
|
285
|
+
await list.addListItem(item2);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### 16. Interactivity & Navigation (Hyperlinks)
|
|
289
|
+
Add internal links (drill-downs) or external links (web references). These appear in the right-click menu in Visio.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// 1. External URL
|
|
293
|
+
await shape.toUrl('https://jira.com/123', 'Open Ticket');
|
|
294
|
+
|
|
295
|
+
// 2. Internal Page Link
|
|
296
|
+
const detailPage = await doc.addPage('Details');
|
|
297
|
+
await shape.toPage(detailPage, 'Go to Details');
|
|
298
|
+
|
|
299
|
+
// 3. Chainable
|
|
300
|
+
await shape.toUrl('https://google.com')
|
|
301
|
+
.toPage(detailPage);
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### 17. Layers
|
|
305
|
+
Organize complex diagrams with layers. Control visibility and locking programmatically.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// 1. Define Layers
|
|
309
|
+
const wireframe = await page.addLayer('Wireframe');
|
|
310
|
+
const annotations = await page.addLayer('Annotations');
|
|
311
|
+
|
|
312
|
+
// 2. Assign Shapes to Layers
|
|
313
|
+
await shape.addToLayer(wireframe);
|
|
314
|
+
await noteBox.addToLayer(annotations);
|
|
315
|
+
|
|
316
|
+
// 3. Toggle Visibility
|
|
317
|
+
await annotations.hide(); // Hide for presentation
|
|
318
|
+
await annotations.show(); // Show again
|
|
319
|
+
|
|
320
|
+
// 4. Lock Layer
|
|
321
|
+
await wireframe.setLocked(true);
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Examples
|
|
325
|
+
|
|
326
|
+
Check out the [examples](./examples) directory for complete scripts.
|
|
327
|
+
|
|
328
|
+
- **[Simple Schema](./examples/simple-schema.ts)**: Generates a database schema ERD with tables, styling, and Crow's Foot connectors.
|
|
329
|
+
- **[Network Topology](./examples/network-diagram.ts)**: Demonstrates the **Fluent Shape Data API** to build a network map with hidden metadata, typed properties, and connections.
|
|
330
|
+
- **[Containers Demo](./examples/containers_demo.ts)**: Shows how to create Classic Visio containers and place shapes within them.
|
|
331
|
+
- **[Lists Demo](./examples/lists_demo.ts)**: Demonstrates Vertical and Horizontal List stacks.
|
|
332
|
+
- **[Hyperlinks Demo](./examples/hyperlinks_demo.ts)**: Demonstrates Internal and External navigation.
|
|
333
|
+
- **[Layers Demo](./examples/layers_demo.ts)**: Shows how to create layers and toggle visibility.
|
|
334
|
+
- **[Image Embedding Demo](./examples/images_demo.ts)**: Demonstrates how to embed PNG or JPEG images into a diagram.
|
|
335
|
+
|
|
336
|
+
## Development
|
|
337
|
+
|
|
338
|
+
This project uses **Vitest** for testing.
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
npm install
|
|
342
|
+
npm test
|
|
343
|
+
```
|
package/dist/Layer.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { VisioPackage } from './VisioPackage';
|
|
2
|
+
export declare class Layer {
|
|
3
|
+
name: string;
|
|
4
|
+
index: number;
|
|
5
|
+
private pageId?;
|
|
6
|
+
private pkg?;
|
|
7
|
+
constructor(name: string, index: number, pageId?: string | undefined, pkg?: VisioPackage | undefined);
|
|
8
|
+
setVisible(visible: boolean): Promise<this>;
|
|
9
|
+
setLocked(locked: boolean): Promise<this>;
|
|
10
|
+
hide(): Promise<this>;
|
|
11
|
+
show(): Promise<this>;
|
|
12
|
+
}
|
package/dist/Layer.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Layer = void 0;
|
|
4
|
+
const ShapeModifier_1 = require("./ShapeModifier");
|
|
5
|
+
class Layer {
|
|
6
|
+
constructor(name, index, pageId, pkg) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.index = index;
|
|
9
|
+
this.pageId = pageId;
|
|
10
|
+
this.pkg = pkg;
|
|
11
|
+
}
|
|
12
|
+
async setVisible(visible) {
|
|
13
|
+
if (!this.pageId || !this.pkg) {
|
|
14
|
+
throw new Error('Layer was not created with page context. Cannot update properties.');
|
|
15
|
+
}
|
|
16
|
+
const modifier = new ShapeModifier_1.ShapeModifier(this.pkg);
|
|
17
|
+
await modifier.updateLayerProperty(this.pageId, this.index, 'Visible', visible ? '1' : '0');
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
async setLocked(locked) {
|
|
21
|
+
if (!this.pageId || !this.pkg) {
|
|
22
|
+
throw new Error('Layer was not created with page context. Cannot update properties.');
|
|
23
|
+
}
|
|
24
|
+
const modifier = new ShapeModifier_1.ShapeModifier(this.pkg);
|
|
25
|
+
await modifier.updateLayerProperty(this.pageId, this.index, 'Lock', locked ? '1' : '0');
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
async hide() {
|
|
29
|
+
return this.setVisible(false);
|
|
30
|
+
}
|
|
31
|
+
async show() {
|
|
32
|
+
return this.setVisible(true);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.Layer = Layer;
|
package/dist/Page.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { VisioPage } from './types/VisioTypes';
|
|
2
|
+
import { VisioPackage } from './VisioPackage';
|
|
3
|
+
import { ShapeModifier } from './ShapeModifier';
|
|
4
|
+
import { NewShapeProps } from './types/VisioTypes';
|
|
5
|
+
import { Shape } from './Shape';
|
|
6
|
+
import { MediaManager } from './core/MediaManager';
|
|
7
|
+
import { RelsManager } from './core/RelsManager';
|
|
8
|
+
import { Layer } from './Layer';
|
|
9
|
+
export declare class Page {
|
|
10
|
+
private internalPage;
|
|
11
|
+
private pkg;
|
|
12
|
+
private media;
|
|
13
|
+
private rels;
|
|
14
|
+
private modifier;
|
|
15
|
+
constructor(internalPage: VisioPage, pkg: VisioPackage, media?: MediaManager, rels?: RelsManager, modifier?: ShapeModifier);
|
|
16
|
+
get id(): string;
|
|
17
|
+
get name(): string;
|
|
18
|
+
getShapes(): Shape[];
|
|
19
|
+
addShape(props: NewShapeProps, parentId?: string): Promise<Shape>;
|
|
20
|
+
connectShapes(fromShape: Shape, toShape: Shape, beginArrow?: string, endArrow?: string): Promise<void>;
|
|
21
|
+
addImage(data: Buffer, name: string, x: number, y: number, width: number, height: number): Promise<Shape>;
|
|
22
|
+
addContainer(props: NewShapeProps): Promise<Shape>;
|
|
23
|
+
addList(props: NewShapeProps, direction?: 'vertical' | 'horizontal'): Promise<Shape>;
|
|
24
|
+
addTable(x: number, y: number, title: string, columns: string[]): Promise<Shape>;
|
|
25
|
+
addLayer(name: string, options?: {
|
|
26
|
+
visible?: boolean;
|
|
27
|
+
lock?: boolean;
|
|
28
|
+
print?: boolean;
|
|
29
|
+
}): Promise<Layer>;
|
|
30
|
+
}
|
package/dist/Page.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Page = void 0;
|
|
4
|
+
const ShapeReader_1 = require("./ShapeReader");
|
|
5
|
+
const ShapeModifier_1 = require("./ShapeModifier");
|
|
6
|
+
const Shape_1 = require("./Shape");
|
|
7
|
+
const MediaManager_1 = require("./core/MediaManager");
|
|
8
|
+
const RelsManager_1 = require("./core/RelsManager");
|
|
9
|
+
const StubHelpers_1 = require("./utils/StubHelpers");
|
|
10
|
+
const Layer_1 = require("./Layer");
|
|
11
|
+
class Page {
|
|
12
|
+
constructor(internalPage, pkg, media, rels, modifier) {
|
|
13
|
+
this.internalPage = internalPage;
|
|
14
|
+
this.pkg = pkg;
|
|
15
|
+
this.media = media || new MediaManager_1.MediaManager(pkg);
|
|
16
|
+
this.rels = rels || new RelsManager_1.RelsManager(pkg);
|
|
17
|
+
this.modifier = modifier || new ShapeModifier_1.ShapeModifier(pkg);
|
|
18
|
+
}
|
|
19
|
+
get id() {
|
|
20
|
+
return this.internalPage.ID;
|
|
21
|
+
}
|
|
22
|
+
get name() {
|
|
23
|
+
return this.internalPage.Name;
|
|
24
|
+
}
|
|
25
|
+
getShapes() {
|
|
26
|
+
const reader = new ShapeReader_1.ShapeReader(this.pkg);
|
|
27
|
+
// Assuming standard path mapping for now
|
|
28
|
+
const pagePath = `visio/pages/page${this.id}.xml`;
|
|
29
|
+
try {
|
|
30
|
+
const internalShapes = reader.readShapes(pagePath);
|
|
31
|
+
return internalShapes.map(s => new Shape_1.Shape(s, this.id, this.pkg));
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
// If page file doesn't exist or is empty, return empty array
|
|
35
|
+
console.warn(`Could not read shapes for page ${this.id}:`, e);
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async addShape(props, parentId) {
|
|
40
|
+
const newId = await this.modifier.addShape(this.id, props, parentId);
|
|
41
|
+
// Return a fresh Shape object representing the new shape
|
|
42
|
+
// We construct a minimal internal shape to satisfy the wrapper
|
|
43
|
+
// In a real scenario, we might want to re-read the shape from disk to get full defaults
|
|
44
|
+
const internalStub = (0, StubHelpers_1.createVisioShapeStub)({
|
|
45
|
+
ID: newId,
|
|
46
|
+
Text: props.text,
|
|
47
|
+
Cells: {
|
|
48
|
+
'Width': props.width,
|
|
49
|
+
'Height': props.height,
|
|
50
|
+
'PinX': props.x,
|
|
51
|
+
'PinY': props.y,
|
|
52
|
+
'LocPinX': props.width / 2,
|
|
53
|
+
'LocPinY': props.height / 2
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return new Shape_1.Shape(internalStub, this.id, this.pkg);
|
|
57
|
+
}
|
|
58
|
+
async connectShapes(fromShape, toShape, beginArrow, endArrow) {
|
|
59
|
+
await this.modifier.addConnector(this.id, fromShape.id, toShape.id, beginArrow, endArrow);
|
|
60
|
+
}
|
|
61
|
+
async addImage(data, name, x, y, width, height) {
|
|
62
|
+
// 1. Upload Media
|
|
63
|
+
const mediaPath = this.media.addMedia(name, data);
|
|
64
|
+
// 2. Link Page to Media
|
|
65
|
+
const rId = await this.rels.addPageImageRel(this.id, mediaPath);
|
|
66
|
+
// 3. Create Shape
|
|
67
|
+
const newId = await this.modifier.addShape(this.id, {
|
|
68
|
+
text: '',
|
|
69
|
+
x, y, width, height,
|
|
70
|
+
type: 'Foreign',
|
|
71
|
+
imgRelId: rId
|
|
72
|
+
});
|
|
73
|
+
const internalStub = (0, StubHelpers_1.createVisioShapeStub)({
|
|
74
|
+
ID: newId,
|
|
75
|
+
Text: '',
|
|
76
|
+
Cells: {
|
|
77
|
+
'Width': width,
|
|
78
|
+
'Height': height,
|
|
79
|
+
'PinX': x,
|
|
80
|
+
'PinY': y
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return new Shape_1.Shape(internalStub, this.id, this.pkg);
|
|
84
|
+
}
|
|
85
|
+
async addContainer(props) {
|
|
86
|
+
const newId = await this.modifier.addContainer(this.id, props);
|
|
87
|
+
const internalStub = (0, StubHelpers_1.createVisioShapeStub)({
|
|
88
|
+
ID: newId,
|
|
89
|
+
Text: props.text,
|
|
90
|
+
Cells: {
|
|
91
|
+
'Width': props.width,
|
|
92
|
+
'Height': props.height,
|
|
93
|
+
'PinX': props.x,
|
|
94
|
+
'PinY': props.y
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return new Shape_1.Shape(internalStub, this.id, this.pkg);
|
|
98
|
+
}
|
|
99
|
+
async addList(props, direction = 'vertical') {
|
|
100
|
+
const newId = await this.modifier.addList(this.id, props, direction);
|
|
101
|
+
const internalStub = (0, StubHelpers_1.createVisioShapeStub)({
|
|
102
|
+
ID: newId,
|
|
103
|
+
Text: props.text,
|
|
104
|
+
Cells: {
|
|
105
|
+
'Width': props.width,
|
|
106
|
+
'Height': props.height,
|
|
107
|
+
'PinX': props.x,
|
|
108
|
+
'PinY': props.y
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return new Shape_1.Shape(internalStub, this.id, this.pkg);
|
|
112
|
+
}
|
|
113
|
+
async addTable(x, y, title, columns) {
|
|
114
|
+
// ... (previous implementation)
|
|
115
|
+
// Dimensions
|
|
116
|
+
const width = 3;
|
|
117
|
+
const headerHeight = 0.5;
|
|
118
|
+
const lineItemHeight = 0.25;
|
|
119
|
+
const bodyHeight = Math.max(0.5, columns.length * lineItemHeight + 0.1); // Min height
|
|
120
|
+
const totalHeight = headerHeight + bodyHeight;
|
|
121
|
+
// 1. Create Main Group Shape (Transparent container)
|
|
122
|
+
// Group Logic:
|
|
123
|
+
// Positioned at (x, y) on the Page.
|
|
124
|
+
// Size encapsulates both header and body.
|
|
125
|
+
const groupShape = await this.addShape({
|
|
126
|
+
text: '', // No text on container
|
|
127
|
+
x: x,
|
|
128
|
+
y: y,
|
|
129
|
+
width: width,
|
|
130
|
+
height: totalHeight,
|
|
131
|
+
type: 'Group'
|
|
132
|
+
});
|
|
133
|
+
// 2. Header Shape (Inside Group)
|
|
134
|
+
// Coords relative to Group (Bottom-Left is 0,0)
|
|
135
|
+
// Header is at top. Center X is Width/2.
|
|
136
|
+
// Center Y = BodyHeight + (HeaderHeight/2)
|
|
137
|
+
const headerCenterY = bodyHeight + (headerHeight / 2);
|
|
138
|
+
await this.addShape({
|
|
139
|
+
text: title,
|
|
140
|
+
x: width / 2, // Relative PinX
|
|
141
|
+
y: headerCenterY, // Relative PinY
|
|
142
|
+
width: width,
|
|
143
|
+
height: headerHeight,
|
|
144
|
+
fillColor: '#DDDDDD',
|
|
145
|
+
bold: true
|
|
146
|
+
}, groupShape.id);
|
|
147
|
+
// 3. Body Shape (Inside Group)
|
|
148
|
+
// Bottom part. Center X is Width/2.
|
|
149
|
+
// Center Y = BodyHeight/2
|
|
150
|
+
const bodyCenterY = bodyHeight / 2;
|
|
151
|
+
const bodyText = columns.join('\n');
|
|
152
|
+
await this.addShape({
|
|
153
|
+
text: bodyText,
|
|
154
|
+
x: width / 2, // Relative PinX
|
|
155
|
+
y: bodyCenterY, // Relative PinY
|
|
156
|
+
width: width,
|
|
157
|
+
height: bodyHeight,
|
|
158
|
+
fillColor: '#FFFFFF',
|
|
159
|
+
fontColor: '#000000'
|
|
160
|
+
}, groupShape.id);
|
|
161
|
+
// Return the Group Shape
|
|
162
|
+
return groupShape;
|
|
163
|
+
}
|
|
164
|
+
async addLayer(name, options) {
|
|
165
|
+
const info = await this.modifier.addLayer(this.id, name, options);
|
|
166
|
+
return new Layer_1.Layer(info.name, info.index, this.id, this.pkg);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.Page = Page;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PageManager = void 0;
|
|
4
|
+
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
5
|
+
class PageManager {
|
|
6
|
+
constructor(pkg) {
|
|
7
|
+
this.pkg = pkg;
|
|
8
|
+
this.parser = new fast_xml_parser_1.XMLParser({
|
|
9
|
+
ignoreAttributes: false,
|
|
10
|
+
attributeNamePrefix: "@_"
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
getPages() {
|
|
14
|
+
let content;
|
|
15
|
+
try {
|
|
16
|
+
content = this.pkg.getFileText('visio/pages/pages.xml');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
const parsed = this.parser.parse(content);
|
|
22
|
+
const pagesData = parsed.Pages?.Page;
|
|
23
|
+
if (!pagesData)
|
|
24
|
+
return [];
|
|
25
|
+
const pagesArray = Array.isArray(pagesData) ? pagesData : [pagesData];
|
|
26
|
+
return pagesArray.map((p) => ({
|
|
27
|
+
ID: p['@_ID'],
|
|
28
|
+
Name: p['@_Name'],
|
|
29
|
+
NameU: p['@_NameU'],
|
|
30
|
+
Shapes: [],
|
|
31
|
+
Connects: []
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.PageManager = PageManager;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Page } from './Page';
|
|
2
|
+
import { Shape } from './Shape';
|
|
3
|
+
export type RelationType = '1:1' | '1:N';
|
|
4
|
+
export declare class SchemaDiagram {
|
|
5
|
+
private page;
|
|
6
|
+
constructor(page: Page);
|
|
7
|
+
/**
|
|
8
|
+
* Adds a table entity to the diagram.
|
|
9
|
+
* @param tableName Name of the table
|
|
10
|
+
* @param columns List of column names (e.g., "id: int")
|
|
11
|
+
* @param x X coordinate
|
|
12
|
+
* @param y Y coordinate
|
|
13
|
+
*/
|
|
14
|
+
addTable(tableName: string, columns: string[], x?: number, y?: number): Promise<Shape>;
|
|
15
|
+
/**
|
|
16
|
+
* Connects two tables with a specific relationship type.
|
|
17
|
+
* @param fromTable Source table shape
|
|
18
|
+
* @param toTable Target table shape
|
|
19
|
+
* @param type Relationship type ('1:1' or '1:N')
|
|
20
|
+
*/
|
|
21
|
+
addRelation(fromTable: Shape, toTable: Shape, type: RelationType): Promise<void>;
|
|
22
|
+
}
|