ts-visio
v1.0.2
Published
A npm package for creating Visio files (vsdx) using TypeScript.
Readme
ts-visio
[!WARNING] Under Construction This library is currently being developed with heavy assistance from AI and is primarily an experimental project. Use with caution.
A Node.js library to strict-type interact with Visio (.vsdx) files.
Built using specific schema-level abstractions to handle the complex internal structure of Visio documents (ShapeSheets, Pages, Masters).
Status: Work In Progress (TDD).
Features
- Read VSDX: Open and parse
.vsdxfiles (zippped XML). - Strict Typing: Interact with
VisioPage,VisioShape, andVisioConnectobjects. - ShapeSheet Access: Read
Cells,Rows, andSectionsdirectly. - Connections: Analyze connectivity between shapes.
- Modular Architecture: Use specialized components for loading, page management, shape reading, and modification.
- Modify Content: Update text content of shapes.
- Create Shapes: Add new rectangular shapes with text to pages.
- Connect Shapes: Create dynamic connectors between shapes.
Installation
npm install ts-visioUsage
1. Create or Load a Document
The VisioDocument class is the main entry point.
import { VisioDocument } from 'ts-visio';
// Create a new blank document
const doc = await VisioDocument.create();
// OR Load an existing file
// const doc = await VisioDocument.load('diagram.vsdx');2. Access Pages
Access pages through the pages property.
const page = doc.pages[0];
console.log(`Editing Page: ${page.name}`);3. Add & Modify Shapes
Add new shapes or modify existing ones without dealing with XML.
// Add a new rectangle shape
const shape = await page.addShape({
text: "Hello World",
x: 1,
y: 1,
width: 3,
height: 1,
fillColor: "#ff0000", // Option hexadecimal fill color
fontColor: "#ffffff",
bold: true
});
// Modify text
await shape.setText("Updated Text");
console.log(`Shape ID: ${shape.id}`);4. Groups & Nesting
Create Group shapes and add children to them.
// 1. Create a Group container
const group = await page.addShape({
text: "Group",
x: 5,
y: 5,
width: 4,
height: 4,
type: 'Group'
});
// 2. Add child shape directly to the group
// Coordinates are relative to the Group's bottom-left corner
await page.addShape({
text: "Child",
x: 1, // Relative X
y: 1, // Relative Y
width: 1,
height: 1
}, group.id);6. Automatic Layout
Easily position shapes relative to each other.
const shape1 = await page.addShape({ text: "Step 1", x: 2, y: 5, width: 2, height: 1 });
const shape2 = await page.addShape({ text: "Step 2", x: 0, y: 0, width: 2, height: 1 }); // X,Y ignored if we place it next
// Place Shape 2 to the right of Shape 1 with a 1-inch gap
await shape2.placeRightOf(shape1, { gap: 1 });
// Chain placement
const shape3 = await page.addShape({ text: "Step 3", x: 0, y: 0, width: 2, height: 1 });
await shape3.placeRightOf(shape2);
// Vertical stacking
const shape4 = await page.addShape({ text: "Below", x: 0, y: 0, width: 2, height: 1 });
await shape4.placeBelow(shape1, { gap: 0.5 });7. Fluent API & Chaining
Combine creation, styling, and connection in a clean syntax.
const shape1 = await page.addShape({ text: "Start", x: 2, y: 4, width: 2, height: 1 });
const shape2 = await page.addShape({ text: "End", x: 6, y: 4, width: 2, height: 1 });
// Fluent Connection
await shape1.connectTo(shape2);
// Chaining Styles & Connections
await shape1.setStyle({ fillColor: '#00FF00' })
.connectTo(shape2);5. Advanced Connections
Use specific arrowheads (Crow's Foot, etc.)
import { ArrowHeads } from 'ts-visio/utils/StyleHelpers';
await page.connectShapes(shape1, shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);
// OR
await shape1.connectTo(shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);6. Database Tables
Create a compound stacked shape for database tables.
const tableShape = await page.addTable(
5,
5,
"Users",
["ID: int", "Name: varchar", "Email: varchar"]
);
console.log(tableShape.id); // Access ID8. Typed Schema Builder (Facade)
For ER diagrams, use the SchemaDiagram wrapper for simplified semantics.
import { VisioDocument, SchemaDiagram } from 'ts-visio';
const doc = await VisioDocument.create();
const page = doc.pages[0];
const schema = new SchemaDiagram(page);
// Add Tables
const users = await schema.addTable('Users', ['id', 'email'], 0, 0);
const posts = await schema.addTable('Posts', ['id', 'user_id'], 5, 0);
// Add Relations (1:N maps to Crow's Foot arrow)
await schema.addRelation(users, posts, '1:N');9. Save the Document
Save the modified document back to disk.
await doc.save('updated_diagram.vsdx');10. Using Stencils (Masters)
Load a template that already contains stencils (like "Network" or "Flowchart") and drop shapes by their Master ID.
// 1. Load a template with stencils
const doc = await VisioDocument.load('template_with_masters.vsdx');
const page = doc.pages[0];
// 2. Drop a shape using a Master ID
// (You can find IDs in visio/masters/masters.xml of the template)
await page.addShape({
text: "Router 1",
x: 2,
y: 2,
width: 1,
height: 1,
masterId: "5" // ID of the 'Router' master in the template
});
// The library automatically handles the relationships so the file stays valid.11. Multi-Page Documents
Create multiple pages and organize your diagram across them.
const doc = await VisioDocument.create();
// Page 1 (Default)
const page1 = doc.pages[0];
await page1.addShape({ text: "Home", x: 1, y: 1 });
// Page 2 (New)
const page2 = await doc.addPage("Architecture Diagram");
await page2.addShape({ text: "Server", x: 4, y: 4 });
await doc.save("multipage.vsdx");12. Shape Data (Custom Properties)
Add metadata to shapes, which is crucial for "Smart" diagrams like visual databases or inventories.
const shape = await page.addShape({ text: "Server DB-01", x: 2, y: 2 });
// Synchronous Fluent API
shape.addData("IP", { value: "192.168.1.10", label: "IP Address" })
.addData("Status", { value: "Active" })
.addData("LastRefreshed", { value: new Date() }) // Auto-serialized as Visio Date
.addData("ConfigID", { value: 1024, hidden: true }); // Invisible to user13. Image Embedding
Embed PNG or JPEG images directly into the diagram.
import * as fs from 'fs';
const buffer = fs.readFileSync('logo.png');
const page = doc.pages[0];
// Add image at (x=2, y=5) with width=3, height=2
await page.addImage(buffer, 'logo.png', 2, 5, 3, 2);14. Containers
Create visual grouping containers (Classic Visio style).
// Create a Container
const container = await page.addContainer({
text: "Network Zone",
x: 1, y: 1,
width: 6, height: 4
});
// Add shapes "inside" the container area
// (Visio treats them as members if they are spatially within)
const shape = await page.addShape({ text: "Server A", x: 2, y: 2, width: 1, height: 1 });
// Explicitly link the shape to the container (so they move together)
await container.addMember(shape);15. Lists (Stacking Containers)
Create ordered lists that automatically stack items either vertically (Tables) or horizontally (Timelines).
// Vertical List (e.g. Database Table)
const list = await page.addList({
text: "Users",
x: 1, y: 10,
width: 3, height: 1
}, 'vertical'); // 'vertical' or 'horizontal'
// Items stack automatically
await list.addListItem(item1);
await list.addListItem(item2);16. Interactivity & Navigation (Hyperlinks)
Add internal links (drill-downs) or external links (web references). These appear in the right-click menu in Visio.
// 1. External URL
await shape.toUrl('https://jira.com/123', 'Open Ticket');
// 2. Internal Page Link
const detailPage = await doc.addPage('Details');
await shape.toPage(detailPage, 'Go to Details');
// 3. Chainable
await shape.toUrl('https://google.com')
.toPage(detailPage);17. Layers
Organize complex diagrams with layers. Control visibility and locking programmatically.
// 1. Define Layers
const wireframe = await page.addLayer('Wireframe');
const annotations = await page.addLayer('Annotations');
// 2. Assign Shapes to Layers
await shape.addToLayer(wireframe);
await noteBox.addToLayer(annotations);
// 3. Toggle Visibility
await annotations.hide(); // Hide for presentation
await annotations.show(); // Show again
// 4. Lock Layer
await wireframe.setLocked(true);18. Cross-Functional Flowcharts (Swimlanes)
Create structured Swimlane diagrams involving Pools and Lanes.
// 1. Create a Pool (Vertical List)
const pool = await page.addSwimlanePool({
text: "User Registration",
x: 5, y: 5, width: 10, height: 6
});
// 2. Create Lanes (Containers)
const lane1 = await page.addSwimlaneLane({ text: "Client", width: 10, height: 2 });
const lane2 = await page.addSwimlaneLane({ text: "Server", width: 10, height: 2 });
// 3. Add Lanes to Pool (Order matters)
await pool.addListItem(lane1);
await pool.addListItem(lane2);
// 4. Group Shapes into Lanes
// This binds their movement so they stay inside the lane
await lane1.addMember(startShape);
await lane2.addMember(serverShape);Examples
Check out the examples directory for complete scripts.
- Simple Schema: Generates a database schema ERD with tables, styling, and Crow's Foot connectors.
- Network Topology: Demonstrates the Fluent Shape Data API to build a network map with hidden metadata, typed properties, and connections.
- Containers Demo: Shows how to create Classic Visio containers and place shapes within them.
- Lists Demo: Demonstrates Vertical and Horizontal List stacks.
- Hyperlinks Demo: Demonstrates Internal and External navigation.
- Layers Demo: Shows how to create layers and toggle visibility.
- Image Embedding Demo: Demonstrates how to embed PNG or JPEG images into a diagram.
- Swimlane Demo: Demonstrates creating Cross-Functional Flowcharts with Pools and Lanes.
Development
This project uses Vitest for testing.
npm install
npm test