ts-visio 1.9.0 → 1.10.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/README.md CHANGED
@@ -27,6 +27,7 @@ Built using specific schema-level abstractions to handle the complex internal st
27
27
  - **Document Metadata**: Read and write document properties (title, author, description, keywords, company, dates) via `doc.getMetadata()` / `doc.setMetadata()`.
28
28
  - **Named Connection Points**: Define specific ports on shapes (`Top`, `Right`, etc.) and connect to them precisely using `fromPort`/`toPort` on any connector API.
29
29
  - **StyleSheets**: Create document-level named styles with fill, line, and text properties via `doc.createStyle()` and apply them to shapes at creation time (`styleId`) or post-creation (`shape.applyStyle()`).
30
+ - **Color Palette**: Register named colors in the document's color table via `doc.addColor()` and look them up by index or hex value with `doc.getColors()` / `doc.getColorIndex()`.
30
31
 
31
32
  Feature gaps are being tracked in [FEATURES.md](./FEATURES.md).
32
33
 
@@ -153,7 +154,7 @@ await shape1.setStyle({ fillColor: '#00FF00' })
153
154
  Use specific arrowheads (Crow's Foot, etc.)
154
155
 
155
156
  ```typescript
156
- import { ArrowHeads } from 'ts-visio/utils/StyleHelpers';
157
+ import { ArrowHeads } from 'ts-visio';
157
158
 
158
159
  await page.connectShapes(shape1, shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);
159
160
  // OR
@@ -449,7 +450,7 @@ Supported values: `'rectangle'` (default), `'ellipse'`, `'diamond'`, `'rounded-r
449
450
  Control line appearance and routing algorithm on any connector.
450
451
 
451
452
  ```typescript
452
- import { ArrowHeads } from 'ts-visio/utils/StyleHelpers';
453
+ import { ArrowHeads } from 'ts-visio';
453
454
 
454
455
  // Styled connector with crow's foot arrow and custom line
455
456
  await shape1.connectTo(shape2, ArrowHeads.One, ArrowHeads.CrowsFoot, {
@@ -692,6 +693,39 @@ console.log(page.getConnectors().length); // 0
692
693
  console.log(page.getShapes().length); // shapes still exist
693
694
  ```
694
695
 
696
+ #### 31. Color Palette
697
+ Register colors in the document's indexed color table (`<Colors>` in `document.xml`). Colors are identified by an integer IX that you can reference anywhere a hex color is accepted.
698
+
699
+ ```typescript
700
+ // Register colors — returns the integer index (IX)
701
+ const blueIx = doc.addColor('#4472C4'); // → 2 (first user color)
702
+ const redIx = doc.addColor('#FF0000'); // → 3
703
+ const greenIx = doc.addColor('#00B050'); // → 4
704
+
705
+ // De-duplication: adding the same color returns the existing index
706
+ doc.addColor('#4472C4'); // → 2 (no duplicate created)
707
+
708
+ // Shorthand, missing #, and case variations are all normalised
709
+ doc.addColor('#FFF'); // same as #FFFFFF → returns 1 (built-in white)
710
+ doc.addColor('4472c4'); // same as #4472C4 → returns 2
711
+
712
+ // Read the full palette (sorted by index)
713
+ const palette = doc.getColors();
714
+ // [
715
+ // { index: 0, rgb: '#000000' }, // built-in black
716
+ // { index: 1, rgb: '#FFFFFF' }, // built-in white
717
+ // { index: 2, rgb: '#4472C4' },
718
+ // { index: 3, rgb: '#FF0000' },
719
+ // { index: 4, rgb: '#00B050' },
720
+ // ]
721
+
722
+ // Look up an index by hex value
723
+ doc.getColorIndex('#4472C4'); // → 2
724
+ doc.getColorIndex('#123456'); // → undefined (not registered)
725
+ ```
726
+
727
+ Built-in colors: IX 0 = `#000000` (black), IX 1 = `#FFFFFF` (white). User colors start at IX 2 and increment sequentially.
728
+
695
729
  ---
696
730
 
697
731
  ## Examples
@@ -1,5 +1,5 @@
1
1
  import { Page } from './Page';
2
- import { DocumentMetadata, StyleProps, StyleRecord } from './types/VisioTypes';
2
+ import { DocumentMetadata, StyleProps, StyleRecord, ColorEntry } from './types/VisioTypes';
3
3
  export declare class VisioDocument {
4
4
  private pkg;
5
5
  private pageManager;
@@ -7,6 +7,7 @@ export declare class VisioDocument {
7
7
  private mediaManager;
8
8
  private metadataManager;
9
9
  private styleSheetManager;
10
+ private colorManager;
10
11
  private constructor();
11
12
  static create(): Promise<VisioDocument>;
12
13
  static load(pathOrBuffer: string | Buffer | ArrayBuffer | Uint8Array): Promise<VisioDocument>;
@@ -56,5 +57,43 @@ export declare class VisioDocument {
56
57
  * Return all stylesheets defined in the document (including built-in styles).
57
58
  */
58
59
  getStyles(): StyleRecord[];
60
+ /**
61
+ * Add a color to the document-level color palette and return its integer index (IX).
62
+ *
63
+ * If the color is already registered the existing index is returned without
64
+ * creating a duplicate. The two built-in colors are always present:
65
+ * - index 0 → `#000000` (black)
66
+ * - index 1 → `#FFFFFF` (white)
67
+ *
68
+ * User colors receive indices starting at 2.
69
+ *
70
+ * @param hex CSS hex string — `'#4472C4'`, `'#ABC'`, or `'4472c4'` are all accepted.
71
+ * @returns Integer IX that uniquely identifies this color in the palette.
72
+ *
73
+ * @example
74
+ * const blueIx = doc.addColor('#4472C4'); // → 2
75
+ * const redIx = doc.addColor('#FF0000'); // → 3
76
+ * doc.addColor('#4472C4'); // → 2 (de-duplicated)
77
+ */
78
+ addColor(hex: string): number;
79
+ /**
80
+ * Return all color entries in the document palette, ordered by index.
81
+ *
82
+ * @example
83
+ * doc.addColor('#4472C4');
84
+ * doc.getColors();
85
+ * // → [{ index: 0, rgb: '#000000' }, { index: 1, rgb: '#FFFFFF' }, { index: 2, rgb: '#4472C4' }]
86
+ */
87
+ getColors(): ColorEntry[];
88
+ /**
89
+ * Look up the palette index of a color by its hex value.
90
+ * Returns `undefined` if the color has not been added to the palette.
91
+ *
92
+ * @example
93
+ * doc.addColor('#4472C4');
94
+ * doc.getColorIndex('#4472C4'); // → 2
95
+ * doc.getColorIndex('#FF0000'); // → undefined
96
+ */
97
+ getColorIndex(hex: string): number | undefined;
59
98
  save(filename?: string): Promise<Buffer>;
60
99
  }
@@ -40,6 +40,7 @@ const Page_1 = require("./Page");
40
40
  const MediaManager_1 = require("./core/MediaManager");
41
41
  const MetadataManager_1 = require("./core/MetadataManager");
42
42
  const StyleSheetManager_1 = require("./core/StyleSheetManager");
43
+ const ColorManager_1 = require("./core/ColorManager");
43
44
  class VisioDocument {
44
45
  constructor(pkg) {
45
46
  this.pkg = pkg;
@@ -48,6 +49,7 @@ class VisioDocument {
48
49
  this.mediaManager = new MediaManager_1.MediaManager(pkg);
49
50
  this.metadataManager = new MetadataManager_1.MetadataManager(pkg);
50
51
  this.styleSheetManager = new StyleSheetManager_1.StyleSheetManager(pkg);
52
+ this.colorManager = new ColorManager_1.ColorManager(pkg);
51
53
  }
52
54
  static async create() {
53
55
  const pkg = await VisioPackage_1.VisioPackage.create();
@@ -169,6 +171,50 @@ class VisioDocument {
169
171
  getStyles() {
170
172
  return this.styleSheetManager.getStyles();
171
173
  }
174
+ /**
175
+ * Add a color to the document-level color palette and return its integer index (IX).
176
+ *
177
+ * If the color is already registered the existing index is returned without
178
+ * creating a duplicate. The two built-in colors are always present:
179
+ * - index 0 → `#000000` (black)
180
+ * - index 1 → `#FFFFFF` (white)
181
+ *
182
+ * User colors receive indices starting at 2.
183
+ *
184
+ * @param hex CSS hex string — `'#4472C4'`, `'#ABC'`, or `'4472c4'` are all accepted.
185
+ * @returns Integer IX that uniquely identifies this color in the palette.
186
+ *
187
+ * @example
188
+ * const blueIx = doc.addColor('#4472C4'); // → 2
189
+ * const redIx = doc.addColor('#FF0000'); // → 3
190
+ * doc.addColor('#4472C4'); // → 2 (de-duplicated)
191
+ */
192
+ addColor(hex) {
193
+ return this.colorManager.addColor(hex);
194
+ }
195
+ /**
196
+ * Return all color entries in the document palette, ordered by index.
197
+ *
198
+ * @example
199
+ * doc.addColor('#4472C4');
200
+ * doc.getColors();
201
+ * // → [{ index: 0, rgb: '#000000' }, { index: 1, rgb: '#FFFFFF' }, { index: 2, rgb: '#4472C4' }]
202
+ */
203
+ getColors() {
204
+ return this.colorManager.getColors();
205
+ }
206
+ /**
207
+ * Look up the palette index of a color by its hex value.
208
+ * Returns `undefined` if the color has not been added to the palette.
209
+ *
210
+ * @example
211
+ * doc.addColor('#4472C4');
212
+ * doc.getColorIndex('#4472C4'); // → 2
213
+ * doc.getColorIndex('#FF0000'); // → undefined
214
+ */
215
+ getColorIndex(hex) {
216
+ return this.colorManager.getColorIndex(hex);
217
+ }
172
218
  async save(filename) {
173
219
  return this.pkg.save(filename);
174
220
  }
@@ -0,0 +1,57 @@
1
+ import { VisioPackage } from '../VisioPackage';
2
+ import { ColorEntry } from '../types/VisioTypes';
3
+ /**
4
+ * Manages the document-level color palette stored in `<Colors>` inside
5
+ * `visio/document.xml`.
6
+ *
7
+ * The palette is a simple indexed table — Visio identifies colors by their
8
+ * zero-based integer index (IX) as well as by their hex RGB value.
9
+ *
10
+ * Built-in entries (always present):
11
+ * - IX 0 → #000000 (black)
12
+ * - IX 1 → #FFFFFF (white)
13
+ *
14
+ * User-added colors receive sequential indices starting at 2.
15
+ */
16
+ export declare class ColorManager {
17
+ private pkg;
18
+ private parser;
19
+ private builder;
20
+ constructor(pkg: VisioPackage);
21
+ /**
22
+ * Add a color to the document palette and return its integer index (IX).
23
+ *
24
+ * If the color is already in the palette the existing index is returned
25
+ * without creating a duplicate. Built-in colors (black = 0, white = 1)
26
+ * are returned directly.
27
+ *
28
+ * @param hex CSS hex string — `'#4472C4'`, `'#abc'`, `'4472c4'` all accepted.
29
+ * @returns Integer IX that can be used as a color reference.
30
+ *
31
+ * @example
32
+ * const ix = doc.addColor('#4472C4'); // → 2 (first user color)
33
+ * await page.addShape({ text: 'Hi', fillColor: '#4472C4', ... });
34
+ */
35
+ addColor(hex: string): number;
36
+ /**
37
+ * Return all color entries currently in the document palette,
38
+ * ordered by index ascending.
39
+ */
40
+ getColors(): ColorEntry[];
41
+ /**
42
+ * Look up the index of a color in the palette by its hex value.
43
+ * Returns `undefined` if the color has not been registered.
44
+ *
45
+ * @example
46
+ * const ix = doc.getColorIndex('#4472C4'); // 2, or undefined if not added
47
+ */
48
+ getColorIndex(hex: string): number | undefined;
49
+ private getParsedDoc;
50
+ private saveParsedDoc;
51
+ /** Ensure `<Colors>` exists and contains the two built-in entries. */
52
+ private ensureColors;
53
+ /** Next IX = max existing index + 1, always at least 2. */
54
+ private nextIndex;
55
+ /** Minimal built-in palette returned when <Colors> is absent. */
56
+ private builtIns;
57
+ }
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColorManager = void 0;
4
+ const XmlHelper_1 = require("../utils/XmlHelper");
5
+ /**
6
+ * Manages the document-level color palette stored in `<Colors>` inside
7
+ * `visio/document.xml`.
8
+ *
9
+ * The palette is a simple indexed table — Visio identifies colors by their
10
+ * zero-based integer index (IX) as well as by their hex RGB value.
11
+ *
12
+ * Built-in entries (always present):
13
+ * - IX 0 → #000000 (black)
14
+ * - IX 1 → #FFFFFF (white)
15
+ *
16
+ * User-added colors receive sequential indices starting at 2.
17
+ */
18
+ class ColorManager {
19
+ constructor(pkg) {
20
+ this.pkg = pkg;
21
+ this.parser = (0, XmlHelper_1.createXmlParser)();
22
+ this.builder = (0, XmlHelper_1.createXmlBuilder)();
23
+ }
24
+ // ── Public API ────────────────────────────────────────────────────────────
25
+ /**
26
+ * Add a color to the document palette and return its integer index (IX).
27
+ *
28
+ * If the color is already in the palette the existing index is returned
29
+ * without creating a duplicate. Built-in colors (black = 0, white = 1)
30
+ * are returned directly.
31
+ *
32
+ * @param hex CSS hex string — `'#4472C4'`, `'#abc'`, `'4472c4'` all accepted.
33
+ * @returns Integer IX that can be used as a color reference.
34
+ *
35
+ * @example
36
+ * const ix = doc.addColor('#4472C4'); // → 2 (first user color)
37
+ * await page.addShape({ text: 'Hi', fillColor: '#4472C4', ... });
38
+ */
39
+ addColor(hex) {
40
+ const normalized = normalizeHex(hex);
41
+ const parsed = this.getParsedDoc();
42
+ const doc = parsed['VisioDocument'];
43
+ this.ensureColors(doc);
44
+ const colors = doc['Colors'];
45
+ const entries = toArray(colors['ColorEntry']);
46
+ // De-duplicate: return existing index if the color is already registered
47
+ const existing = entries.find((e) => normalizeHex(e['@_RGB']) === normalized);
48
+ if (existing)
49
+ return parseInt(existing['@_IX'], 10);
50
+ // Append at the next sequential index
51
+ const nextIX = this.nextIndex(entries);
52
+ entries.push({ '@_IX': nextIX.toString(), '@_RGB': normalized });
53
+ colors['ColorEntry'] = entries;
54
+ this.saveParsedDoc(parsed);
55
+ return nextIX;
56
+ }
57
+ /**
58
+ * Return all color entries currently in the document palette,
59
+ * ordered by index ascending.
60
+ */
61
+ getColors() {
62
+ const parsed = this.getParsedDoc();
63
+ const doc = parsed['VisioDocument'];
64
+ const colors = doc?.['Colors'];
65
+ if (!colors)
66
+ return this.builtIns();
67
+ return toArray(colors['ColorEntry'])
68
+ .map((e) => ({
69
+ index: parseInt(e['@_IX'], 10),
70
+ rgb: normalizeHex(e['@_RGB']),
71
+ }))
72
+ .sort((a, b) => a.index - b.index);
73
+ }
74
+ /**
75
+ * Look up the index of a color in the palette by its hex value.
76
+ * Returns `undefined` if the color has not been registered.
77
+ *
78
+ * @example
79
+ * const ix = doc.getColorIndex('#4472C4'); // 2, or undefined if not added
80
+ */
81
+ getColorIndex(hex) {
82
+ const normalized = normalizeHex(hex);
83
+ return this.getColors().find(e => e.rgb === normalized)?.index;
84
+ }
85
+ // ── Private helpers ───────────────────────────────────────────────────────
86
+ getParsedDoc() {
87
+ const xml = this.pkg.getFileText('visio/document.xml');
88
+ return this.parser.parse(xml);
89
+ }
90
+ saveParsedDoc(parsed) {
91
+ this.pkg.updateFile('visio/document.xml', (0, XmlHelper_1.buildXml)(this.builder, parsed));
92
+ }
93
+ /** Ensure `<Colors>` exists and contains the two built-in entries. */
94
+ ensureColors(doc) {
95
+ if (!doc['Colors'])
96
+ doc['Colors'] = {};
97
+ const colors = doc['Colors'];
98
+ const entries = toArray(colors['ColorEntry']);
99
+ if (!entries.some((e) => e['@_IX'] === '0')) {
100
+ entries.unshift({ '@_IX': '0', '@_RGB': '#000000' });
101
+ }
102
+ if (!entries.some((e) => e['@_IX'] === '1')) {
103
+ const blackPos = entries.findIndex((e) => e['@_IX'] === '0');
104
+ entries.splice(blackPos + 1, 0, { '@_IX': '1', '@_RGB': '#FFFFFF' });
105
+ }
106
+ colors['ColorEntry'] = entries;
107
+ }
108
+ /** Next IX = max existing index + 1, always at least 2. */
109
+ nextIndex(entries) {
110
+ const max = entries.reduce((m, e) => {
111
+ const ix = parseInt(e['@_IX'], 10);
112
+ return isNaN(ix) ? m : Math.max(m, ix);
113
+ }, 1);
114
+ return max + 1;
115
+ }
116
+ /** Minimal built-in palette returned when <Colors> is absent. */
117
+ builtIns() {
118
+ return [
119
+ { index: 0, rgb: '#000000' },
120
+ { index: 1, rgb: '#FFFFFF' },
121
+ ];
122
+ }
123
+ }
124
+ exports.ColorManager = ColorManager;
125
+ // ── Module-level helpers ──────────────────────────────────────────────────────
126
+ /** Normalise any hex string to uppercase `#RRGGBB`. */
127
+ function normalizeHex(hex) {
128
+ let h = hex.startsWith('#') ? hex.slice(1) : hex;
129
+ if (h.length === 3)
130
+ h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
131
+ return '#' + h.toUpperCase();
132
+ }
133
+ function toArray(val) {
134
+ if (!val)
135
+ return [];
136
+ return Array.isArray(val) ? val : [val];
137
+ }
package/dist/index.d.ts CHANGED
@@ -13,3 +13,5 @@ export { Layer } from './Layer';
13
13
  export { SchemaDiagram } from './SchemaDiagram';
14
14
  export { VisioValidator } from './core/VisioValidator';
15
15
  export * from './types/VisioTypes';
16
+ export { ArrowHeads, hexToRgb } from './utils/StyleHelpers';
17
+ export type { ShapeStyle } from './ShapeModifier';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.VisioValidator = exports.SchemaDiagram = exports.Layer = exports.Connector = exports.Shape = exports.Page = exports.VisioDocument = exports.ShapeModifier = exports.ShapeReader = exports.PageManager = exports.VisioPackage = void 0;
17
+ exports.hexToRgb = exports.ArrowHeads = exports.VisioValidator = exports.SchemaDiagram = exports.Layer = exports.Connector = exports.Shape = exports.Page = exports.VisioDocument = exports.ShapeModifier = exports.ShapeReader = exports.PageManager = exports.VisioPackage = void 0;
18
18
  var VisioPackage_1 = require("./VisioPackage");
19
19
  Object.defineProperty(exports, "VisioPackage", { enumerable: true, get: function () { return VisioPackage_1.VisioPackage; } });
20
20
  var PageManager_1 = require("./core/PageManager");
@@ -38,3 +38,6 @@ Object.defineProperty(exports, "SchemaDiagram", { enumerable: true, get: functio
38
38
  var VisioValidator_1 = require("./core/VisioValidator");
39
39
  Object.defineProperty(exports, "VisioValidator", { enumerable: true, get: function () { return VisioValidator_1.VisioValidator; } });
40
40
  __exportStar(require("./types/VisioTypes"), exports);
41
+ var StyleHelpers_1 = require("./utils/StyleHelpers");
42
+ Object.defineProperty(exports, "ArrowHeads", { enumerable: true, get: function () { return StyleHelpers_1.ArrowHeads; } });
43
+ Object.defineProperty(exports, "hexToRgb", { enumerable: true, get: function () { return StyleHelpers_1.hexToRgb; } });
@@ -146,6 +146,19 @@ export interface ConnectorStyle {
146
146
  }
147
147
  /** Non-rectangular geometry variants supported by ShapeBuilder. */
148
148
  export type ShapeGeometry = 'rectangle' | 'ellipse' | 'diamond' | 'rounded-rectangle' | 'triangle' | 'parallelogram';
149
+ /**
150
+ * A single entry in the document-level color palette (`<Colors>` in `document.xml`).
151
+ * Returned by `doc.getColors()` and created via `doc.addColor()`.
152
+ */
153
+ export interface ColorEntry {
154
+ /**
155
+ * Zero-based integer index (IX). Can be passed as a color reference
156
+ * anywhere a hex string is accepted (e.g. `fillColor`, `lineColor`).
157
+ */
158
+ index: number;
159
+ /** Normalized CSS hex string, always uppercase `#RRGGBB`. */
160
+ rgb: string;
161
+ }
149
162
  /** A reference to a document-level stylesheet, returned by `doc.createStyle()`. */
150
163
  export interface StyleRecord {
151
164
  /** Zero-based integer ID used as `LineStyle` / `FillStyle` / `TextStyle` on shapes. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-visio",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^25.0.9",
28
+ "tsx": "^4.21.0",
28
29
  "typescript": "^5.9.3",
29
30
  "vitest": "^4.0.17"
30
31
  }