@diagrammo/dgmo 0.47.0 → 0.48.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/dist/advanced.js +11 -11
- package/dist/auto.js +16 -16
- package/dist/auto.mjs +41 -12
- package/dist/block.js +4 -4
- package/dist/{chunk-FNB3PNGF.js → chunk-7DTWEFUE.js} +3 -3
- package/dist/{chunk-2DXMZRWJ.js → chunk-B65G5DNL.js} +8 -1
- package/dist/{chunk-RZCF2VVM.js → chunk-B7JUCQUA.js} +3 -1
- package/dist/{chunk-DU7W62N5.js → chunk-FANX6UJR.js} +3 -3
- package/dist/{chunk-6FZYCKXB.js → chunk-HSH73DP6.js} +2 -2
- package/dist/{chunk-P4RH4OSP.js → chunk-ILKORZ3H.js} +2 -2
- package/dist/{chunk-Y5QGVTVC.js → chunk-JTSMEYIZ.js} +7 -3
- package/dist/{chunk-RCERWOJR.js → chunk-ODZVHU3R.js} +1 -2
- package/dist/{chunk-DZAQBW35.js → chunk-SGZYXELT.js} +3 -7
- package/dist/{chunk-CUVHQHZ7.js → chunk-THXCFRTU.js} +3 -3
- package/dist/{chunk-IH6ZMVQ5.js → chunk-UC7TFHUM.js} +29 -11
- package/dist/{chunk-EMS2SK26.js → chunk-ZBWMSD5N.js} +3 -1
- package/dist/cli.cjs +15 -15
- package/dist/{dgmo-router-DZCIDUO5.js → dgmo-router-X35VLZLM.js} +3 -3
- package/dist/element.js +16 -16
- package/dist/element.mjs +41 -12
- package/dist/{flowchart-renderer-ZAEOL4DX.js → flowchart-renderer-SNDSII4D.js} +2 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -4
- package/dist/{parser-FUSKFGZN.js → parser-2EWECDSA.js} +2 -2
- package/dist/{renderer-UWA5XS74.js → renderer-4JHRUBE6.js} +3 -3
- package/dist/{renderer-WZRREQ2P.js → renderer-5MYBMBYO.js} +2 -2
- package/dist/{renderer-TOCOVLHS.js → renderer-B2OMQIQG.js} +3 -3
- package/dist/{renderer-2XQQQ6QR.js → renderer-N43WR5GV.js} +3 -3
- package/dist/{renderer-T7YKC5VP.js → renderer-ZRNG54CW.js} +3 -3
- package/dist/{state-renderer-RFYHSP4K.js → state-renderer-B4DQBM6M.js} +2 -2
- package/package.json +23 -71
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
package/src/sitemap/types.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Sitemap Diagram Types
|
|
3
|
-
// ============================================================
|
|
4
|
-
|
|
5
|
-
import type { DgmoError } from '../diagnostics';
|
|
6
|
-
import type { TagGroup } from '../utils/tag-groups';
|
|
7
|
-
|
|
8
|
-
export interface SitemapNode {
|
|
9
|
-
readonly id: string;
|
|
10
|
-
readonly label: string;
|
|
11
|
-
readonly metadata: Readonly<Record<string, string>>;
|
|
12
|
-
readonly children: readonly SitemapNode[];
|
|
13
|
-
readonly parentId: string | null;
|
|
14
|
-
readonly description?: readonly string[];
|
|
15
|
-
/** True for [Group Name] container nodes */
|
|
16
|
-
readonly isContainer: boolean;
|
|
17
|
-
readonly lineNumber: number;
|
|
18
|
-
readonly color?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface SitemapEdge {
|
|
22
|
-
readonly sourceId: string;
|
|
23
|
-
readonly targetId: string;
|
|
24
|
-
readonly label?: string;
|
|
25
|
-
readonly lineNumber: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type SitemapDirection = 'TB' | 'LR';
|
|
29
|
-
|
|
30
|
-
export interface ParsedSitemap {
|
|
31
|
-
readonly title: string | null;
|
|
32
|
-
readonly titleLineNumber: number | null;
|
|
33
|
-
readonly direction: SitemapDirection;
|
|
34
|
-
/** Top-level nodes (roots of the hierarchy) */
|
|
35
|
-
readonly roots: readonly SitemapNode[];
|
|
36
|
-
/** All cross-link edges */
|
|
37
|
-
readonly edges: readonly SitemapEdge[];
|
|
38
|
-
readonly tagGroups: readonly TagGroup[];
|
|
39
|
-
readonly options: Readonly<Record<string, string>>;
|
|
40
|
-
readonly diagnostics: readonly DgmoError[];
|
|
41
|
-
readonly error: string | null;
|
|
42
|
-
}
|
package/src/slope/parser.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// slope parser door — Story 109.2 (arch-review).
|
|
3
|
-
//
|
|
4
|
-
// The six visualizations share one parse state machine (visualizations/parse.ts);
|
|
5
|
-
// this is slope's typed entry point into it, returning the narrowed
|
|
6
|
-
// ParsedSlope shape. The registry binds the 'slope' chart type to this door.
|
|
7
|
-
// ============================================================
|
|
8
|
-
|
|
9
|
-
import type { PaletteColors } from '../palettes';
|
|
10
|
-
import { parseVisualization } from '../visualizations/parse';
|
|
11
|
-
import type { ParsedSlope } from '../visualizations/types';
|
|
12
|
-
|
|
13
|
-
export function parseSlope(
|
|
14
|
-
content: string,
|
|
15
|
-
palette?: PaletteColors
|
|
16
|
-
): ParsedSlope {
|
|
17
|
-
return parseVisualization(content, palette) as ParsedSlope;
|
|
18
|
-
}
|
package/src/slope/renderer.ts
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Slope renderer — Story 109.2 (arch-review). Extracted from d3.ts.
|
|
3
|
-
// ============================================================
|
|
4
|
-
|
|
5
|
-
import * as d3Scale from 'd3-scale';
|
|
6
|
-
import * as d3Shape from 'd3-shape';
|
|
7
|
-
import * as d3Array from 'd3-array';
|
|
8
|
-
import { measureText, wrapTextToWidth } from '../utils/text-measure';
|
|
9
|
-
import type { D3ExportDimensions } from '../utils/d3-types';
|
|
10
|
-
import { ScaleContext } from '../utils/scaling';
|
|
11
|
-
import {
|
|
12
|
-
createTooltip,
|
|
13
|
-
hideTooltip,
|
|
14
|
-
initD3Chart,
|
|
15
|
-
renderChartTitle,
|
|
16
|
-
showTooltip,
|
|
17
|
-
} from '../utils/d3-helpers';
|
|
18
|
-
import type { ParsedSlope } from '../visualizations/types';
|
|
19
|
-
import type { PaletteColors } from '../palettes';
|
|
20
|
-
|
|
21
|
-
// ============================================================
|
|
22
|
-
// Slope Chart Renderer
|
|
23
|
-
// ============================================================
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Resolves vertical label collisions by nudging overlapping items apart.
|
|
27
|
-
* Takes items with a naturalY (center) and height, returns adjusted center Y positions.
|
|
28
|
-
* Optional maxY clamps the bottom edge so labels don't overflow the chart area.
|
|
29
|
-
*/
|
|
30
|
-
export function resolveVerticalCollisions(
|
|
31
|
-
items: { naturalY: number; height: number }[],
|
|
32
|
-
minGap: number,
|
|
33
|
-
maxY?: number
|
|
34
|
-
): number[] {
|
|
35
|
-
if (items.length === 0) return [];
|
|
36
|
-
const sorted = items
|
|
37
|
-
.map((it, i) => ({ ...it, idx: i }))
|
|
38
|
-
.sort((a, b) => a.naturalY - b.naturalY);
|
|
39
|
-
const adjustedY = new Array<number>(items.length);
|
|
40
|
-
let prevBottom = -Infinity;
|
|
41
|
-
for (const item of sorted) {
|
|
42
|
-
const halfH = item.height / 2;
|
|
43
|
-
let top = Math.max(item.naturalY - halfH, prevBottom + minGap);
|
|
44
|
-
// Clamp so the label bottom doesn't exceed maxY
|
|
45
|
-
if (maxY !== undefined) {
|
|
46
|
-
top = Math.min(top, maxY - item.height);
|
|
47
|
-
}
|
|
48
|
-
adjustedY[item.idx] = top + halfH;
|
|
49
|
-
prevBottom = top + item.height;
|
|
50
|
-
}
|
|
51
|
-
return adjustedY;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
|
|
55
|
-
const SLOPE_LABEL_FONT_SIZE = 14;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Renders a slope chart into the given container using D3.
|
|
59
|
-
*/
|
|
60
|
-
export function renderSlopeChart(
|
|
61
|
-
container: HTMLDivElement,
|
|
62
|
-
parsed: ParsedSlope,
|
|
63
|
-
palette: PaletteColors,
|
|
64
|
-
isDark: boolean,
|
|
65
|
-
onClickItem?: (lineNumber: number) => void,
|
|
66
|
-
exportDims?: D3ExportDimensions
|
|
67
|
-
): void {
|
|
68
|
-
const { periods, data } = parsed;
|
|
69
|
-
const title = parsed.noTitle ? null : parsed.title;
|
|
70
|
-
if (data.length === 0 || periods.length < 2) return;
|
|
71
|
-
|
|
72
|
-
const init = initD3Chart(container, palette, exportDims);
|
|
73
|
-
if (!init) return;
|
|
74
|
-
const { svg, width, height, textColor, mutedColor, bgColor, colors } = init;
|
|
75
|
-
|
|
76
|
-
const idealWidth = SLOPE_MARGIN.left + periods.length * 100 + 150;
|
|
77
|
-
const ctx = exportDims
|
|
78
|
-
? ScaleContext.identity()
|
|
79
|
-
: ScaleContext.from(width, idealWidth);
|
|
80
|
-
|
|
81
|
-
const sMarginTop = ctx.aesthetic(SLOPE_MARGIN.top);
|
|
82
|
-
const sMarginBottom = ctx.aesthetic(SLOPE_MARGIN.bottom);
|
|
83
|
-
const sMarginLeft = ctx.aesthetic(SLOPE_MARGIN.left);
|
|
84
|
-
const sLabelFontSize = ctx.text(SLOPE_LABEL_FONT_SIZE);
|
|
85
|
-
const sPeriodFont = ctx.text(18);
|
|
86
|
-
const sValueFont = ctx.text(16);
|
|
87
|
-
const sPeriodHeaderY = ctx.structural(15);
|
|
88
|
-
const sDash = `${ctx.structural(4)},${ctx.structural(4)}`;
|
|
89
|
-
const sLineStroke = ctx.structural(2.5);
|
|
90
|
-
const sHitWidth = ctx.structural(14);
|
|
91
|
-
const sPointR = ctx.structural(4);
|
|
92
|
-
const sPointStroke = ctx.structural(1.5);
|
|
93
|
-
const sValueLabelXOff = ctx.structural(10);
|
|
94
|
-
const sLabelGap = ctx.structural(10);
|
|
95
|
-
|
|
96
|
-
svg.attr('preserveAspectRatio', 'xMidYMin meet');
|
|
97
|
-
if (ctx.isBelowFloor) {
|
|
98
|
-
svg.attr('width', '100%');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Compute right margin from the longest end-of-line label
|
|
102
|
-
const maxLabelText = data.reduce((longest, item) => {
|
|
103
|
-
const text = `${item.values[item.values.length - 1]} — ${item.label}`;
|
|
104
|
-
return text.length > longest.length ? text : longest;
|
|
105
|
-
}, '');
|
|
106
|
-
const estimatedLabelWidth = measureText(maxLabelText, sLabelFontSize);
|
|
107
|
-
const maxRightMargin = Math.floor(width * 0.35);
|
|
108
|
-
const rightMargin = Math.min(
|
|
109
|
-
Math.max(estimatedLabelWidth + ctx.aesthetic(30), ctx.aesthetic(120)),
|
|
110
|
-
maxRightMargin
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const innerWidth = width - sMarginLeft - rightMargin;
|
|
114
|
-
const innerHeight = height - sMarginTop - sMarginBottom;
|
|
115
|
-
|
|
116
|
-
// Scales
|
|
117
|
-
const allValues = data.flatMap((d) => d.values);
|
|
118
|
-
const [minVal, maxVal] = d3Array.extent(allValues) as [number, number];
|
|
119
|
-
const valuePadding = (maxVal - minVal) * 0.1 || 1;
|
|
120
|
-
|
|
121
|
-
const yScale = d3Scale
|
|
122
|
-
.scaleLinear()
|
|
123
|
-
.domain([minVal - valuePadding, maxVal + valuePadding])
|
|
124
|
-
.range([innerHeight, 0]);
|
|
125
|
-
|
|
126
|
-
const xScale = d3Scale
|
|
127
|
-
.scalePoint<string>()
|
|
128
|
-
.domain(periods)
|
|
129
|
-
.range([0, innerWidth])
|
|
130
|
-
.padding(0);
|
|
131
|
-
|
|
132
|
-
const g = svg
|
|
133
|
-
.append('g')
|
|
134
|
-
.attr('transform', `translate(${sMarginLeft},${sMarginTop})`);
|
|
135
|
-
|
|
136
|
-
// Tooltip
|
|
137
|
-
const tooltip = createTooltip(container, palette, isDark);
|
|
138
|
-
|
|
139
|
-
// Title
|
|
140
|
-
renderChartTitle(
|
|
141
|
-
svg,
|
|
142
|
-
title,
|
|
143
|
-
parsed.titleLineNumber,
|
|
144
|
-
width,
|
|
145
|
-
textColor,
|
|
146
|
-
onClickItem
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
// Period column headers
|
|
150
|
-
for (const period of periods) {
|
|
151
|
-
const x = xScale(period)!;
|
|
152
|
-
g.append('text')
|
|
153
|
-
.attr('x', x)
|
|
154
|
-
.attr('y', -sPeriodHeaderY)
|
|
155
|
-
.attr('text-anchor', 'middle')
|
|
156
|
-
.attr('fill', textColor)
|
|
157
|
-
.attr('font-size', `${sPeriodFont}px`)
|
|
158
|
-
.attr('font-weight', '600')
|
|
159
|
-
.text(period);
|
|
160
|
-
|
|
161
|
-
// Vertical guide line
|
|
162
|
-
g.append('line')
|
|
163
|
-
.attr('x1', x)
|
|
164
|
-
.attr('y1', 0)
|
|
165
|
-
.attr('x2', x)
|
|
166
|
-
.attr('y2', innerHeight)
|
|
167
|
-
.attr('stroke', mutedColor)
|
|
168
|
-
.attr('stroke-width', 1)
|
|
169
|
-
.attr('stroke-dasharray', sDash);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Line generator
|
|
173
|
-
const lineGen = d3Shape
|
|
174
|
-
.line<number>()
|
|
175
|
-
// periods[i] is in-bounds — i comes from d3 line's index callback over the data array.
|
|
176
|
-
.x((_d, i) => xScale(periods[i]!)!)
|
|
177
|
-
.y((d) => yScale(d));
|
|
178
|
-
|
|
179
|
-
// Pre-compute per-series data for label collision resolution
|
|
180
|
-
const seriesInfo = data.map((item, idx) => {
|
|
181
|
-
// colors is non-empty; modulo guarantees in-bounds.
|
|
182
|
-
const color = item.color ?? colors[idx % colors.length]!;
|
|
183
|
-
// values is non-empty by parser validation (slope requires P>=2 values per series).
|
|
184
|
-
const firstVal = item.values[0]!;
|
|
185
|
-
const lastVal = item.values[item.values.length - 1]!;
|
|
186
|
-
const absChange = lastVal - firstVal;
|
|
187
|
-
const pctChange = firstVal !== 0 ? (absChange / firstVal) * 100 : null;
|
|
188
|
-
const sign = absChange > 0 ? '+' : '';
|
|
189
|
-
const tipLines = [`${sign}${parseFloat(absChange.toFixed(2))}`];
|
|
190
|
-
if (pctChange !== null) tipLines.push(`${sign}${pctChange.toFixed(1)}%`);
|
|
191
|
-
const tipHtml = tipLines.join('<br>');
|
|
192
|
-
|
|
193
|
-
// Compute right-side label text and wrapping info
|
|
194
|
-
// periods is non-empty (slope requires P >= 2 periods).
|
|
195
|
-
const lastX = xScale(periods[periods.length - 1]!)!;
|
|
196
|
-
const labelText = `${lastVal} — ${item.label}`;
|
|
197
|
-
const availableWidth = rightMargin - ctx.aesthetic(15);
|
|
198
|
-
|
|
199
|
-
let labelLineCount = 1;
|
|
200
|
-
let wrappedLines: string[] | null = null;
|
|
201
|
-
if (measureText(labelText, sLabelFontSize) > availableWidth) {
|
|
202
|
-
const lines = wrapTextToWidth(labelText, sLabelFontSize, availableWidth);
|
|
203
|
-
labelLineCount = lines.length;
|
|
204
|
-
wrappedLines = lines;
|
|
205
|
-
}
|
|
206
|
-
const lineHeight = sLabelFontSize * 1.2;
|
|
207
|
-
const labelHeight =
|
|
208
|
-
labelLineCount === 1 ? sLabelFontSize : labelLineCount * lineHeight;
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
item,
|
|
212
|
-
idx,
|
|
213
|
-
color,
|
|
214
|
-
firstVal,
|
|
215
|
-
lastVal,
|
|
216
|
-
tipHtml,
|
|
217
|
-
lastX,
|
|
218
|
-
labelText,
|
|
219
|
-
wrappedLines,
|
|
220
|
-
labelHeight,
|
|
221
|
-
};
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// --- Resolve left-side label collisions per non-last period column ---
|
|
225
|
-
const leftLabelHeight = sValueFont * 1.25;
|
|
226
|
-
const leftLabelCollisions: Map<number, number[]> = new Map();
|
|
227
|
-
for (let pi = 0; pi < periods.length - 1; pi++) {
|
|
228
|
-
const entries = data.map((item) => ({
|
|
229
|
-
// pi is in-bounds by loop guard against periods.length, and each data row has periods.length values.
|
|
230
|
-
naturalY: yScale(item.values[pi]!),
|
|
231
|
-
height: leftLabelHeight,
|
|
232
|
-
}));
|
|
233
|
-
leftLabelCollisions.set(
|
|
234
|
-
pi,
|
|
235
|
-
resolveVerticalCollisions(entries, 4, innerHeight)
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// --- Resolve right-side label collisions ---
|
|
240
|
-
const rightEntries = seriesInfo.map((si) => ({
|
|
241
|
-
naturalY: yScale(si.lastVal),
|
|
242
|
-
height: Math.max(si.labelHeight, sLabelFontSize * 1.4),
|
|
243
|
-
}));
|
|
244
|
-
const rightAdjustedY = resolveVerticalCollisions(
|
|
245
|
-
rightEntries,
|
|
246
|
-
4,
|
|
247
|
-
innerHeight
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
// Render each data series
|
|
251
|
-
data.forEach((item, idx) => {
|
|
252
|
-
// seriesInfo was built by data.map() above, so idx is in-bounds.
|
|
253
|
-
const si = seriesInfo[idx]!;
|
|
254
|
-
const color = si.color;
|
|
255
|
-
|
|
256
|
-
// Wrap each series in a group with data-line-number for sync adapter
|
|
257
|
-
const seriesG = g
|
|
258
|
-
.append('g')
|
|
259
|
-
.attr('class', 'slope-series')
|
|
260
|
-
.attr('data-line-number', String(item.lineNumber));
|
|
261
|
-
|
|
262
|
-
// Line
|
|
263
|
-
seriesG
|
|
264
|
-
.append('path')
|
|
265
|
-
.datum(item.values)
|
|
266
|
-
.attr('fill', 'none')
|
|
267
|
-
.attr('stroke', color)
|
|
268
|
-
.attr('stroke-width', sLineStroke)
|
|
269
|
-
.attr('d', lineGen);
|
|
270
|
-
|
|
271
|
-
// Invisible wider path for easier hover targeting
|
|
272
|
-
seriesG
|
|
273
|
-
.append('path')
|
|
274
|
-
.datum(item.values)
|
|
275
|
-
.attr('fill', 'none')
|
|
276
|
-
.attr('stroke', 'transparent')
|
|
277
|
-
.attr('stroke-width', sHitWidth)
|
|
278
|
-
.attr('d', lineGen)
|
|
279
|
-
.style('cursor', onClickItem ? 'pointer' : 'default')
|
|
280
|
-
.on('mouseenter', (event: MouseEvent) =>
|
|
281
|
-
showTooltip(tooltip, si.tipHtml, event)
|
|
282
|
-
)
|
|
283
|
-
.on('mousemove', (event: MouseEvent) =>
|
|
284
|
-
showTooltip(tooltip, si.tipHtml, event)
|
|
285
|
-
)
|
|
286
|
-
.on('mouseleave', () => hideTooltip(tooltip))
|
|
287
|
-
.on('click', () => {
|
|
288
|
-
if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Points and value labels
|
|
292
|
-
item.values.forEach((val, i) => {
|
|
293
|
-
// periods[i] is in-bounds because item.values.length === periods.length (slope contract).
|
|
294
|
-
const x = xScale(periods[i]!)!;
|
|
295
|
-
const y = yScale(val);
|
|
296
|
-
|
|
297
|
-
// Point circle
|
|
298
|
-
seriesG
|
|
299
|
-
.append('circle')
|
|
300
|
-
.attr('cx', x)
|
|
301
|
-
.attr('cy', y)
|
|
302
|
-
.attr('r', sPointR)
|
|
303
|
-
.attr('fill', color)
|
|
304
|
-
.attr('stroke', bgColor)
|
|
305
|
-
.attr('stroke-width', sPointStroke)
|
|
306
|
-
.style('cursor', onClickItem ? 'pointer' : 'default')
|
|
307
|
-
.on('mouseenter', (event: MouseEvent) =>
|
|
308
|
-
showTooltip(tooltip, si.tipHtml, event)
|
|
309
|
-
)
|
|
310
|
-
.on('mousemove', (event: MouseEvent) =>
|
|
311
|
-
showTooltip(tooltip, si.tipHtml, event)
|
|
312
|
-
)
|
|
313
|
-
.on('mouseleave', () => hideTooltip(tooltip))
|
|
314
|
-
.on('click', () => {
|
|
315
|
-
if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// Value label — skip last point (shown in series label instead)
|
|
319
|
-
const isFirst = i === 0;
|
|
320
|
-
const isLast = i === periods.length - 1;
|
|
321
|
-
if (!isLast) {
|
|
322
|
-
// leftLabelCollisions was set for every i in [0, periods.length-1); idx is in-bounds by data.map.
|
|
323
|
-
const adjustedY = leftLabelCollisions.get(i)![idx]!;
|
|
324
|
-
seriesG
|
|
325
|
-
.append('text')
|
|
326
|
-
.attr('x', isFirst ? x - sValueLabelXOff : x)
|
|
327
|
-
.attr('y', adjustedY)
|
|
328
|
-
.attr('dy', '0.35em')
|
|
329
|
-
.attr('text-anchor', isFirst ? 'end' : 'middle')
|
|
330
|
-
.attr('fill', color)
|
|
331
|
-
.attr('font-size', `${sValueFont}px`)
|
|
332
|
-
.text(val.toString());
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Series label with value at end of line — wraps if it exceeds available space
|
|
337
|
-
// rightAdjustedY was produced from rightEntries.length === seriesInfo.length === data.length.
|
|
338
|
-
const adjustedLastY = rightAdjustedY[idx]!;
|
|
339
|
-
|
|
340
|
-
const labelEl = seriesG
|
|
341
|
-
.append('text')
|
|
342
|
-
.attr('x', si.lastX + sLabelGap)
|
|
343
|
-
.attr('y', adjustedLastY)
|
|
344
|
-
.attr('text-anchor', 'start')
|
|
345
|
-
.attr('fill', color)
|
|
346
|
-
.attr('font-size', `${sLabelFontSize}px`)
|
|
347
|
-
.attr('font-weight', '500');
|
|
348
|
-
|
|
349
|
-
if (!si.wrappedLines) {
|
|
350
|
-
labelEl.attr('dy', '0.35em').text(si.labelText);
|
|
351
|
-
} else {
|
|
352
|
-
const lineHeight = sLabelFontSize * 1.2;
|
|
353
|
-
const totalHeight = (si.wrappedLines.length - 1) * lineHeight;
|
|
354
|
-
const startDy = -totalHeight / 2;
|
|
355
|
-
|
|
356
|
-
si.wrappedLines.forEach((line, li) => {
|
|
357
|
-
labelEl
|
|
358
|
-
.append('tspan')
|
|
359
|
-
.attr('x', si.lastX + sLabelGap)
|
|
360
|
-
.attr(
|
|
361
|
-
'dy',
|
|
362
|
-
li === 0
|
|
363
|
-
? `${startDy + sLabelFontSize * 0.35}px`
|
|
364
|
-
: `${lineHeight}px`
|
|
365
|
-
)
|
|
366
|
-
.text(line);
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// ============================================================
|
|
373
|
-
// Arc Node Ordering
|
|
374
|
-
// ============================================================
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Orders arc diagram nodes based on the selected ordering strategy.
|
|
378
|
-
*/
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Swimlane Diagram — Diagnostic Catalog
|
|
3
|
-
// ============================================================
|
|
4
|
-
//
|
|
5
|
-
// Declarative metadata for every coded diagnostic the swimlane parser
|
|
6
|
-
// (`src/swimlane/parser.ts`) can emit. This is ADDITIVE — the parser still
|
|
7
|
-
// owns emission via its local `push(line, message, code, severity)` helper;
|
|
8
|
-
// each entry here mirrors that call site's canonical wording so consumers
|
|
9
|
-
// (CLI `diagnostics`, console error-review, MCP `validate_diagram`, spec docs)
|
|
10
|
-
// can enumerate the catalog without the parser re-typing wording.
|
|
11
|
-
//
|
|
12
|
-
// Every message builder tolerates being called with `{}` so the catalog can
|
|
13
|
-
// render a representative string with no live params. The parser emits these
|
|
14
|
-
// via `emit(SWIMLANE_DX.<KEY>, line, params)` — the message wording lives here
|
|
15
|
-
// only, never re-typed at the call site.
|
|
16
|
-
|
|
17
|
-
import type { DiagnosticSpec } from '../diagnostics';
|
|
18
|
-
|
|
19
|
-
/** Keyed specs — referenced by the parser's `emit()` call sites. */
|
|
20
|
-
export const SWIMLANE_DX = {
|
|
21
|
-
DUPLICATE_NODE: {
|
|
22
|
-
code: 'E_SWIMLANE_DUPLICATE_NODE',
|
|
23
|
-
severity: 'error',
|
|
24
|
-
chartType: 'swimlane',
|
|
25
|
-
title: 'Duplicate node',
|
|
26
|
-
message: (p) => `Duplicate node name '${p.name ?? 'Foo'}'`,
|
|
27
|
-
hint: 'Every node name must be unique across the diagram — rename or remove the second declaration.',
|
|
28
|
-
example: `swimlane T
|
|
29
|
-
lane L
|
|
30
|
-
L
|
|
31
|
-
Foo
|
|
32
|
-
Foo`,
|
|
33
|
-
},
|
|
34
|
-
UNKNOWN_NODE: {
|
|
35
|
-
code: 'E_SWIMLANE_UNKNOWN_NODE',
|
|
36
|
-
severity: 'error',
|
|
37
|
-
chartType: 'swimlane',
|
|
38
|
-
title: 'Unknown node in edge',
|
|
39
|
-
message: (p) => `Edge references unknown node '${p.node ?? 'Bar'}'`,
|
|
40
|
-
hint: 'Declare the node under a lane before referencing it in a flow arrow.',
|
|
41
|
-
example: `swimlane T
|
|
42
|
-
lane L
|
|
43
|
-
L
|
|
44
|
-
Foo
|
|
45
|
-
Foo -> Bar`,
|
|
46
|
-
},
|
|
47
|
-
UNKNOWN_LANE: {
|
|
48
|
-
code: 'E_SWIMLANE_UNKNOWN_LANE',
|
|
49
|
-
severity: 'error',
|
|
50
|
-
chartType: 'swimlane',
|
|
51
|
-
title: 'Node outside a lane',
|
|
52
|
-
message: (p) =>
|
|
53
|
-
`Node '${p.node ?? 'Foo'}' is not inside a lane — declare a lane first`,
|
|
54
|
-
hint: 'Open a lane context (a bare line matching a declared lane) before the node line.',
|
|
55
|
-
example: `swimlane T
|
|
56
|
-
lane L
|
|
57
|
-
[Start]
|
|
58
|
-
Foo`,
|
|
59
|
-
},
|
|
60
|
-
NO_LANES: {
|
|
61
|
-
code: 'E_SWIMLANE_NO_LANES',
|
|
62
|
-
severity: 'error',
|
|
63
|
-
chartType: 'swimlane',
|
|
64
|
-
title: 'Phase without lanes',
|
|
65
|
-
message: (p) => `Phase '${p.phase ?? 'Start'}' declared but no lanes exist`,
|
|
66
|
-
hint: 'Declare at least one `lane <Name>` before any `[Phase]` header.',
|
|
67
|
-
example: `swimlane T
|
|
68
|
-
[Start]`,
|
|
69
|
-
},
|
|
70
|
-
// One code, many wordings — the call site passes the exact `reason`:
|
|
71
|
-
// parseNodeToken: 'inclusive gateway (<o …>) is fast-follow', event-based,
|
|
72
|
-
// timer|message|signal events; declareNode: 'notes are
|
|
73
|
-
// deferred past v1', 'data objects are deferred past v1',
|
|
74
|
-
// `${k} events are fast-follow`; addChain: 'message flow
|
|
75
|
-
// (~>) is fast-follow'.
|
|
76
|
-
UNSUPPORTED: {
|
|
77
|
-
code: 'E_SWIMLANE_UNSUPPORTED',
|
|
78
|
-
severity: 'error',
|
|
79
|
-
chartType: 'swimlane',
|
|
80
|
-
title: 'Unsupported construct',
|
|
81
|
-
message: (p) =>
|
|
82
|
-
typeof p.reason === 'string' && p.reason
|
|
83
|
-
? p.reason
|
|
84
|
-
: 'inclusive gateway (<o …>) is fast-follow',
|
|
85
|
-
hint: 'This construct is deferred past v1 / fast-follow — remove it or use a supported shape, event, or flow.',
|
|
86
|
-
example: `swimlane T
|
|
87
|
-
lane L
|
|
88
|
-
L
|
|
89
|
-
<o Maybe>`,
|
|
90
|
-
},
|
|
91
|
-
} satisfies Record<string, DiagnosticSpec>;
|
|
92
|
-
|
|
93
|
-
export const SWIMLANE_DIAGNOSTICS: DiagnosticSpec[] =
|
|
94
|
-
Object.values(SWIMLANE_DX);
|