excalidraw-gen 0.1.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 +320 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +107 -0
- package/dist/cli.js.map +1 -0
- package/dist/exporter/index.d.ts +4 -0
- package/dist/exporter/index.js +33 -0
- package/dist/exporter/index.js.map +1 -0
- package/dist/layout/arrow-router.d.ts +14 -0
- package/dist/layout/arrow-router.js +171 -0
- package/dist/layout/arrow-router.js.map +1 -0
- package/dist/layout/dag.d.ts +13 -0
- package/dist/layout/dag.js +158 -0
- package/dist/layout/dag.js.map +1 -0
- package/dist/layout/grid.d.ts +3 -0
- package/dist/layout/grid.js +52 -0
- package/dist/layout/grid.js.map +1 -0
- package/dist/layout/index.d.ts +5 -0
- package/dist/layout/index.js +21 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/normalizer/index.d.ts +5 -0
- package/dist/normalizer/index.js +37 -0
- package/dist/normalizer/index.js.map +1 -0
- package/dist/parser/index.d.ts +5 -0
- package/dist/parser/index.js +154 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/renderer/elements.d.ts +4 -0
- package/dist/renderer/elements.js +210 -0
- package/dist/renderer/elements.js.map +1 -0
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.js +76 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/seed.d.ts +5 -0
- package/dist/renderer/seed.js +18 -0
- package/dist/renderer/seed.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.js +93 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/themes.d.ts +3 -0
- package/dist/templates/themes.js +90 -0
- package/dist/templates/themes.js.map +1 -0
- package/dist/types/index.d.ts +222 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validator/index.d.ts +4 -0
- package/dist/validator/index.js +132 -0
- package/dist/validator/index.js.map +1 -0
- package/dist/validator/preflight.d.ts +10 -0
- package/dist/validator/preflight.js +138 -0
- package/dist/validator/preflight.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nixon Kurian
|
|
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,320 @@
|
|
|
1
|
+
# excalidraw-gen
|
|
2
|
+
|
|
3
|
+
A CLI tool that generates [Excalidraw](https://excalidraw.com)-compatible `.excalidraw` JSON files from structured JSON **or YAML** input specs.
|
|
4
|
+
|
|
5
|
+
Define your diagram with nodes, edges, types, colors, sizes, and arrow styles — get a fully-formed Excalidraw diagram you can open immediately in the browser or desktop app.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **JSON and YAML input** — both formats supported natively
|
|
12
|
+
- **Per-node style overrides** — color, size, shape, opacity, fill pattern, border style
|
|
13
|
+
- **Per-edge style overrides** — stroke color, dash style, thickness, opacity
|
|
14
|
+
- **DAG layout** — BFS layered layout with Kahn's algorithm (handles cyclic graphs without hanging)
|
|
15
|
+
- **Dynamic node sizing** — node height auto-expands for multi-line or long labels
|
|
16
|
+
- **Grid layout** — simple √n-column grid for non-hierarchical diagrams
|
|
17
|
+
- **Two templates** — `flowchart` and `architecture` with distinct node styles
|
|
18
|
+
- **Three themes** — `default`, `pastel`, `dark`
|
|
19
|
+
- **Step-routed elbow arrows** — clearly exit the correct edge of the source node
|
|
20
|
+
- **Accurate arrow labels** — labels placed at the geometric midpoint of the elbow path
|
|
21
|
+
- **Pre-flight validation** — catches Excalidraw JSON issues before writing the file
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/your-username/excalidraw-gen.git
|
|
29
|
+
cd excalidraw-gen
|
|
30
|
+
npm install
|
|
31
|
+
npm run build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
To use globally:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm link
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
excalidraw-gen generate <input> [options]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Option | Default | Description |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `--template` | `flowchart` | `flowchart` or `architecture` |
|
|
51
|
+
| `--theme` | `default` | `default`, `pastel`, or `dark` |
|
|
52
|
+
| `--layout` | `dag` | `dag` or `grid` |
|
|
53
|
+
| `--out` | stdout | Output file path (`.excalidraw`) |
|
|
54
|
+
| `--max-nodes <n>` | `200` | Reject diagrams with more nodes than this |
|
|
55
|
+
|
|
56
|
+
### Examples
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# JSON flowchart
|
|
60
|
+
excalidraw-gen generate examples/simple-flowchart.json \
|
|
61
|
+
--template flowchart --out out/flow.excalidraw
|
|
62
|
+
|
|
63
|
+
# YAML input
|
|
64
|
+
excalidraw-gen generate examples/pipeline.yaml --out out/pipeline.excalidraw
|
|
65
|
+
|
|
66
|
+
# Architecture diagram with dark theme
|
|
67
|
+
excalidraw-gen generate examples/architecture.json \
|
|
68
|
+
--template architecture --theme dark --out out/arch.excalidraw
|
|
69
|
+
|
|
70
|
+
# Styled flowchart with per-node/edge colors
|
|
71
|
+
excalidraw-gen generate examples/styled-flowchart.json --out out/styled.excalidraw
|
|
72
|
+
|
|
73
|
+
# Print to stdout (pipe-friendly)
|
|
74
|
+
excalidraw-gen generate examples/simple-flowchart.json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Open the `.excalidraw` file at [excalidraw.com](https://excalidraw.com) via **Open** → select file.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Input Format
|
|
82
|
+
|
|
83
|
+
Input can be **JSON** or **YAML**. Both support the same fields.
|
|
84
|
+
|
|
85
|
+
### JSON
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"type": "flowchart",
|
|
90
|
+
"title": "My Diagram",
|
|
91
|
+
"nodes": [
|
|
92
|
+
{
|
|
93
|
+
"id": "start",
|
|
94
|
+
"label": "Start",
|
|
95
|
+
"type": "start",
|
|
96
|
+
"style": {
|
|
97
|
+
"backgroundColor": "#a5d8ff",
|
|
98
|
+
"strokeColor": "#1971c2",
|
|
99
|
+
"shape": "ellipse",
|
|
100
|
+
"width": 160,
|
|
101
|
+
"height": 60
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{ "id": "process", "label": "Do Something", "type": "process" },
|
|
105
|
+
{
|
|
106
|
+
"id": "decide",
|
|
107
|
+
"label": "OK?",
|
|
108
|
+
"type": "decision",
|
|
109
|
+
"style": { "strokeStyle": "dashed", "strokeWidth": 3 }
|
|
110
|
+
},
|
|
111
|
+
{ "id": "end", "label": "End", "type": "end" }
|
|
112
|
+
],
|
|
113
|
+
"edges": [
|
|
114
|
+
{ "from": "start", "to": "process" },
|
|
115
|
+
{ "from": "process", "to": "decide", "label": "Check" },
|
|
116
|
+
{
|
|
117
|
+
"from": "decide",
|
|
118
|
+
"to": "end",
|
|
119
|
+
"label": "Yes",
|
|
120
|
+
"style": { "strokeColor": "#2f9e44", "strokeWidth": 2 }
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### YAML
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
type: flowchart
|
|
130
|
+
title: My Diagram
|
|
131
|
+
|
|
132
|
+
nodes:
|
|
133
|
+
- id: start
|
|
134
|
+
label: Start
|
|
135
|
+
type: start
|
|
136
|
+
style:
|
|
137
|
+
backgroundColor: "#a5d8ff"
|
|
138
|
+
strokeColor: "#1971c2"
|
|
139
|
+
shape: ellipse
|
|
140
|
+
|
|
141
|
+
- id: process
|
|
142
|
+
label: Do Something
|
|
143
|
+
type: process
|
|
144
|
+
|
|
145
|
+
edges:
|
|
146
|
+
- from: start
|
|
147
|
+
to: process
|
|
148
|
+
label: Go
|
|
149
|
+
style:
|
|
150
|
+
strokeColor: "#7048e8"
|
|
151
|
+
strokeStyle: dashed
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Style Overrides
|
|
157
|
+
|
|
158
|
+
### Per-node `style` fields
|
|
159
|
+
|
|
160
|
+
| Field | Type | Description |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `backgroundColor` | `string` | Fill color (hex, e.g. `"#ff6b6b"` or `"transparent"`) |
|
|
163
|
+
| `strokeColor` | `string` | Border color |
|
|
164
|
+
| `strokeWidth` | `number` | Border thickness in px |
|
|
165
|
+
| `strokeStyle` | `"solid"` \| `"dashed"` \| `"dotted"` | Border style |
|
|
166
|
+
| `fillStyle` | `"solid"` \| `"hachure"` \| `"cross-hatch"` \| `"zigzag"` | Fill pattern |
|
|
167
|
+
| `shape` | `"rectangle"` \| `"ellipse"` | Override the template shape |
|
|
168
|
+
| `width` | `number` | Node width in px |
|
|
169
|
+
| `height` | `number` | Node height in px (auto-computed if omitted) |
|
|
170
|
+
| `opacity` | `number` | Opacity 0–100 |
|
|
171
|
+
|
|
172
|
+
### Per-edge `style` fields
|
|
173
|
+
|
|
174
|
+
| Field | Type | Description |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `strokeColor` | `string` | Arrow color |
|
|
177
|
+
| `strokeStyle` | `"solid"` \| `"dashed"` \| `"dotted"` | Line style |
|
|
178
|
+
| `strokeWidth` | `number` | Thickness in px |
|
|
179
|
+
| `opacity` | `number` | Opacity 0–100 |
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Node Types
|
|
184
|
+
|
|
185
|
+
### Flowchart template
|
|
186
|
+
|
|
187
|
+
| Type | Default style |
|
|
188
|
+
|---|---|
|
|
189
|
+
| `start` / `end` | Ellipse |
|
|
190
|
+
| `process` | Rectangle (purple) |
|
|
191
|
+
| `decision` | Dashed orange rectangle |
|
|
192
|
+
| `io` | Rectangle (teal) |
|
|
193
|
+
| `subprocess` | Rectangle (green) |
|
|
194
|
+
|
|
195
|
+
### Architecture template
|
|
196
|
+
|
|
197
|
+
| Type | Default style |
|
|
198
|
+
|---|---|
|
|
199
|
+
| `service` / `api` | Rectangle (purple) |
|
|
200
|
+
| `db` / `database` | Rectangle (green) |
|
|
201
|
+
| `queue` | Rectangle (yellow) |
|
|
202
|
+
| `cache` | Rectangle (orange) |
|
|
203
|
+
| `gateway` / `orchestrator` | Bold rectangle (red) |
|
|
204
|
+
| `frontend` | Rectangle (blue) |
|
|
205
|
+
| `user` / `actor` | Ellipse |
|
|
206
|
+
| `external` | Rectangle (light red) |
|
|
207
|
+
| `ml` / `ai` | Rectangle (purple haze) |
|
|
208
|
+
| `monitor` | Rectangle (green) |
|
|
209
|
+
|
|
210
|
+
> **Note:** No `diamond` shape exists in Excalidraw raw JSON. Decision nodes use a dashed orange rectangle as the canonical alternative.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
### Scripts
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm run build # Compile TypeScript → dist/
|
|
220
|
+
npm run dev # Run directly via ts-node (no build needed)
|
|
221
|
+
npm test # Run all tests (vitest)
|
|
222
|
+
npm run test:watch # Watch mode
|
|
223
|
+
npm run lint # Type-check without emitting
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Project structure
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
src/
|
|
230
|
+
cli.ts # Commander CLI entrypoint
|
|
231
|
+
types/ # Shared TypeScript types (NodeStyle, EdgeStyle, etc.)
|
|
232
|
+
parser/ # JSON + YAML input → Diagram object
|
|
233
|
+
validator/ # Input validation (errors + warnings)
|
|
234
|
+
normalizer/ # Defaults, ID cleanup, duplicate-edge detection
|
|
235
|
+
layout/
|
|
236
|
+
dag.ts # Kahn's topological sort + BFS DAG layout
|
|
237
|
+
grid.ts # Grid layout
|
|
238
|
+
arrow-router.ts # Elbow arrow anchor + step point calculation
|
|
239
|
+
renderer/
|
|
240
|
+
elements.ts # Shape / text / arrow element factories (with style merging)
|
|
241
|
+
index.ts # Full render orchestration
|
|
242
|
+
seed.ts # Deterministic ID hashing (FNV-1a)
|
|
243
|
+
templates/
|
|
244
|
+
index.ts # flowchart + architecture template definitions
|
|
245
|
+
themes.ts # default / pastel / dark color overlays
|
|
246
|
+
exporter/ # Assemble ExcalidrawFile, pre-flight check, write
|
|
247
|
+
validator/
|
|
248
|
+
preflight.ts # Post-render element sanity checks
|
|
249
|
+
examples/
|
|
250
|
+
simple-flowchart.json # 5-node login flow
|
|
251
|
+
styled-flowchart.json # Payment flow with per-node/edge style overrides
|
|
252
|
+
architecture.json # 8-node 3-tier web app
|
|
253
|
+
pipeline.yaml # CI/CD pipeline (YAML format)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Contributing
|
|
259
|
+
|
|
260
|
+
### 1. Fork and clone
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
git clone https://github.com/your-username/excalidraw-gen.git
|
|
264
|
+
cd excalidraw-gen
|
|
265
|
+
npm install
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 2. Create a branch
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
git checkout -b feat/your-feature-name
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Use a descriptive prefix: `feat/`, `fix/`, `docs/`, `test/`, `refactor/`.
|
|
275
|
+
|
|
276
|
+
### 3. Make your changes
|
|
277
|
+
|
|
278
|
+
- Keep changes focused — one concern per PR
|
|
279
|
+
- Match the existing code style (TypeScript strict mode, no `any`)
|
|
280
|
+
- Do not add comments or docstrings to code you didn't change
|
|
281
|
+
|
|
282
|
+
### 4. Run tests and build
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
npm run build && npm test
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Both must pass with zero errors before submitting.
|
|
289
|
+
|
|
290
|
+
### 5. Submit a pull request
|
|
291
|
+
|
|
292
|
+
Open a PR against `main` with a clear description of what changes and why.
|
|
293
|
+
|
|
294
|
+
### Adding a new template
|
|
295
|
+
|
|
296
|
+
1. Add a `TemplateDefinition` in `src/templates/index.ts` using `registerTemplate()`
|
|
297
|
+
2. Add theme overrides in `src/templates/themes.ts` if needed
|
|
298
|
+
3. Add an example JSON or YAML file under `examples/`
|
|
299
|
+
4. Add tests in `tests/renderer.test.ts`
|
|
300
|
+
|
|
301
|
+
### Adding a new layout engine
|
|
302
|
+
|
|
303
|
+
1. Implement `(diagram: Diagram) => LayoutNode[]` in `src/layout/`
|
|
304
|
+
2. Register it in `src/layout/index.ts` using `registerLayoutEngine()`
|
|
305
|
+
3. Add tests in `tests/layout.test.ts`
|
|
306
|
+
|
|
307
|
+
### Known Excalidraw raw JSON constraints
|
|
308
|
+
|
|
309
|
+
- No `type: "diamond"` — use dashed rectangles for decision nodes
|
|
310
|
+
- Labels require **two elements**: the shape with `boundElements` + a separate `text` with `containerId`
|
|
311
|
+
- Arrows must have `roughness: 0`, `roundness: null`, `elbowed: true`
|
|
312
|
+
- Arrow `x, y` is the **edge point** of the source shape, not the center
|
|
313
|
+
- `points[0]` is always `[0, 0]`; all other points are relative offsets
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
[MIT](LICENSE)
|
|
320
|
+
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const commander_1 = require("commander");
|
|
6
|
+
const index_js_1 = require("./parser/index.js");
|
|
7
|
+
const index_js_2 = require("./validator/index.js");
|
|
8
|
+
const index_js_3 = require("./normalizer/index.js");
|
|
9
|
+
const index_js_4 = require("./templates/index.js");
|
|
10
|
+
const themes_js_1 = require("./templates/themes.js");
|
|
11
|
+
const index_js_5 = require("./layout/index.js");
|
|
12
|
+
const index_js_6 = require("./renderer/index.js");
|
|
13
|
+
const index_js_7 = require("./exporter/index.js");
|
|
14
|
+
const preflight_js_1 = require("./validator/preflight.js");
|
|
15
|
+
const program = new commander_1.Command();
|
|
16
|
+
program
|
|
17
|
+
.name("excalidraw-gen")
|
|
18
|
+
.description("Generate Excalidraw-compatible JSON diagrams from structured input specs")
|
|
19
|
+
.version("0.1.0");
|
|
20
|
+
program
|
|
21
|
+
.command("generate <input>")
|
|
22
|
+
.description("Generate an Excalidraw diagram from a JSON spec file")
|
|
23
|
+
.option("--template <name>", "Diagram template: flowchart | architecture", "flowchart")
|
|
24
|
+
.option("--theme <name>", "Color theme: default | pastel | dark", "default")
|
|
25
|
+
.option("--layout <type>", "Layout algorithm: dag | grid", "dag")
|
|
26
|
+
.option("--out <filepath>", "Output file path (default: stdout)")
|
|
27
|
+
.option("--no-deterministic", "Disable deterministic mode (enabled by default)")
|
|
28
|
+
.option("--max-nodes <n>", "Maximum nodes allowed", (v) => parseInt(v, 10), 200)
|
|
29
|
+
.action((inputPath, opts) => {
|
|
30
|
+
const options = {
|
|
31
|
+
template: opts.template,
|
|
32
|
+
theme: opts.theme,
|
|
33
|
+
layout: opts.layout,
|
|
34
|
+
out: opts.out,
|
|
35
|
+
deterministic: opts.deterministic,
|
|
36
|
+
maxNodes: opts.maxNodes,
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
// ── 1. Read input ──────────────────────────────────────────────────
|
|
40
|
+
let inputContent;
|
|
41
|
+
try {
|
|
42
|
+
inputContent = (0, fs_1.readFileSync)(inputPath, "utf-8");
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
process.stderr.write(`Error: Cannot read file "${inputPath}"\n`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
// ── 2. Parse ───────────────────────────────────────────────────────
|
|
49
|
+
let diagram;
|
|
50
|
+
try {
|
|
51
|
+
diagram = (0, index_js_1.parse)(inputContent);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof index_js_1.ParseError) {
|
|
55
|
+
process.stderr.write(`Parse error: ${err.message}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
// ── 3. Validate (input) ────────────────────────────────────────────
|
|
61
|
+
const { errors, warnings } = (0, index_js_2.validate)(diagram, { maxNodes: options.maxNodes });
|
|
62
|
+
for (const warn of warnings) {
|
|
63
|
+
process.stderr.write(`Warning: ${warn}\n`);
|
|
64
|
+
}
|
|
65
|
+
if (errors.length > 0) {
|
|
66
|
+
for (const err of errors) {
|
|
67
|
+
process.stderr.write(`Error: ${err}\n`);
|
|
68
|
+
}
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
// ── 4. Normalize ───────────────────────────────────────────────────
|
|
72
|
+
const { diagram: normalDiagram, warnings: normWarnings } = (0, index_js_3.normalize)(diagram);
|
|
73
|
+
for (const warn of normWarnings) {
|
|
74
|
+
process.stderr.write(`Warning: ${warn}\n`);
|
|
75
|
+
}
|
|
76
|
+
// ── 5. Layout ──────────────────────────────────────────────────────
|
|
77
|
+
const layoutNodes = (0, index_js_5.runLayout)(normalDiagram, options.layout);
|
|
78
|
+
// ── 6. Template + Theme ────────────────────────────────────────────
|
|
79
|
+
const baseTemplate = (0, index_js_4.getTemplate)(options.template);
|
|
80
|
+
const template = (0, themes_js_1.applyThemeToTemplate)(baseTemplate, options.theme);
|
|
81
|
+
// ── 7. Render ──────────────────────────────────────────────────────
|
|
82
|
+
const elements = (0, index_js_6.render)(normalDiagram, layoutNodes, template);
|
|
83
|
+
// ── 8. Export ──────────────────────────────────────────────────────
|
|
84
|
+
if (options.out) {
|
|
85
|
+
(0, index_js_7.exportToFile)(elements, options.theme, options.out);
|
|
86
|
+
process.stderr.write(`✓ Diagram written to ${options.out}\n`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
(0, index_js_7.exportToStdout)(elements, options.theme);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err instanceof preflight_js_1.PreflightError) {
|
|
94
|
+
process.stderr.write(`${err.message}\n`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (err instanceof Error) {
|
|
98
|
+
process.stderr.write(`Unexpected error: ${err.message}\n`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
process.stderr.write(`Unexpected error: ${String(err)}\n`);
|
|
102
|
+
}
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
program.parse(process.argv);
|
|
107
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExcalidrawElement, ExcalidrawFile, Theme } from "../types/index.js";
|
|
2
|
+
export declare function buildExcalidrawFile(elements: ExcalidrawElement[], theme: Theme): ExcalidrawFile;
|
|
3
|
+
export declare function exportToFile(elements: ExcalidrawElement[], theme: Theme, outputPath: string): void;
|
|
4
|
+
export declare function exportToStdout(elements: ExcalidrawElement[], theme: Theme): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildExcalidrawFile = buildExcalidrawFile;
|
|
4
|
+
exports.exportToFile = exportToFile;
|
|
5
|
+
exports.exportToStdout = exportToStdout;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const preflight_js_1 = require("../validator/preflight.js");
|
|
8
|
+
const themes_js_1 = require("../templates/themes.js");
|
|
9
|
+
function buildExcalidrawFile(elements, theme) {
|
|
10
|
+
return {
|
|
11
|
+
type: "excalidraw",
|
|
12
|
+
version: 2,
|
|
13
|
+
source: "excalidraw-gen",
|
|
14
|
+
elements,
|
|
15
|
+
appState: {
|
|
16
|
+
gridSize: 20,
|
|
17
|
+
viewBackgroundColor: (0, themes_js_1.getViewBackground)(theme),
|
|
18
|
+
},
|
|
19
|
+
files: {},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function exportToFile(elements, theme, outputPath) {
|
|
23
|
+
(0, preflight_js_1.preflightValidate)(elements);
|
|
24
|
+
const file = buildExcalidrawFile(elements, theme);
|
|
25
|
+
const json = JSON.stringify(file, null, 2);
|
|
26
|
+
(0, fs_1.writeFileSync)(outputPath, json, "utf-8");
|
|
27
|
+
}
|
|
28
|
+
function exportToStdout(elements, theme) {
|
|
29
|
+
(0, preflight_js_1.preflightValidate)(elements);
|
|
30
|
+
const file = buildExcalidrawFile(elements, theme);
|
|
31
|
+
process.stdout.write(JSON.stringify(file, null, 2) + "\n");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exporter/index.ts"],"names":[],"mappings":";;AAKA,kDAeC;AAED,oCASC;AAED,wCAOC;AAxCD,2BAAmC;AAEnC,4DAA8D;AAC9D,sDAA2D;AAE3D,SAAgB,mBAAmB,CACjC,QAA6B,EAC7B,KAAY;IAEZ,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,gBAAgB;QACxB,QAAQ;QACR,QAAQ,EAAE;YACR,QAAQ,EAAE,EAAE;YACZ,mBAAmB,EAAE,IAAA,6BAAiB,EAAC,KAAK,CAAC;SAC9C;QACD,KAAK,EAAE,EAAE;KACV,CAAC;AACJ,CAAC;AAED,SAAgB,YAAY,CAC1B,QAA6B,EAC7B,KAAY,EACZ,UAAkB;IAElB,IAAA,gCAAiB,EAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3C,IAAA,kBAAa,EAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,cAAc,CAC5B,QAA6B,EAC7B,KAAY;IAEZ,IAAA,gCAAiB,EAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LayoutNode, ArrowRoute } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Minimum y-difference to consider nodes as being on different rows.
|
|
4
|
+
* Matches SPACING_Y * 0.4 from the layout engine (150 * 0.4 = 60).
|
|
5
|
+
* Exported so the renderer can use the same threshold for stagger grouping.
|
|
6
|
+
*/
|
|
7
|
+
export declare const VERTICAL_THRESHOLD = 60;
|
|
8
|
+
/**
|
|
9
|
+
* Route an elbow arrow from source → target nodes.
|
|
10
|
+
*
|
|
11
|
+
* @param sourceEdgeIndex 0-based index of this arrow among all arrows sharing the same source edge
|
|
12
|
+
* @param totalFromSource total count of arrows leaving from the same edge of source
|
|
13
|
+
*/
|
|
14
|
+
export declare function routeArrow(source: LayoutNode, target: LayoutNode, sourceEdgeIndex?: number, totalFromSource?: number): ArrowRoute;
|