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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/dist/Layer.d.ts +12 -0
  4. package/dist/Layer.js +35 -0
  5. package/dist/Page.d.ts +30 -0
  6. package/dist/Page.js +169 -0
  7. package/dist/PageManager.d.ts +8 -0
  8. package/dist/PageManager.js +35 -0
  9. package/dist/SchemaDiagram.d.ts +22 -0
  10. package/dist/SchemaDiagram.js +36 -0
  11. package/dist/Shape.d.ts +68 -0
  12. package/dist/Shape.js +203 -0
  13. package/dist/ShapeModifier.d.ts +66 -0
  14. package/dist/ShapeModifier.js +889 -0
  15. package/dist/ShapeReader.d.ts +9 -0
  16. package/dist/ShapeReader.js +51 -0
  17. package/dist/VisioDocument.d.ts +21 -0
  18. package/dist/VisioDocument.js +119 -0
  19. package/dist/VisioPackage.d.ts +10 -0
  20. package/dist/VisioPackage.js +112 -0
  21. package/dist/core/MasterManager.d.ts +15 -0
  22. package/dist/core/MasterManager.js +43 -0
  23. package/dist/core/MediaConstants.d.ts +5 -0
  24. package/dist/core/MediaConstants.js +16 -0
  25. package/dist/core/MediaManager.d.ts +13 -0
  26. package/dist/core/MediaManager.js +88 -0
  27. package/dist/core/PageManager.d.ts +28 -0
  28. package/dist/core/PageManager.js +244 -0
  29. package/dist/core/RelsManager.d.ts +11 -0
  30. package/dist/core/RelsManager.js +81 -0
  31. package/dist/core/VisioConstants.d.ts +38 -0
  32. package/dist/core/VisioConstants.js +41 -0
  33. package/dist/core/VisioValidator.d.ts +27 -0
  34. package/dist/core/VisioValidator.js +362 -0
  35. package/dist/index.d.ts +8 -0
  36. package/dist/index.js +32 -0
  37. package/dist/shapes/ConnectorBuilder.d.ts +37 -0
  38. package/dist/shapes/ConnectorBuilder.js +173 -0
  39. package/dist/shapes/ContainerBuilder.d.ts +6 -0
  40. package/dist/shapes/ContainerBuilder.js +103 -0
  41. package/dist/shapes/ForeignShapeBuilder.d.ts +4 -0
  42. package/dist/shapes/ForeignShapeBuilder.js +47 -0
  43. package/dist/shapes/ShapeBuilder.d.ts +4 -0
  44. package/dist/shapes/ShapeBuilder.js +68 -0
  45. package/dist/templates/MinimalVsdx.d.ts +10 -0
  46. package/dist/templates/MinimalVsdx.js +66 -0
  47. package/dist/types/VisioTypes.d.ts +85 -0
  48. package/dist/types/VisioTypes.js +14 -0
  49. package/dist/utils/StubHelpers.d.ts +7 -0
  50. package/dist/utils/StubHelpers.js +16 -0
  51. package/dist/utils/StyleHelpers.d.ts +30 -0
  52. package/dist/utils/StyleHelpers.js +95 -0
  53. package/dist/utils/VisioParsers.d.ts +6 -0
  54. package/dist/utils/VisioParsers.js +45 -0
  55. 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
+ ```
@@ -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,8 @@
1
+ import { VisioPackage } from './VisioPackage';
2
+ import { VisioPage } from './types/VisioTypes';
3
+ export declare class PageManager {
4
+ private pkg;
5
+ private parser;
6
+ constructor(pkg: VisioPackage);
7
+ getPages(): VisioPage[];
8
+ }
@@ -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
+ }