@diagrammo/dgmo 0.0.1 → 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/dist/cli.cjs +5195 -0
- package/dist/index.cjs +214 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +213 -37
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cli.ts +189 -0
- package/src/index.ts +7 -1
- package/src/sequence/parser.ts +7 -0
- package/src/sequence/renderer.ts +339 -99
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diagrammo/dgmo",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "DGMO diagram markup language — parser, renderer, and color system",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dgmo": "./dist/cli.cjs"
|
|
9
|
+
},
|
|
7
10
|
"main": "./dist/index.cjs",
|
|
8
11
|
"module": "./dist/index.js",
|
|
9
12
|
"types": "./dist/index.d.ts",
|
|
@@ -37,9 +40,11 @@
|
|
|
37
40
|
"d3-scale": "^4.0.2",
|
|
38
41
|
"d3-selection": "^3.0.0",
|
|
39
42
|
"d3-shape": "^3.2.0",
|
|
40
|
-
"echarts": "^5.6.0"
|
|
43
|
+
"echarts": "^5.6.0",
|
|
44
|
+
"jsdom": "^26.0.0"
|
|
41
45
|
},
|
|
42
46
|
"devDependencies": {
|
|
47
|
+
"@types/jsdom": "^21.1.7",
|
|
43
48
|
"@types/d3-array": "^3.2.1",
|
|
44
49
|
"@types/d3-cloud": "^1.2.9",
|
|
45
50
|
"@types/d3-scale": "^4.0.8",
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { JSDOM } from 'jsdom';
|
|
4
|
+
import { renderD3ForExport } from './d3';
|
|
5
|
+
import { getPalette } from './palettes/registry';
|
|
6
|
+
|
|
7
|
+
const PALETTES = [
|
|
8
|
+
'nord',
|
|
9
|
+
'solarized',
|
|
10
|
+
'catppuccin',
|
|
11
|
+
'rose-pine',
|
|
12
|
+
'gruvbox',
|
|
13
|
+
'tokyo-night',
|
|
14
|
+
'one-dark',
|
|
15
|
+
'bold',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const THEMES = ['light', 'dark', 'transparent'] as const;
|
|
19
|
+
|
|
20
|
+
function printHelp(): void {
|
|
21
|
+
console.log(`Usage: dgmo render <input> [options]
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
render <input> Render a .dgmo file to SVG
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
-o <file> Write SVG to file (default: stdout)
|
|
28
|
+
--theme <theme> Theme: ${THEMES.join(', ')} (default: light)
|
|
29
|
+
--palette <name> Palette: ${PALETTES.join(', ')} (default: nord)
|
|
30
|
+
--help Show this help
|
|
31
|
+
--version Show version`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function printVersion(): void {
|
|
35
|
+
const pkg = JSON.parse(
|
|
36
|
+
readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8')
|
|
37
|
+
);
|
|
38
|
+
console.log(pkg.version);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseArgs(argv: string[]): {
|
|
42
|
+
command: string | undefined;
|
|
43
|
+
input: string | undefined;
|
|
44
|
+
output: string | undefined;
|
|
45
|
+
theme: (typeof THEMES)[number];
|
|
46
|
+
palette: string;
|
|
47
|
+
help: boolean;
|
|
48
|
+
version: boolean;
|
|
49
|
+
} {
|
|
50
|
+
const result = {
|
|
51
|
+
command: undefined as string | undefined,
|
|
52
|
+
input: undefined as string | undefined,
|
|
53
|
+
output: undefined as string | undefined,
|
|
54
|
+
theme: 'light' as (typeof THEMES)[number],
|
|
55
|
+
palette: 'nord',
|
|
56
|
+
help: false,
|
|
57
|
+
version: false,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const args = argv.slice(2); // skip node + script
|
|
61
|
+
let i = 0;
|
|
62
|
+
|
|
63
|
+
while (i < args.length) {
|
|
64
|
+
const arg = args[i];
|
|
65
|
+
|
|
66
|
+
if (arg === '--help' || arg === '-h') {
|
|
67
|
+
result.help = true;
|
|
68
|
+
i++;
|
|
69
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
70
|
+
result.version = true;
|
|
71
|
+
i++;
|
|
72
|
+
} else if (arg === '-o') {
|
|
73
|
+
result.output = args[++i];
|
|
74
|
+
i++;
|
|
75
|
+
} else if (arg === '--theme') {
|
|
76
|
+
const val = args[++i];
|
|
77
|
+
if (!THEMES.includes(val as (typeof THEMES)[number])) {
|
|
78
|
+
console.error(
|
|
79
|
+
`Error: Invalid theme "${val}". Valid themes: ${THEMES.join(', ')}`
|
|
80
|
+
);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
result.theme = val as (typeof THEMES)[number];
|
|
84
|
+
i++;
|
|
85
|
+
} else if (arg === '--palette') {
|
|
86
|
+
const val = args[++i];
|
|
87
|
+
if (!PALETTES.includes(val)) {
|
|
88
|
+
console.error(
|
|
89
|
+
`Error: Unknown palette "${val}". Valid palettes: ${PALETTES.join(', ')}`
|
|
90
|
+
);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
result.palette = val;
|
|
94
|
+
i++;
|
|
95
|
+
} else if (!result.command) {
|
|
96
|
+
result.command = arg;
|
|
97
|
+
i++;
|
|
98
|
+
} else if (!result.input) {
|
|
99
|
+
result.input = arg;
|
|
100
|
+
i++;
|
|
101
|
+
} else {
|
|
102
|
+
console.error(`Error: Unexpected argument "${arg}"`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function setupDom(): void {
|
|
111
|
+
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
112
|
+
const win = dom.window;
|
|
113
|
+
|
|
114
|
+
// Expose DOM globals needed by d3-selection and renderers
|
|
115
|
+
Object.defineProperty(globalThis, 'document', { value: win.document, configurable: true });
|
|
116
|
+
Object.defineProperty(globalThis, 'window', { value: win, configurable: true });
|
|
117
|
+
Object.defineProperty(globalThis, 'navigator', { value: win.navigator, configurable: true });
|
|
118
|
+
Object.defineProperty(globalThis, 'HTMLElement', { value: win.HTMLElement, configurable: true });
|
|
119
|
+
Object.defineProperty(globalThis, 'SVGElement', { value: win.SVGElement, configurable: true });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function main(): Promise<void> {
|
|
123
|
+
const opts = parseArgs(process.argv);
|
|
124
|
+
|
|
125
|
+
if (opts.help) {
|
|
126
|
+
printHelp();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (opts.version) {
|
|
131
|
+
printVersion();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (opts.command !== 'render') {
|
|
136
|
+
if (opts.command) {
|
|
137
|
+
console.error(`Error: Unknown command "${opts.command}"`);
|
|
138
|
+
} else {
|
|
139
|
+
console.error('Error: No command specified');
|
|
140
|
+
}
|
|
141
|
+
console.error('Run "dgmo --help" for usage');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!opts.input) {
|
|
146
|
+
console.error('Error: No input file specified');
|
|
147
|
+
console.error('Usage: dgmo render <input> [-o output.svg]');
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const inputPath = resolve(opts.input);
|
|
152
|
+
let content: string;
|
|
153
|
+
try {
|
|
154
|
+
content = readFileSync(inputPath, 'utf-8');
|
|
155
|
+
} catch {
|
|
156
|
+
console.error(`Error: Cannot read file "${inputPath}"`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Set up jsdom before any d3/renderer code runs
|
|
161
|
+
setupDom();
|
|
162
|
+
|
|
163
|
+
const isDark = opts.theme === 'dark';
|
|
164
|
+
const paletteColors = isDark
|
|
165
|
+
? getPalette(opts.palette).dark
|
|
166
|
+
: getPalette(opts.palette).light;
|
|
167
|
+
|
|
168
|
+
const svg = await renderD3ForExport(content, opts.theme, paletteColors);
|
|
169
|
+
|
|
170
|
+
if (!svg) {
|
|
171
|
+
console.error(
|
|
172
|
+
'Error: Failed to render diagram. The input may be empty, invalid, or use an unsupported chart type (e.g. Chart.js/ECharts charts require a browser).'
|
|
173
|
+
);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (opts.output) {
|
|
178
|
+
const outputPath = resolve(opts.output);
|
|
179
|
+
writeFileSync(outputPath, svg, 'utf-8');
|
|
180
|
+
console.error(`Wrote ${outputPath}`);
|
|
181
|
+
} else {
|
|
182
|
+
process.stdout.write(svg);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main().catch((err: Error) => {
|
|
187
|
+
console.error(`Error: ${err.message}`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -85,8 +85,14 @@ export {
|
|
|
85
85
|
computeActivations,
|
|
86
86
|
applyPositionOverrides,
|
|
87
87
|
applyGroupOrdering,
|
|
88
|
+
groupMessagesBySection,
|
|
89
|
+
} from './sequence/renderer';
|
|
90
|
+
export type {
|
|
91
|
+
RenderStep,
|
|
92
|
+
Activation,
|
|
93
|
+
SectionMessageGroup,
|
|
94
|
+
SequenceRenderOptions,
|
|
88
95
|
} from './sequence/renderer';
|
|
89
|
-
export type { RenderStep, Activation } from './sequence/renderer';
|
|
90
96
|
|
|
91
97
|
// ============================================================
|
|
92
98
|
// Colors & Palettes
|
package/src/sequence/parser.ts
CHANGED
|
@@ -247,8 +247,15 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
247
247
|
if (trimmed.startsWith('#') || trimmed.startsWith('//')) continue;
|
|
248
248
|
|
|
249
249
|
// Parse section dividers — "== Label ==" or "== Label(color) =="
|
|
250
|
+
// Close blocks first — sections at indent 0 should not nest inside blocks
|
|
250
251
|
const sectionMatch = trimmed.match(SECTION_PATTERN);
|
|
251
252
|
if (sectionMatch) {
|
|
253
|
+
const sectionIndent = measureIndent(raw);
|
|
254
|
+
while (blockStack.length > 0) {
|
|
255
|
+
const top = blockStack[blockStack.length - 1];
|
|
256
|
+
if (sectionIndent > top.indent) break;
|
|
257
|
+
blockStack.pop();
|
|
258
|
+
}
|
|
252
259
|
const labelRaw = sectionMatch[1].trim();
|
|
253
260
|
const colorMatch = labelRaw.match(/^(.+?)\((\w+)\)$/);
|
|
254
261
|
const section: SequenceSection = {
|