@diagrammo/dgmo 0.36.0 → 0.38.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/.cursorrules +3 -0
- package/.github/copilot-instructions.md +3 -0
- package/.windsurfrules +3 -0
- package/SKILL.md +3 -0
- package/dist/advanced.cjs +1058 -135
- package/dist/advanced.d.cts +122 -8
- package/dist/advanced.d.ts +122 -8
- package/dist/advanced.js +1055 -133
- package/dist/auto.cjs +1047 -132
- package/dist/auto.js +96 -95
- package/dist/auto.mjs +1048 -130
- package/dist/cli.cjs +117 -116
- package/dist/editor.cjs +4 -1
- package/dist/editor.js +4 -1
- package/dist/highlight.cjs +3 -0
- package/dist/highlight.js +3 -0
- package/dist/index.cjs +1043 -131
- package/dist/index.js +1044 -129
- package/docs/ai-integration.md +3 -0
- package/docs/language-reference.md +106 -7
- package/gallery/fixtures/map-coastline.dgmo +2 -2
- package/gallery/fixtures/map-route.dgmo +5 -5
- package/gallery/fixtures/treemap-branch.dgmo +15 -0
- package/gallery/fixtures/treemap-heat.dgmo +12 -0
- package/gallery/fixtures/treemap-tag-heat.dgmo +21 -0
- package/gallery/fixtures/treemap-tags.dgmo +18 -0
- package/package.json +1 -1
- package/src/advanced.ts +11 -0
- package/src/boxes-and-lines/layout-search.ts +19 -1
- package/src/chart-type-registry.ts +4 -0
- package/src/chart-types.ts +5 -0
- package/src/completion.ts +64 -0
- package/src/d3.ts +30 -18
- package/src/editor/highlight-api.ts +2 -0
- package/src/editor/keywords.ts +3 -0
- package/src/infra/parser.ts +95 -38
- package/src/map/dimensions.ts +19 -2
- package/src/map/layout.ts +91 -7
- package/src/map/parser.ts +51 -29
- package/src/map/renderer.ts +10 -0
- package/src/map/types.ts +9 -8
- package/src/treemap/layout.ts +189 -0
- package/src/treemap/parser.ts +475 -0
- package/src/treemap/renderer.ts +698 -0
- package/src/treemap/types.ts +57 -0
- package/src/utils/parsing.ts +1 -0
- package/src/utils/reserved-key-registry.ts +5 -0
package/docs/ai-integration.md
CHANGED
|
@@ -143,6 +143,7 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
143
143
|
| `quadrant` | 2×2 positioning matrix |
|
|
144
144
|
| `pyramid` | stacked hierarchy of layers (Maslow, DIKW) |
|
|
145
145
|
| `ring` | concentric rings of nested categories |
|
|
146
|
+
| `treemap` | nested rectangles sized by value (budgets, disk usage, portfolios) |
|
|
146
147
|
| `map` | geographic concept map: regions, points, routes |
|
|
147
148
|
| `wireframe` | low-fidelity UI layout with panels and controls |
|
|
148
149
|
| `bar` | categorical comparisons |
|
|
@@ -167,6 +168,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
167
168
|
|
|
168
169
|
**Need more than the index gives you?** Fetch the per-type section: MCP `get_language_reference(type)` / `get_examples(type)`, or read that type's section below. The `suggest_chart_type` tool returns the chosen type's section automatically.
|
|
169
170
|
|
|
171
|
+
**When the type isn't obvious, ask — don't guess.** Call `suggest_chart_type` first. If it returns an `⚠️ ASK THE USER` directive (the request is ambiguous between candidates, or nothing matched), present those candidate options to the user and wait for their pick before generating — never silently choose. A confident result (high/medium) you can proceed with.
|
|
172
|
+
|
|
170
173
|
### Common examples (curated, parse-clean)
|
|
171
174
|
|
|
172
175
|
_The most common types, inline so you can generate them without a fetch. For the other 37, get the per-type section (see below)._
|
|
@@ -129,6 +129,7 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
129
129
|
| `quadrant` | 2×2 positioning matrix |
|
|
130
130
|
| `pyramid` | stacked hierarchy of layers (Maslow, DIKW) |
|
|
131
131
|
| `ring` | concentric rings of nested categories |
|
|
132
|
+
| `treemap` | nested rectangles sized by value (budgets, disk usage, portfolios) |
|
|
132
133
|
| `map` | geographic concept map: regions, points, routes |
|
|
133
134
|
| `wireframe` | low-fidelity UI layout with panels and controls |
|
|
134
135
|
| `bar` | categorical comparisons |
|
|
@@ -152,6 +153,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
152
153
|
| `function` | mathematical expressions (colon required: `f(x): x^2`) |
|
|
153
154
|
|
|
154
155
|
**Need more than the index gives you?** Fetch the per-type section: MCP `get_language_reference(type)` / `get_examples(type)`, or read that type's section below. The `suggest_chart_type` tool returns the chosen type's section automatically.
|
|
156
|
+
|
|
157
|
+
**When the type isn't obvious, ask — don't guess.** Call `suggest_chart_type` first. If it returns an `⚠️ ASK THE USER` directive (the request is ambiguous between candidates, or nothing matched), present those candidate options to the user and wait for their pick before generating — never silently choose. A confident result (high/medium) you can proceed with.
|
|
155
158
|
<!-- AI-CORE:TYPE-INDEX end -->
|
|
156
159
|
|
|
157
160
|
<!-- The grouped data-chart / matrix ids share one documented section. This map is the single source of truth for which TYPE block each id resolves to (read by gen-ai-core.mjs and the MCP slicer). -->
|
|
@@ -3087,6 +3090,100 @@ When ring band thickness would force the in-band label below the readable floor
|
|
|
3087
3090
|
|
|
3088
3091
|
---
|
|
3089
3092
|
|
|
3093
|
+
## 24C. Treemaps
|
|
3094
|
+
|
|
3095
|
+
<!-- TYPE:treemap -->
|
|
3096
|
+
|
|
3097
|
+
<!-- TIPS start -->
|
|
3098
|
+
**Styling tips:** Put the unit in the title (`Cloud Spend ($)`) — there is no currency/format directive. Reach for `depth N` once a tree goes past ~3 levels (deeper subtrees collapse to a drillable block) and `other-below N` to fold a long tail of tiny categories into one **Other** box. Pick the color mode to match intent: tags for categories, `heat` for a gain/loss heatmap, branch for zero-config structure.
|
|
3099
|
+
<!-- TIPS end -->
|
|
3100
|
+
|
|
3101
|
+
Nested rectangles sized by value — the canonical way to show a hierarchy's proportions at a glance (budgets, disk usage, portfolios, taxonomies). Indentation is the hierarchy; a bare trailing number on a leaf is its size; parents auto-sum their children. Built on a squarified layout so cells keep good aspect ratios.
|
|
3102
|
+
|
|
3103
|
+
### Declaration
|
|
3104
|
+
|
|
3105
|
+
```
|
|
3106
|
+
treemap [Title with units, e.g. "Cloud Spend ($)"]
|
|
3107
|
+
|
|
3108
|
+
Branch
|
|
3109
|
+
Leaf 320
|
|
3110
|
+
Leaf 180
|
|
3111
|
+
```
|
|
3112
|
+
|
|
3113
|
+
### Example
|
|
3114
|
+
|
|
3115
|
+
```
|
|
3116
|
+
treemap Q3 Budget
|
|
3117
|
+
|
|
3118
|
+
tag Team as t
|
|
3119
|
+
Eng blue
|
|
3120
|
+
Sales green
|
|
3121
|
+
Ops orange
|
|
3122
|
+
|
|
3123
|
+
Engineering t: Eng
|
|
3124
|
+
Platform 320
|
|
3125
|
+
Mobile 180
|
|
3126
|
+
Data 140
|
|
3127
|
+
Go-to-Market t: Sales
|
|
3128
|
+
Ads 90
|
|
3129
|
+
Field Sales 130
|
|
3130
|
+
Operations t: Ops
|
|
3131
|
+
Cloud 110
|
|
3132
|
+
Support 70
|
|
3133
|
+
```
|
|
3134
|
+
|
|
3135
|
+
### Value & hierarchy
|
|
3136
|
+
|
|
3137
|
+
- **Indentation** sets the hierarchy (same model as mindmap / org).
|
|
3138
|
+
- A **bare trailing number** on a leaf is its size (`Platform 320`) — the funnel/sankey idiom (§1.5). No thousands commas (`1_000` is fine).
|
|
3139
|
+
- **Parents auto-sum** their descendants; a trailing number on a branch line is ignored with a warning (auto-sum always wins).
|
|
3140
|
+
- To keep a label that genuinely ends in a digit, **quote it** (`"Region 5"`); unquoted `Region 5` parses as label `Region`, value `5`.
|
|
3141
|
+
|
|
3142
|
+
### Color modes
|
|
3143
|
+
|
|
3144
|
+
Three modes, with the source-declared default selected as **tag → heat → branch** (the desktop app adds a runtime switcher that previews the others without editing source):
|
|
3145
|
+
|
|
3146
|
+
| Mode | When it's the default | Coloring |
|
|
3147
|
+
| ---------- | --------------------------- | ------------------------------------------------------------ |
|
|
3148
|
+
| **tag** | a `tag` group is declared | categorical color per tag value (legend hover-dims) |
|
|
3149
|
+
| **heat** | any `heat:` value / `heat` directive | value ramp over a second per-node `heat:` metric |
|
|
3150
|
+
| **branch** | neither tags nor heat | each top-level branch a distinct hue, tinted with depth |
|
|
3151
|
+
|
|
3152
|
+
### Tags
|
|
3153
|
+
|
|
3154
|
+
Declare a `tag` group before content and apply it inline (`Engineering t: Eng`); children inherit the tag unless they override it. See §1.3.
|
|
3155
|
+
|
|
3156
|
+
### Heat metric & ramp
|
|
3157
|
+
|
|
3158
|
+
Add a per-node `heat:` number (a second metric, distinct from size; negatives/floats ok) and name the ramp with a `heat <Label> [low] [high]` directive. Colors are optional and **data-aware**: data that crosses zero defaults to a diverging `red·neutral·green` ramp with the midpoint pinned at 0; one-sign data defaults to a sequential `neutral·accent` ramp. One explicit color = `neutral→hue`; two = `low → high` (with a neutral midpoint). **Named palette colors only — no hex.** `heat:` is used instead of `score:` (which journey-map reserves for 1–5 emotion, §22).
|
|
3159
|
+
|
|
3160
|
+
### Node Metadata
|
|
3161
|
+
|
|
3162
|
+
| Key | Type | Default | Description |
|
|
3163
|
+
| ------- | --------------- | ------- | -------------------------------------------- |
|
|
3164
|
+
| `heat` | number | — | Color-by-value metric (signed / float ok) |
|
|
3165
|
+
| `<tag>` | declared value | — | Tag value via the group's alias or name |
|
|
3166
|
+
|
|
3167
|
+
### Directives
|
|
3168
|
+
|
|
3169
|
+
| Directive | Effect |
|
|
3170
|
+
| --------------- | --------------------------------------------------------------------------------------- |
|
|
3171
|
+
| `heat <Label>` | Name (and optionally color) the value ramp; pairs with the `heat:` key. |
|
|
3172
|
+
| `depth N` | Render N levels; deeper subtrees collapse to a drillable solid block (a render budget). |
|
|
3173
|
+
| `other-below N` | Roll children under N% of their parent into a single hatched **Other** bucket (opt-in). |
|
|
3174
|
+
| `no-values` | Hide value labels. |
|
|
3175
|
+
| `no-percent` | Hide percentage labels. |
|
|
3176
|
+
| `no-headers` | Hide parent header bars. |
|
|
3177
|
+
| `no-legend` | Hide the legend. |
|
|
3178
|
+
|
|
3179
|
+
Numbers auto-compact (1.2M, 940k). Units live in the title — there is no format/currency directive.
|
|
3180
|
+
|
|
3181
|
+
### Interactivity vs export
|
|
3182
|
+
|
|
3183
|
+
In the desktop app a treemap is interactive: click a parent to drill in (with a breadcrumb), hover for a tooltip (path / value / % / heat), and flip the color mode. Static SVG / PNG export is the clean, full tree with none of that chrome — the same interactive-vs-export split the map chart uses.
|
|
3184
|
+
|
|
3185
|
+
---
|
|
3186
|
+
|
|
3090
3187
|
## 24A. RACI Matrices (RACI / RASCI / DACI)
|
|
3091
3188
|
|
|
3092
3189
|
<!-- TYPE:raci -->
|
|
@@ -3264,15 +3361,17 @@ poi Austin red # direct marker color (trailing token, §1.5
|
|
|
3264
3361
|
|
|
3265
3362
|
### Routes & connectors
|
|
3266
3363
|
|
|
3267
|
-
`route <origin
|
|
3364
|
+
`route <origin>` — an ordered, auto-numbered voyage; the origin gets a distinct marker, and the header takes **no shape option**. Each indented line is a `<arrow> destination` leg that continues from the previous stop, using the same indented arrow idiom as a sitemap — a leg is an edge (in-arrow label, `value:` thickness, and the **arrow glyph alone sets shape**: `-…->` straight, `~…~>` arc, mixable per leg). The **arrow is required and must be directional** — a bare destination errors, and so does an undirected `--`/`~~` glyph (a voyage always flows forward; use `->`/`~>`). A **tag on a leg colours the LINE** (categorical leg-type colouring — flights vs cruises vs trains); `label:`/`as` still name the destination stop. Repeat the origin as a leg's destination to close a loop (no second marker):
|
|
3268
3365
|
|
|
3269
3366
|
```
|
|
3270
|
-
route Miami
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3367
|
+
route Miami
|
|
3368
|
+
~weigh anchor~> Havana value: 40 # arc leg (its own ~> glyph)
|
|
3369
|
+
~> Kingston
|
|
3370
|
+
~> Miami # destination == origin → closed loop
|
|
3274
3371
|
```
|
|
3275
3372
|
|
|
3373
|
+
There is no header `style:` — bow a whole voyage by using `~>` on every leg.
|
|
3374
|
+
|
|
3276
3375
|
Native edges handle any other connection (no `link`/`leg` keyword). A token draws an arrowhead iff it ends in `>`, an arc iff it starts with `~`; drop the `>` for a plain line when an arrow would mislead. The label always sits between the delimiters:
|
|
3277
3376
|
|
|
3278
3377
|
| | no label | labeled |
|
|
@@ -3292,9 +3391,9 @@ JFK ~daily~> LHR # (NOT indented — indented legs are only valid
|
|
|
3292
3391
|
JFK ~2x daily~> SFO # inside a `route` block, which is an ordered voyage)
|
|
3293
3392
|
```
|
|
3294
3393
|
|
|
3295
|
-
Each native edge is ONE full line — `<origin> <connector> <destination>`.
|
|
3394
|
+
Each native edge is ONE full line — `<origin> <connector> <destination>`. A top-level indented `-> dest` with no parent errors as "Malformed edge"; to fan out, either repeat the origin on each line as above OR indent the spokes under a `poi` (a hub — `poi JFK` then indented `~> LAX`, `~> LHR`). An indented child under a `poi` that names a place **must carry an arrow** — a bare name there errors as a malformed hub edge. Endpoints auto-create their POIs, so a connected map needs no separate `poi` lines.
|
|
3296
3395
|
|
|
3297
|
-
There is no geographic path-finding and no `surface:` — legs are plain straight or arced geometry (
|
|
3396
|
+
There is no geographic path-finding and no `surface:` — legs are plain straight or arced geometry (use a `~>` glyph to bow one) and may cross land.
|
|
3298
3397
|
|
|
3299
3398
|
**Tagging the line.** A tag value on any connector or route leg colours the **line itself** (the universal tag model applied to edges) — the "trip-leg type" idiom. Edge-only tag groups show in the legend as a line-colour key; hovering a legend entry dims the lines that don't match. To categorise a **stop** instead, tag its own `poi` line. An edge-only tag group never tints regions or suppresses the colorize dress.
|
|
3300
3399
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
treemap Engineering Portfolio (effort, pts)
|
|
2
|
+
|
|
3
|
+
tag Squad as sq
|
|
4
|
+
Platform blue
|
|
5
|
+
Product green
|
|
6
|
+
Infra orange
|
|
7
|
+
|
|
8
|
+
heat Δ vs plan %
|
|
9
|
+
|
|
10
|
+
Platform sq: Platform
|
|
11
|
+
API Gateway 120 heat: 4.0
|
|
12
|
+
Auth 80 heat: -6.5
|
|
13
|
+
Service Mesh 60 heat: 1.2
|
|
14
|
+
Product sq: Product
|
|
15
|
+
Onboarding 140 heat: 8.1
|
|
16
|
+
Checkout 110 heat: -3.4
|
|
17
|
+
Search 70 heat: 0.5
|
|
18
|
+
Infra sq: Infra
|
|
19
|
+
CI Pipeline 90 heat: -1.8
|
|
20
|
+
Observability 75 heat: 5.6
|
|
21
|
+
Cost Controls 50 heat: 2.3
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
treemap Q3 Budget
|
|
2
|
+
|
|
3
|
+
tag Team as t
|
|
4
|
+
Eng blue
|
|
5
|
+
Sales green
|
|
6
|
+
Ops orange
|
|
7
|
+
|
|
8
|
+
Engineering t: Eng
|
|
9
|
+
Platform 320
|
|
10
|
+
Mobile 180
|
|
11
|
+
Data 140
|
|
12
|
+
Go-to-Market t: Sales
|
|
13
|
+
Ads 90
|
|
14
|
+
Field Sales 130
|
|
15
|
+
Events 40
|
|
16
|
+
Operations t: Ops
|
|
17
|
+
Cloud 110
|
|
18
|
+
Support 70
|
package/package.json
CHANGED
package/src/advanced.ts
CHANGED
|
@@ -507,6 +507,17 @@ export { parseRing } from './ring/parser';
|
|
|
507
507
|
export { renderRing, renderRingForExport } from './ring/renderer';
|
|
508
508
|
export type { ParsedRing, RingLayer } from './ring/types';
|
|
509
509
|
|
|
510
|
+
export { parseTreemap } from './treemap/parser';
|
|
511
|
+
export { renderTreemap, renderTreemapForExport } from './treemap/renderer';
|
|
512
|
+
export { layoutTreemap } from './treemap/layout';
|
|
513
|
+
export type { TreemapCell, TreemapLayoutResult } from './treemap/layout';
|
|
514
|
+
export type {
|
|
515
|
+
ParsedTreemap,
|
|
516
|
+
TreemapNode,
|
|
517
|
+
TreemapColorMode,
|
|
518
|
+
TreemapOptions,
|
|
519
|
+
} from './treemap/types';
|
|
520
|
+
|
|
510
521
|
// Map (§24B) — the interactive render surface for app/editor consumers. The
|
|
511
522
|
// Node fs `loadMapData` is exported for completeness, but the browser supplies
|
|
512
523
|
// `MapData` by DI (resolveMap/renderMap take it as an argument).
|
|
@@ -933,6 +933,17 @@ export async function layoutBoxesAndLinesSearch(
|
|
|
933
933
|
const tick = onProgress
|
|
934
934
|
? (): Promise<void> => new Promise<void>((r) => setTimeout(r))
|
|
935
935
|
: (): undefined => undefined;
|
|
936
|
+
// Yielding is only worth its cost once a search is genuinely slow. A typical
|
|
937
|
+
// diagram finishes its whole candidate pool in tens of ms; yielding (a
|
|
938
|
+
// setTimeout, clamped to ~4ms/turn in browsers) after every one of ~90
|
|
939
|
+
// candidates would turn a 30ms layout into ~1s — all to flash a throbber that
|
|
940
|
+
// never shows (the app only reveals it after 150ms). So we suppress yields
|
|
941
|
+
// until the search has burned through a budget, then throttle them: fast
|
|
942
|
+
// diagrams run flat-out, only truly heavy ones cede the main thread to paint.
|
|
943
|
+
const YIELD_AFTER_MS = 150;
|
|
944
|
+
const YIELD_EVERY_MS = 30;
|
|
945
|
+
const searchStart = performance.now();
|
|
946
|
+
let lastYield = searchStart;
|
|
936
947
|
|
|
937
948
|
// collapsed group labels (shown as plain boxes) — mirrors the ELK path
|
|
938
949
|
const collapsedGroupLabels = new Set<string>();
|
|
@@ -1514,7 +1525,14 @@ export async function layoutBoxesAndLinesSearch(
|
|
|
1514
1525
|
const step = async (phase: string): Promise<void> => {
|
|
1515
1526
|
if (!onProgress) return;
|
|
1516
1527
|
onProgress(++progressDone, progressTotal, phase);
|
|
1517
|
-
|
|
1528
|
+
const now = performance.now();
|
|
1529
|
+
if (
|
|
1530
|
+
now - searchStart > YIELD_AFTER_MS &&
|
|
1531
|
+
now - lastYield > YIELD_EVERY_MS
|
|
1532
|
+
) {
|
|
1533
|
+
lastYield = now;
|
|
1534
|
+
await tick();
|
|
1535
|
+
}
|
|
1518
1536
|
};
|
|
1519
1537
|
|
|
1520
1538
|
// Build the candidate pool.
|
|
@@ -66,6 +66,7 @@ import { parseCycle } from './cycle/parser';
|
|
|
66
66
|
import { parseJourneyMap } from './journey-map/parser';
|
|
67
67
|
import { parsePyramid } from './pyramid/parser';
|
|
68
68
|
import { parseRing } from './ring/parser';
|
|
69
|
+
import { parseTreemap } from './treemap/parser';
|
|
69
70
|
import { parseRaci, allTasks } from './raci/parser';
|
|
70
71
|
import type { DgmoError } from './diagnostics';
|
|
71
72
|
|
|
@@ -451,6 +452,9 @@ export const CHART_TYPE_REGISTRY: readonly ChartTypeDescriptor[] = [
|
|
|
451
452
|
{ id: 'cycle', category: 'visualization', parse: parseCycle },
|
|
452
453
|
{ id: 'pyramid', category: 'visualization', parse: parsePyramid },
|
|
453
454
|
{ id: 'ring', category: 'visualization', parse: parseRing },
|
|
455
|
+
// Treemap: squarified hierarchy. No measure/minDims — a treemap fills whatever
|
|
456
|
+
// rectangle it's given and has no intrinsic aspect/size (F11).
|
|
457
|
+
{ id: 'treemap', category: 'visualization', parse: parseTreemap },
|
|
454
458
|
|
|
455
459
|
// ── Geographic map (own parser → resolver → layout → renderer) ──
|
|
456
460
|
{ id: 'map', category: 'visualization', parse: parseMap },
|
package/src/chart-types.ts
CHANGED
|
@@ -122,6 +122,11 @@ export const chartTypes: readonly ChartTypeMeta[] = [
|
|
|
122
122
|
description:
|
|
123
123
|
'Concentric rings showing nested or hierarchical categories (read core-out)',
|
|
124
124
|
},
|
|
125
|
+
{
|
|
126
|
+
id: 'treemap',
|
|
127
|
+
description:
|
|
128
|
+
'Nested rectangles sized by value — show a hierarchy’s proportions (budgets, disk usage, portfolios) with color by category, value heatmap, or branch',
|
|
129
|
+
},
|
|
125
130
|
{
|
|
126
131
|
id: 'map',
|
|
127
132
|
description:
|
package/src/completion.ts
CHANGED
|
@@ -521,6 +521,24 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
521
521
|
// layer pipe-metadata, not directives — they live in PIPE_METADATA.
|
|
522
522
|
withGlobals({}),
|
|
523
523
|
],
|
|
524
|
+
[
|
|
525
|
+
'treemap',
|
|
526
|
+
// Hierarchy sized by a bare trailing number; color modes + opt-outs.
|
|
527
|
+
withGlobals({
|
|
528
|
+
heat: {
|
|
529
|
+
description:
|
|
530
|
+
'Name the color-by-value ramp (`heat <Label> [low] [high]`); pairs with the per-node `heat:` key',
|
|
531
|
+
},
|
|
532
|
+
depth: {
|
|
533
|
+
description:
|
|
534
|
+
'Render N levels; deeper subtrees collapse to a drillable block',
|
|
535
|
+
},
|
|
536
|
+
'no-values': { description: 'Hide value labels' },
|
|
537
|
+
'no-percent': { description: 'Hide percentage labels' },
|
|
538
|
+
'no-headers': { description: 'Hide parent header bars' },
|
|
539
|
+
'no-legend': { description: 'Hide the legend' },
|
|
540
|
+
}),
|
|
541
|
+
],
|
|
524
542
|
[
|
|
525
543
|
'map',
|
|
526
544
|
// Geographic map directives (§24B.2/.7). Cosmetics are ON by default — the
|
|
@@ -695,6 +713,7 @@ export const STRUCTURAL_KEYWORDS = new Map<string, string[]>([
|
|
|
695
713
|
['infra', ['tag']],
|
|
696
714
|
['pert', ['tag']],
|
|
697
715
|
['mindmap', ['tag']],
|
|
716
|
+
['treemap', ['tag']],
|
|
698
717
|
['boxes-and-lines', ['tag']],
|
|
699
718
|
['er', ['tag']],
|
|
700
719
|
['cycle', ['direction-counterclockwise', 'circle-nodes']],
|
|
@@ -1828,6 +1847,50 @@ function extractMindmapSymbols(docText: string): DiagramSymbols {
|
|
|
1828
1847
|
return { kind: 'mindmap', entities };
|
|
1829
1848
|
}
|
|
1830
1849
|
|
|
1850
|
+
// ============================================================
|
|
1851
|
+
// Treemap extractor
|
|
1852
|
+
// ============================================================
|
|
1853
|
+
|
|
1854
|
+
function extractTreemapSymbols(docText: string): DiagramSymbols {
|
|
1855
|
+
const lines = docText.split('\n');
|
|
1856
|
+
const entities: string[] = [];
|
|
1857
|
+
let pastFirstLine = false;
|
|
1858
|
+
let inTagBlock = false;
|
|
1859
|
+
|
|
1860
|
+
for (const line of lines) {
|
|
1861
|
+
const trimmed = line.trim();
|
|
1862
|
+
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
1863
|
+
|
|
1864
|
+
if (!pastFirstLine) {
|
|
1865
|
+
pastFirstLine = true;
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// Directives and tag blocks are not node entities.
|
|
1870
|
+
if (
|
|
1871
|
+
/^(depth|heat|no-[a-z]+)\s/i.test(trimmed) ||
|
|
1872
|
+
/^no-[a-z]+$/i.test(trimmed)
|
|
1873
|
+
)
|
|
1874
|
+
continue;
|
|
1875
|
+
if (/^tag\s+/i.test(trimmed)) {
|
|
1876
|
+
inTagBlock = true;
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
const indent = line.search(/\S/);
|
|
1880
|
+
if (inTagBlock) {
|
|
1881
|
+
if (indent > 0) continue;
|
|
1882
|
+
inTagBlock = false;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// Node name: strip same-line metadata + the bare trailing value number.
|
|
1886
|
+
let label = trimmed.split(/\s+\w+:/)[0]!.trim();
|
|
1887
|
+
label = label.replace(/\s+-?\d[\d_,.]*$/, '').trim();
|
|
1888
|
+
if (label && !entities.includes(label)) entities.push(label);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
return { kind: 'treemap', entities };
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1831
1894
|
// ============================================================
|
|
1832
1895
|
// Pyramid extractor
|
|
1833
1896
|
// ============================================================
|
|
@@ -2311,6 +2374,7 @@ registerExtractor('daci', extractRaciSymbols);
|
|
|
2311
2374
|
registerExtractor('org', extractOrgSymbols);
|
|
2312
2375
|
registerExtractor('kanban', extractKanbanSymbols);
|
|
2313
2376
|
registerExtractor('mindmap', extractMindmapSymbols);
|
|
2377
|
+
registerExtractor('treemap', extractTreemapSymbols);
|
|
2314
2378
|
registerExtractor('pyramid', extractPyramidSymbols);
|
|
2315
2379
|
registerExtractor('ring', extractRingSymbols);
|
|
2316
2380
|
registerExtractor('arc', extractArcSymbols);
|
package/src/d3.ts
CHANGED
|
@@ -117,6 +117,7 @@ export const DIAGRAM_EXPORT_HANDLERS: Record<string, DiagramExportHandler> = {
|
|
|
117
117
|
map: exportMap,
|
|
118
118
|
pyramid: exportPyramid,
|
|
119
119
|
ring: exportRing,
|
|
120
|
+
treemap: exportTreemap,
|
|
120
121
|
raci: exportRaci,
|
|
121
122
|
rasci: exportRaci,
|
|
122
123
|
daci: exportRaci,
|
|
@@ -962,12 +963,33 @@ async function exportRing(ctx: ExportContext): Promise<string> {
|
|
|
962
963
|
if (ringParsed.error || ringParsed.layers.length === 0) return '';
|
|
963
964
|
|
|
964
965
|
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
965
|
-
renderRingForExport(
|
|
966
|
+
renderRingForExport(container, ringParsed, effectivePalette, ctx.isDark, {
|
|
967
|
+
width: EXPORT_WIDTH,
|
|
968
|
+
height: EXPORT_HEIGHT,
|
|
969
|
+
});
|
|
970
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
async function exportTreemap(ctx: ExportContext): Promise<string> {
|
|
974
|
+
const { content, theme, palette } = ctx;
|
|
975
|
+
const { parseTreemap } = await import('./treemap/parser');
|
|
976
|
+
const { renderTreemapForExport } = await import('./treemap/renderer');
|
|
977
|
+
|
|
978
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
979
|
+
const treemapParsed = parseTreemap(content, effectivePalette);
|
|
980
|
+
if (treemapParsed.error || treemapParsed.roots.length === 0) return '';
|
|
981
|
+
|
|
982
|
+
// Headless export: full tree (no depth window), source-declared color mode.
|
|
983
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
984
|
+
renderTreemapForExport(
|
|
966
985
|
container,
|
|
967
|
-
|
|
986
|
+
treemapParsed,
|
|
968
987
|
effectivePalette,
|
|
969
988
|
ctx.isDark,
|
|
970
|
-
{
|
|
989
|
+
{
|
|
990
|
+
width: EXPORT_WIDTH,
|
|
991
|
+
height: EXPORT_HEIGHT,
|
|
992
|
+
}
|
|
971
993
|
);
|
|
972
994
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
973
995
|
}
|
|
@@ -982,13 +1004,10 @@ async function exportRaci(ctx: ExportContext): Promise<string> {
|
|
|
982
1004
|
if (raciParsed.error) return '';
|
|
983
1005
|
|
|
984
1006
|
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
985
|
-
renderRaciForExport(
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
ctx.isDark,
|
|
990
|
-
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
991
|
-
);
|
|
1007
|
+
renderRaciForExport(container, raciParsed, effectivePalette, ctx.isDark, {
|
|
1008
|
+
width: EXPORT_WIDTH,
|
|
1009
|
+
height: EXPORT_HEIGHT,
|
|
1010
|
+
});
|
|
992
1011
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
993
1012
|
}
|
|
994
1013
|
|
|
@@ -1091,14 +1110,7 @@ async function exportVenn(ctx: ExportContext): Promise<string> {
|
|
|
1091
1110
|
if (parsed.error || parsed.vennSets.length < 2) return '';
|
|
1092
1111
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
1093
1112
|
const { container, dims } = beginVizExport();
|
|
1094
|
-
renderVenn(
|
|
1095
|
-
container,
|
|
1096
|
-
parsed,
|
|
1097
|
-
effectivePalette,
|
|
1098
|
-
ctx.isDark,
|
|
1099
|
-
undefined,
|
|
1100
|
-
dims
|
|
1101
|
-
);
|
|
1113
|
+
renderVenn(container, parsed, effectivePalette, ctx.isDark, undefined, dims);
|
|
1102
1114
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
1103
1115
|
}
|
|
1104
1116
|
|
|
@@ -213,6 +213,8 @@ export const ATTRIBUTE_KEYS = new Set([
|
|
|
213
213
|
'value',
|
|
214
214
|
'label',
|
|
215
215
|
'style',
|
|
216
|
+
// Treemap color-by-value metric key
|
|
217
|
+
'heat',
|
|
216
218
|
// Infra node behavior + edge colon-keys (§4) come from the single-source
|
|
217
219
|
// directives registry — the parser accepts them only as `key: value` node
|
|
218
220
|
// properties, so they render as `propertyName`. The dual-use top-level SLO
|
package/src/editor/keywords.ts
CHANGED
|
@@ -27,6 +27,7 @@ export const CHART_TYPES = new Set([
|
|
|
27
27
|
'journey-map',
|
|
28
28
|
'pyramid',
|
|
29
29
|
'ring',
|
|
30
|
+
'treemap',
|
|
30
31
|
'raci',
|
|
31
32
|
'rasci',
|
|
32
33
|
'daci',
|
|
@@ -91,6 +92,8 @@ export const METADATA_KEYS = new Set([
|
|
|
91
92
|
'value',
|
|
92
93
|
'label',
|
|
93
94
|
'style',
|
|
95
|
+
// Treemap color-by-value metric key
|
|
96
|
+
'heat',
|
|
94
97
|
]);
|
|
95
98
|
|
|
96
99
|
/** Tag declaration keyword. */
|