@diagrammo/dgmo 0.31.0 → 0.32.1
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/.cursorrules +4 -1
- package/.github/copilot-instructions.md +4 -1
- package/.windsurfrules +4 -1
- package/SKILL.md +4 -1
- package/dist/advanced.cjs +1297 -358
- package/dist/advanced.d.cts +117 -15
- package/dist/advanced.d.ts +117 -15
- package/dist/advanced.js +1291 -358
- package/dist/auto.cjs +1087 -316
- package/dist/auto.js +98 -98
- package/dist/auto.mjs +1087 -316
- package/dist/cli.cjs +140 -140
- package/dist/index.cjs +1090 -397
- package/dist/index.js +1090 -397
- package/docs/ai-integration.md +4 -1
- package/docs/language-reference.md +282 -27
- package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
- package/gallery/fixtures/c4-full.dgmo +4 -5
- package/gallery/fixtures/c4.dgmo +2 -3
- package/package.json +7 -1
- package/src/advanced.ts +7 -0
- package/src/boxes-and-lines/focus.ts +257 -0
- package/src/boxes-and-lines/layout-search.ts +131 -65
- package/src/boxes-and-lines/layout.ts +7 -1
- package/src/boxes-and-lines/parser.ts +19 -4
- package/src/boxes-and-lines/renderer.ts +54 -3
- package/src/c4/parser.ts +8 -7
- package/src/chart-type-registry.ts +129 -4
- package/src/chart-types.ts +4 -4
- package/src/chart.ts +18 -1
- package/src/colors.ts +225 -2
- package/src/cycle/parser.ts +2 -7
- package/src/d3.ts +67 -54
- package/src/diagnostics.ts +17 -0
- package/src/dimensions.ts +9 -13
- package/src/echarts.ts +42 -14
- package/src/er/parser.ts +6 -1
- package/src/gantt/parser.ts +44 -7
- package/src/graph/flowchart-parser.ts +77 -3
- package/src/graph/state-renderer.ts +2 -2
- package/src/infra/parser.ts +80 -0
- package/src/journey-map/parser.ts +8 -7
- package/src/kanban/parser.ts +8 -7
- package/src/map/context-labels.ts +134 -27
- package/src/map/geo.ts +10 -2
- package/src/map/layout.ts +259 -4
- package/src/map/parser.ts +2 -0
- package/src/map/renderer.ts +22 -11
- package/src/map/resolver.ts +68 -19
- package/src/mindmap/parser.ts +15 -7
- package/src/mindmap/renderer.ts +50 -12
- package/src/org/parser.ts +8 -7
- package/src/org/renderer.ts +22 -7
- package/src/palettes/color-utils.ts +12 -2
- package/src/palettes/index.ts +1 -0
- package/src/pert/renderer.ts +2 -2
- package/src/pyramid/parser.ts +2 -7
- package/src/quadrant/renderer.ts +2 -2
- package/src/raci/parser.ts +2 -7
- package/src/raci/renderer.ts +4 -4
- package/src/ring/parser.ts +2 -7
- package/src/sequence/parser.ts +18 -7
- package/src/sequence/renderer.ts +4 -4
- package/src/sitemap/parser.ts +8 -7
- package/src/sitemap/renderer.ts +2 -2
- package/src/tech-radar/parser.ts +2 -7
- package/src/timeline/renderer.ts +15 -5
- package/src/utils/parsing.ts +13 -1
- package/src/utils/scaling.ts +38 -81
- package/src/utils/tag-groups.ts +38 -0
- package/src/visualizations/parse.ts +6 -1
- package/src/wireframe/parser.ts +6 -1
package/src/colors.ts
CHANGED
|
@@ -117,6 +117,211 @@ export function resolveColor(
|
|
|
117
117
|
import type { DgmoError } from './diagnostics';
|
|
118
118
|
import { makeDgmoError, suggest } from './diagnostics';
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Stable diagnostic code for "this token is not one of the 11 named palette
|
|
122
|
+
* colors" — covers both hex/CSS literals (emitted as `error`) and unrecognized
|
|
123
|
+
* bare words like `crimson` (emitted as `warning`). Consumers that want to
|
|
124
|
+
* HARD-BLOCK invalid colors regardless of severity (e.g. the MCP render gate)
|
|
125
|
+
* filter on this code rather than re-deriving the rule.
|
|
126
|
+
*/
|
|
127
|
+
export const INVALID_COLOR_CODE = 'E_INVALID_COLOR';
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* CSS / X11 color names that are NOT one of DGMO's 11 — mapped to their hex so
|
|
131
|
+
* a "nearest valid color" hint can be computed. This is the blocklist that lets
|
|
132
|
+
* the trailing-token rule tell an *intended-but-invalid* color (`pink`,
|
|
133
|
+
* `crimson`, `navy`) apart from an ordinary label word (`Zinfandel`, `Blanc`):
|
|
134
|
+
* a lowercase trailing token found here is flagged, anything else stays label
|
|
135
|
+
* text. Our 11 valid names are deliberately excluded. Extend freely — it only
|
|
136
|
+
* sharpens detection. (Case-sensitive lowercase, matching the §1.5 color rule.)
|
|
137
|
+
*/
|
|
138
|
+
export const INVALID_CSS_COLOR_HEX: Readonly<Record<string, string>> =
|
|
139
|
+
Object.freeze({
|
|
140
|
+
pink: '#ffc0cb',
|
|
141
|
+
hotpink: '#ff69b4',
|
|
142
|
+
deeppink: '#ff1493',
|
|
143
|
+
lightpink: '#ffb6c1',
|
|
144
|
+
palevioletred: '#db7093',
|
|
145
|
+
crimson: '#dc143c',
|
|
146
|
+
scarlet: '#ff2400',
|
|
147
|
+
firebrick: '#b22222',
|
|
148
|
+
darkred: '#8b0000',
|
|
149
|
+
maroon: '#800000',
|
|
150
|
+
salmon: '#fa8072',
|
|
151
|
+
lightsalmon: '#ffa07a',
|
|
152
|
+
darksalmon: '#e9967a',
|
|
153
|
+
coral: '#ff7f50',
|
|
154
|
+
lightcoral: '#f08080',
|
|
155
|
+
tomato: '#ff6347',
|
|
156
|
+
orangered: '#ff4500',
|
|
157
|
+
darkorange: '#ff8c00',
|
|
158
|
+
gold: '#ffd700',
|
|
159
|
+
goldenrod: '#daa520',
|
|
160
|
+
darkgoldenrod: '#b8860b',
|
|
161
|
+
khaki: '#f0e68c',
|
|
162
|
+
darkkhaki: '#bdb76b',
|
|
163
|
+
amber: '#ffbf00',
|
|
164
|
+
lavender: '#e6e6fa',
|
|
165
|
+
violet: '#ee82ee',
|
|
166
|
+
magenta: '#ff00ff',
|
|
167
|
+
fuchsia: '#ff00ff',
|
|
168
|
+
orchid: '#da70d6',
|
|
169
|
+
plum: '#dda0dd',
|
|
170
|
+
indigo: '#4b0082',
|
|
171
|
+
navy: '#000080',
|
|
172
|
+
midnightblue: '#191970',
|
|
173
|
+
darkblue: '#00008b',
|
|
174
|
+
mediumblue: '#0000cd',
|
|
175
|
+
royalblue: '#4169e1',
|
|
176
|
+
cornflowerblue: '#6495ed',
|
|
177
|
+
dodgerblue: '#1e90ff',
|
|
178
|
+
deepskyblue: '#00bfff',
|
|
179
|
+
skyblue: '#87ceeb',
|
|
180
|
+
lightskyblue: '#87cefa',
|
|
181
|
+
lightblue: '#add8e6',
|
|
182
|
+
powderblue: '#b0e0e6',
|
|
183
|
+
steelblue: '#4682b4',
|
|
184
|
+
slateblue: '#6a5acd',
|
|
185
|
+
cadetblue: '#5f9ea0',
|
|
186
|
+
turquoise: '#40e0d0',
|
|
187
|
+
aqua: '#00ffff',
|
|
188
|
+
aquamarine: '#7fffd4',
|
|
189
|
+
lime: '#00ff00',
|
|
190
|
+
limegreen: '#32cd32',
|
|
191
|
+
lightgreen: '#90ee90',
|
|
192
|
+
palegreen: '#98fb98',
|
|
193
|
+
seagreen: '#2e8b57',
|
|
194
|
+
mediumseagreen: '#3cb371',
|
|
195
|
+
forestgreen: '#228b22',
|
|
196
|
+
darkgreen: '#006400',
|
|
197
|
+
olive: '#808000',
|
|
198
|
+
olivedrab: '#6b8e23',
|
|
199
|
+
darkolivegreen: '#556b2f',
|
|
200
|
+
chartreuse: '#7fff00',
|
|
201
|
+
lawngreen: '#7cfc00',
|
|
202
|
+
springgreen: '#00ff7f',
|
|
203
|
+
greenyellow: '#adff2f',
|
|
204
|
+
brown: '#a52a2a',
|
|
205
|
+
sienna: '#a0522d',
|
|
206
|
+
chocolate: '#d2691e',
|
|
207
|
+
peru: '#cd853f',
|
|
208
|
+
tan: '#d2b48c',
|
|
209
|
+
beige: '#f5f5dc',
|
|
210
|
+
wheat: '#f5deb3',
|
|
211
|
+
ivory: '#fffff0',
|
|
212
|
+
silver: '#c0c0c0',
|
|
213
|
+
lightgray: '#d3d3d3',
|
|
214
|
+
lightgrey: '#d3d3d3',
|
|
215
|
+
darkgray: '#a9a9a9',
|
|
216
|
+
darkgrey: '#a9a9a9',
|
|
217
|
+
dimgray: '#696969',
|
|
218
|
+
dimgrey: '#696969',
|
|
219
|
+
slategray: '#708090',
|
|
220
|
+
slategrey: '#708090',
|
|
221
|
+
gainsboro: '#dcdcdc',
|
|
222
|
+
grey: '#808080',
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Best-effort nearest recognized color NAME for an unsupported hex value.
|
|
227
|
+
* Matches by HUE (with a low-saturation cutoff routing to black/white/gray),
|
|
228
|
+
* NOT raw RGB distance to the muted palette hexes — vivid LLM colors like a
|
|
229
|
+
* `#3cb44b` green would otherwise snap to `gray` against a desaturated sage.
|
|
230
|
+
* Returns null for non-hex input (CSS function/keyword colors, no RGB to read).
|
|
231
|
+
* Used ONLY to enrich a diagnostic — never to silently accept the value.
|
|
232
|
+
*/
|
|
233
|
+
export function nearestNamedColor(input: string): string | null {
|
|
234
|
+
// CSS color name → resolve to its hex first so hue-matching works on it too.
|
|
235
|
+
const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
|
|
236
|
+
if (cssHex) input = cssHex;
|
|
237
|
+
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
|
|
238
|
+
if (!m) return null;
|
|
239
|
+
let h = m[1]!.toLowerCase();
|
|
240
|
+
if (h.length === 3)
|
|
241
|
+
h = h
|
|
242
|
+
.split('')
|
|
243
|
+
.map((c) => c + c)
|
|
244
|
+
.join('');
|
|
245
|
+
const r = parseInt(h.slice(0, 2), 16) / 255;
|
|
246
|
+
const g = parseInt(h.slice(2, 4), 16) / 255;
|
|
247
|
+
const b = parseInt(h.slice(4, 6), 16) / 255;
|
|
248
|
+
const max = Math.max(r, g, b);
|
|
249
|
+
const min = Math.min(r, g, b);
|
|
250
|
+
const delta = max - min;
|
|
251
|
+
const l = (max + min) / 2;
|
|
252
|
+
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
253
|
+
// Low chroma → a neutral; pick by lightness.
|
|
254
|
+
if (s < 0.15) {
|
|
255
|
+
if (l < 0.2) return 'black';
|
|
256
|
+
if (l > 0.85) return 'white';
|
|
257
|
+
return 'gray';
|
|
258
|
+
}
|
|
259
|
+
let hue: number;
|
|
260
|
+
if (max === r) hue = 60 * (((g - b) / delta) % 6);
|
|
261
|
+
else if (max === g) hue = 60 * ((b - r) / delta + 2);
|
|
262
|
+
else hue = 60 * ((r - g) / delta + 4);
|
|
263
|
+
if (hue < 0) hue += 360;
|
|
264
|
+
// Canonical hue anchors for the 8 chromatic names (red appears at both 0 and
|
|
265
|
+
// 360 so wrap-around resolves correctly).
|
|
266
|
+
const anchors: readonly [string, number][] = [
|
|
267
|
+
['red', 0],
|
|
268
|
+
['orange', 30],
|
|
269
|
+
['yellow', 55],
|
|
270
|
+
['green', 120],
|
|
271
|
+
['teal', 170],
|
|
272
|
+
['cyan', 190],
|
|
273
|
+
['blue', 225],
|
|
274
|
+
['purple', 285],
|
|
275
|
+
['red', 360],
|
|
276
|
+
];
|
|
277
|
+
let best = 'red';
|
|
278
|
+
let bestD = Infinity;
|
|
279
|
+
for (const [name, deg] of anchors) {
|
|
280
|
+
const d = Math.abs(hue - deg);
|
|
281
|
+
if (d < bestD) {
|
|
282
|
+
bestD = d;
|
|
283
|
+
best = name;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return best;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* True iff `token` is an INTENDED-but-invalid color: a hex/`rgb()`/`hsl()`
|
|
291
|
+
* literal, or a known CSS color name that isn't one of DGMO's 11. Lets the
|
|
292
|
+
* trailing-token rule flag `Rosé pink` / `Foo #e6194b` while leaving genuine
|
|
293
|
+
* label words (`Zinfandel`) untouched.
|
|
294
|
+
*/
|
|
295
|
+
export function isInvalidColorToken(token: string): boolean {
|
|
296
|
+
if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
|
|
297
|
+
const lower = token.toLowerCase();
|
|
298
|
+
return (
|
|
299
|
+
INVALID_CSS_COLOR_HEX[lower] !== undefined && !isRecognizedColorName(lower)
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Build an `E_INVALID_COLOR` diagnostic for an intended-but-invalid trailing
|
|
305
|
+
* color token, or return null if `token` isn't color-like (so it stays label
|
|
306
|
+
* text). Severity is `warning` so the library degrades gracefully (the value
|
|
307
|
+
* just keeps the word); the MCP render gate blocks on the code regardless.
|
|
308
|
+
* Used by `extractColor` to close the trailing-token "silent swallow" gap.
|
|
309
|
+
*/
|
|
310
|
+
export function invalidColorDiagnostic(
|
|
311
|
+
token: string,
|
|
312
|
+
line: number
|
|
313
|
+
): DgmoError | null {
|
|
314
|
+
if (!isInvalidColorToken(token)) return null;
|
|
315
|
+
const nearest = nearestNamedColor(token);
|
|
316
|
+
const near = nearest ? ` Nearest: ${nearest}.` : '';
|
|
317
|
+
return makeDgmoError(
|
|
318
|
+
line,
|
|
319
|
+
`Color "${token}" is not a valid DGMO color — DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(', ')} (no hex, no CSS color names).${near}`,
|
|
320
|
+
'warning',
|
|
321
|
+
INVALID_COLOR_CODE
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
120
325
|
/**
|
|
121
326
|
* Resolves a color name and pushes a warning diagnostic on failure.
|
|
122
327
|
* Returns the hex string for valid names, or `undefined` for unknown
|
|
@@ -131,13 +336,31 @@ export function resolveColorWithDiagnostic(
|
|
|
131
336
|
): string | undefined {
|
|
132
337
|
const resolved = resolveColor(color, palette);
|
|
133
338
|
if (resolved !== null) return resolved;
|
|
339
|
+
// Literal color values (hex `#e6194b`, `rgb(...)`, `hsl(...)`) are NOT part
|
|
340
|
+
// of the DGMO language — only the 11 named palette colors are accepted, so
|
|
341
|
+
// that a single source recolors correctly across every palette and theme.
|
|
342
|
+
// Flag these with a precise error rather than a typo suggestion.
|
|
343
|
+
if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
|
|
344
|
+
const nearest = nearestNamedColor(color);
|
|
345
|
+
const near = nearest ? ` Nearest: ${nearest}.` : '';
|
|
346
|
+
diagnostics.push(
|
|
347
|
+
makeDgmoError(
|
|
348
|
+
line,
|
|
349
|
+
`Color "${color}" is not supported — DGMO does not accept hex or CSS color values. Use a named palette color: ${RECOGNIZED_COLOR_NAMES.join(', ')}.${near}`,
|
|
350
|
+
'error',
|
|
351
|
+
INVALID_COLOR_CODE
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
134
356
|
const hint = suggest(color, RECOGNIZED_COLOR_NAMES as readonly string[]);
|
|
135
357
|
const suggestion = hint ? ` ${hint}` : '';
|
|
136
358
|
diagnostics.push(
|
|
137
359
|
makeDgmoError(
|
|
138
360
|
line,
|
|
139
|
-
`Unknown color "${color}".
|
|
140
|
-
'warning'
|
|
361
|
+
`Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(', ')} (no hex, no CSS color names).${suggestion}`,
|
|
362
|
+
'warning',
|
|
363
|
+
INVALID_COLOR_CODE
|
|
141
364
|
)
|
|
142
365
|
);
|
|
143
366
|
return undefined;
|
package/src/cycle/parser.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
formatDgmoError,
|
|
7
6
|
makeDgmoError,
|
|
7
|
+
makeFail,
|
|
8
8
|
METADATA_DIAGNOSTIC_CODES,
|
|
9
9
|
pipeOperatorRemovedMessage,
|
|
10
10
|
} from '../diagnostics';
|
|
@@ -64,12 +64,7 @@ export function parseCycle(content: string): ParsedCycle {
|
|
|
64
64
|
let currentEdge: Writable<CycleEdge> | null = null;
|
|
65
65
|
// nodeBaseIndent tracking removed — indent-based nesting not used in cycle
|
|
66
66
|
|
|
67
|
-
const fail = (
|
|
68
|
-
const diag = makeDgmoError(line, message);
|
|
69
|
-
result.diagnostics.push(diag);
|
|
70
|
-
result.error = formatDgmoError(diag);
|
|
71
|
-
return result;
|
|
72
|
-
};
|
|
67
|
+
const fail = makeFail(result);
|
|
73
68
|
|
|
74
69
|
const warn = (
|
|
75
70
|
line: number,
|