@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
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Boxes and Lines Diagram — Layout Engine
|
|
3
|
-
// ============================================================
|
|
4
|
-
//
|
|
5
|
-
// Node sizing + the public `layoutBoxesAndLines` entry. Placement and edge
|
|
6
|
-
// routing are delegated to the dagre placement-search engine (layout-search.ts);
|
|
7
|
-
// this module owns node sizing, parallel-edge fan offsets, and note floating —
|
|
8
|
-
// the engine-agnostic post-passes applied to whatever the engine returns.
|
|
9
|
-
|
|
10
|
-
import type { ParsedBoxesAndLines, BLNode, BLGroup } from './types';
|
|
11
|
-
import { measureText, wrapTextToWidth } from '../utils/text-measure';
|
|
12
|
-
import { placeEdgeLabels } from './label-placement';
|
|
13
|
-
import {
|
|
14
|
-
resolveNotes,
|
|
15
|
-
buildPlacedNotes,
|
|
16
|
-
noteCanvasShift,
|
|
17
|
-
type PlacedNote,
|
|
18
|
-
} from '../utils/notes';
|
|
19
|
-
|
|
20
|
-
// ── Constants ──────────────────────────────────────────────
|
|
21
|
-
const MARGIN = 40;
|
|
22
|
-
const MAX_PARALLEL_EDGES = 5;
|
|
23
|
-
const PARALLEL_SPACING = 22;
|
|
24
|
-
|
|
25
|
-
const PHI = 1.618;
|
|
26
|
-
export const NODE_HEIGHT = 60;
|
|
27
|
-
export const NODE_WIDTH = Math.round(NODE_HEIGHT * PHI);
|
|
28
|
-
const DESC_NODE_WIDTH = 140;
|
|
29
|
-
const DESC_FONT_SIZE = 10;
|
|
30
|
-
const DESC_LINE_HEIGHT = 1.4;
|
|
31
|
-
const DESC_PADDING = 8;
|
|
32
|
-
const SEPARATOR_GAP = 4;
|
|
33
|
-
const MAX_DESC_LINES = 6;
|
|
34
|
-
const MAX_LABEL_LINES = 3;
|
|
35
|
-
const LABEL_LINE_HEIGHT = 1.3;
|
|
36
|
-
const LABEL_PAD = 12;
|
|
37
|
-
// Bottom value-row reserved on a DESCRIBED node under `show-values`: a thin
|
|
38
|
-
// divider + a "Metric: value" footer line (replaces the old corner badge).
|
|
39
|
-
const VALUE_ROW_FONT = 11;
|
|
40
|
-
const VALUE_ROW_H =
|
|
41
|
-
SEPARATOR_GAP + VALUE_ROW_FONT * DESC_LINE_HEIGHT + DESC_PADDING;
|
|
42
|
-
|
|
43
|
-
// ── Result types ───────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
export interface BLLayoutNode {
|
|
46
|
-
readonly label: string;
|
|
47
|
-
readonly x: number;
|
|
48
|
-
readonly y: number;
|
|
49
|
-
readonly width: number;
|
|
50
|
-
readonly height: number;
|
|
51
|
-
/** A note floated beside this box (never moves the box). */
|
|
52
|
-
readonly note?: PlacedNote;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface BLLayoutEdge {
|
|
56
|
-
readonly source: string;
|
|
57
|
-
readonly target: string;
|
|
58
|
-
readonly label?: string;
|
|
59
|
-
readonly bidirectional: boolean;
|
|
60
|
-
readonly lineNumber: number;
|
|
61
|
-
readonly points: ReadonlyArray<{ readonly x: number; readonly y: number }>;
|
|
62
|
-
/** Centre of the label box (set by label-placement). */
|
|
63
|
-
readonly labelX?: number;
|
|
64
|
-
readonly labelY?: number;
|
|
65
|
-
/** Wrapped label box dimensions + lines (set by label-placement; the renderer
|
|
66
|
-
* draws the halo + tspans straight from these). */
|
|
67
|
-
readonly labelWidth?: number;
|
|
68
|
-
readonly labelHeight?: number;
|
|
69
|
-
readonly labelLines?: readonly string[];
|
|
70
|
-
readonly yOffset: number;
|
|
71
|
-
readonly parallelCount: number;
|
|
72
|
-
readonly metadata: Readonly<Record<string, string>>;
|
|
73
|
-
/** Marker for renderer: draw with linear curve, not curveBasis (ELK gives
|
|
74
|
-
* us orthogonal polylines and curveBasis would smooth corners into waves) */
|
|
75
|
-
readonly deferred?: boolean;
|
|
76
|
-
/** Pinned-layout connector: a border-clipped straight 2-point segment (Canvas
|
|
77
|
-
* Editor spike, Decision 7). Renderer draws it with a linear generator —
|
|
78
|
-
* curveBasis collapses a 2-point polyline. */
|
|
79
|
-
readonly straight?: boolean;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface BLLayoutGroup {
|
|
83
|
-
readonly label: string;
|
|
84
|
-
readonly lineNumber: number;
|
|
85
|
-
readonly x: number;
|
|
86
|
-
readonly y: number;
|
|
87
|
-
readonly width: number;
|
|
88
|
-
readonly height: number;
|
|
89
|
-
readonly collapsed: boolean;
|
|
90
|
-
readonly childCount?: number;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface BLLayoutResult {
|
|
94
|
-
readonly nodes: readonly BLLayoutNode[];
|
|
95
|
-
readonly edges: readonly BLLayoutEdge[];
|
|
96
|
-
readonly groups: readonly BLLayoutGroup[];
|
|
97
|
-
readonly width: number;
|
|
98
|
-
readonly height: number;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ── Node sizing ────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
function splitCamelCase(word: string): string[] {
|
|
104
|
-
const parts: string[] = [];
|
|
105
|
-
let start = 0;
|
|
106
|
-
for (let i = 1; i < word.length; i++) {
|
|
107
|
-
// In-bounds by loop guard (i >= 1 and i < word.length).
|
|
108
|
-
const prev = word.charAt(i - 1);
|
|
109
|
-
const curr = word.charAt(i);
|
|
110
|
-
const next = i + 1 < word.length ? word.charAt(i + 1) : '';
|
|
111
|
-
const lowerToUpper =
|
|
112
|
-
prev >= 'a' && prev <= 'z' && curr >= 'A' && curr <= 'Z';
|
|
113
|
-
const upperRunEnd =
|
|
114
|
-
prev >= 'A' &&
|
|
115
|
-
prev <= 'Z' &&
|
|
116
|
-
curr >= 'A' &&
|
|
117
|
-
curr <= 'Z' &&
|
|
118
|
-
next >= 'a' &&
|
|
119
|
-
next <= 'z';
|
|
120
|
-
if (lowerToUpper || upperRunEnd) {
|
|
121
|
-
parts.push(word.slice(start, i));
|
|
122
|
-
start = i;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
parts.push(word.slice(start));
|
|
126
|
-
return parts.length > 1 ? parts : [word];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function estimateLabelLines(label: string, nodeWidth = NODE_WIDTH): number {
|
|
130
|
-
const rawParts = label.split(/[\s-]+/);
|
|
131
|
-
const words: string[] = [];
|
|
132
|
-
for (const part of rawParts) {
|
|
133
|
-
if (!part) continue;
|
|
134
|
-
words.push(...splitCamelCase(part));
|
|
135
|
-
}
|
|
136
|
-
const maxTextWidth = nodeWidth - 24;
|
|
137
|
-
for (let fontSize = 13; fontSize >= 9; fontSize--) {
|
|
138
|
-
if (maxTextWidth < measureText('MM', fontSize)) continue;
|
|
139
|
-
let lines = 1;
|
|
140
|
-
let current = '';
|
|
141
|
-
for (const word of words) {
|
|
142
|
-
const test = current ? `${current} ${word}` : word;
|
|
143
|
-
if (measureText(test, fontSize) <= maxTextWidth) {
|
|
144
|
-
current = test;
|
|
145
|
-
} else {
|
|
146
|
-
lines++;
|
|
147
|
-
current = word;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (lines <= MAX_LABEL_LINES) return Math.min(lines, MAX_LABEL_LINES);
|
|
151
|
-
}
|
|
152
|
-
return MAX_LABEL_LINES;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export function computeNodeSize(
|
|
156
|
-
node: BLNode,
|
|
157
|
-
reserveValueRow: boolean
|
|
158
|
-
): { width: number; height: number } {
|
|
159
|
-
if (!node.description || node.description.length === 0) {
|
|
160
|
-
return { width: NODE_WIDTH, height: NODE_HEIGHT };
|
|
161
|
-
}
|
|
162
|
-
const w = DESC_NODE_WIDTH;
|
|
163
|
-
const labelLines = estimateLabelLines(node.label, w);
|
|
164
|
-
const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT + LABEL_PAD;
|
|
165
|
-
const maxTextWidth = w - 24;
|
|
166
|
-
let totalRenderedLines = 0;
|
|
167
|
-
for (const line of node.description) {
|
|
168
|
-
if (measureText(line, DESC_FONT_SIZE) <= maxTextWidth) {
|
|
169
|
-
totalRenderedLines += 1;
|
|
170
|
-
} else {
|
|
171
|
-
// Hard-break long words to match the renderer's slicing behaviour.
|
|
172
|
-
totalRenderedLines += wrapTextToWidth(
|
|
173
|
-
line,
|
|
174
|
-
DESC_FONT_SIZE,
|
|
175
|
-
maxTextWidth,
|
|
176
|
-
{
|
|
177
|
-
hardBreak: true,
|
|
178
|
-
}
|
|
179
|
-
).length;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
totalRenderedLines = Math.min(totalRenderedLines, MAX_DESC_LINES);
|
|
183
|
-
const descriptionHeight =
|
|
184
|
-
totalRenderedLines * DESC_FONT_SIZE * DESC_LINE_HEIGHT;
|
|
185
|
-
const totalHeight =
|
|
186
|
-
labelHeight +
|
|
187
|
-
SEPARATOR_GAP +
|
|
188
|
-
DESC_PADDING +
|
|
189
|
-
descriptionHeight +
|
|
190
|
-
DESC_PADDING +
|
|
191
|
-
(reserveValueRow ? VALUE_ROW_H : 0);
|
|
192
|
-
return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ── Main layout ────────────────────────────────────────────
|
|
196
|
-
|
|
197
|
-
export async function layoutBoxesAndLines(
|
|
198
|
-
parsed: ParsedBoxesAndLines,
|
|
199
|
-
collapseInfo?: {
|
|
200
|
-
collapsedChildCounts: Map<string, number>;
|
|
201
|
-
originalGroups: readonly BLGroup[];
|
|
202
|
-
},
|
|
203
|
-
layoutOptions?: {
|
|
204
|
-
hideDescriptions?: boolean;
|
|
205
|
-
collapsedNotes?: ReadonlySet<number>;
|
|
206
|
-
/** Previous node positions (label → {x,y}) for layout stability —
|
|
207
|
-
* minimizes node drift on edit/collapse. */
|
|
208
|
-
previousPositions?: ReadonlyMap<string, { x: number; y: number }>;
|
|
209
|
-
/** Progress hook (interactive path). When set, the search yields between
|
|
210
|
-
* candidates so the UI can paint a "trying X of Y" indicator. */
|
|
211
|
-
onProgress?: (done: number, total: number, phase: string) => void;
|
|
212
|
-
}
|
|
213
|
-
): Promise<BLLayoutResult> {
|
|
214
|
-
const { layoutBoxesAndLinesSearch } = await import('./layout-search');
|
|
215
|
-
const searchOpts = {
|
|
216
|
-
...(layoutOptions?.hideDescriptions !== undefined && {
|
|
217
|
-
hideDescriptions: layoutOptions.hideDescriptions,
|
|
218
|
-
}),
|
|
219
|
-
...(layoutOptions?.previousPositions !== undefined && {
|
|
220
|
-
previousPositions: layoutOptions.previousPositions,
|
|
221
|
-
}),
|
|
222
|
-
...(layoutOptions?.onProgress !== undefined && {
|
|
223
|
-
onProgress: layoutOptions.onProgress,
|
|
224
|
-
}),
|
|
225
|
-
};
|
|
226
|
-
const searched = await layoutBoxesAndLinesSearch(
|
|
227
|
-
parsed,
|
|
228
|
-
collapseInfo,
|
|
229
|
-
searchOpts
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Edge-label legibility (priority ladder): wrap + reposition labels on the
|
|
233
|
-
// chosen layout. If any label still can't clear a node box, escalate ONCE to a
|
|
234
|
-
// label-aware relayout that reserves dagre label space so a gap opens — and
|
|
235
|
-
// keep it only if it actually resolves more labels.
|
|
236
|
-
let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
|
|
237
|
-
if (placed.unresolved.length > 0) {
|
|
238
|
-
const relaid = await layoutBoxesAndLinesSearch(parsed, collapseInfo, {
|
|
239
|
-
...searchOpts,
|
|
240
|
-
reserveEdgeLabels: true,
|
|
241
|
-
});
|
|
242
|
-
const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
|
|
243
|
-
if (relaidPlaced.unresolved.length < placed.unresolved.length)
|
|
244
|
-
placed = relaidPlaced;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Engine-agnostic post-processing: float notes (and shift the canvas to fit
|
|
248
|
-
// them) on the label-placed layout.
|
|
249
|
-
return attachNotes(placed.layout, parsed, layoutOptions?.collapsedNotes);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Float notes beside their boxes on the chosen layout (runs after variant
|
|
254
|
-
* selection — notes don't affect scoring). `no-notes` opts out. A note placed
|
|
255
|
-
* above/left can land off-canvas, so the whole layout is shifted to fit.
|
|
256
|
-
* Un-annotated diagrams are returned unchanged (min coords stay ≥ 0).
|
|
257
|
-
*/
|
|
258
|
-
function attachNotes(
|
|
259
|
-
layout: BLLayoutResult,
|
|
260
|
-
parsed: ParsedBoxesAndLines,
|
|
261
|
-
collapsedNotes?: ReadonlySet<number>
|
|
262
|
-
): BLLayoutResult {
|
|
263
|
-
const notesSuppressed = parsed.options?.['no-notes'] === 'on';
|
|
264
|
-
const noteByNode =
|
|
265
|
-
notesSuppressed || !parsed.notes
|
|
266
|
-
? new Map()
|
|
267
|
-
: resolveNotes(
|
|
268
|
-
parsed.notes,
|
|
269
|
-
parsed.nodes.map((n) => ({ id: n.label, label: n.label }))
|
|
270
|
-
);
|
|
271
|
-
if (noteByNode.size === 0) return layout;
|
|
272
|
-
|
|
273
|
-
const placed = buildPlacedNotes(
|
|
274
|
-
layout.nodes.map((n) => ({
|
|
275
|
-
id: n.label,
|
|
276
|
-
x: n.x,
|
|
277
|
-
y: n.y,
|
|
278
|
-
width: n.width,
|
|
279
|
-
height: n.height,
|
|
280
|
-
})),
|
|
281
|
-
noteByNode,
|
|
282
|
-
parsed.direction === 'TB' ? 'TB' : 'LR',
|
|
283
|
-
collapsedNotes
|
|
284
|
-
);
|
|
285
|
-
const notedNodes: BLLayoutNode[] = layout.nodes.map((n) => {
|
|
286
|
-
const note = placed.get(n.label);
|
|
287
|
-
return note ? { ...n, note } : n;
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// Content bbox over nodes (+ their floated notes) and groups — matches the
|
|
291
|
-
// prior max-extent computation plus the notes.
|
|
292
|
-
let bbMinX = Infinity;
|
|
293
|
-
let bbMinY = Infinity;
|
|
294
|
-
let bbMaxX = -Infinity;
|
|
295
|
-
let bbMaxY = -Infinity;
|
|
296
|
-
const extend = (l: number, t: number, r: number, b: number): void => {
|
|
297
|
-
if (l < bbMinX) bbMinX = l;
|
|
298
|
-
if (t < bbMinY) bbMinY = t;
|
|
299
|
-
if (r > bbMaxX) bbMaxX = r;
|
|
300
|
-
if (b > bbMaxY) bbMaxY = b;
|
|
301
|
-
};
|
|
302
|
-
for (const n of notedNodes) {
|
|
303
|
-
extend(
|
|
304
|
-
n.x - n.width / 2,
|
|
305
|
-
n.y - n.height / 2,
|
|
306
|
-
n.x + n.width / 2,
|
|
307
|
-
n.y + n.height / 2
|
|
308
|
-
);
|
|
309
|
-
if (n.note && !n.note.collapsed) {
|
|
310
|
-
extend(
|
|
311
|
-
n.x + n.note.x,
|
|
312
|
-
n.y + n.note.y,
|
|
313
|
-
n.x + n.note.x + n.note.width,
|
|
314
|
-
n.y + n.note.y + n.note.height
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
for (const grp of layout.groups) {
|
|
319
|
-
extend(
|
|
320
|
-
grp.x - grp.width / 2,
|
|
321
|
-
grp.y - grp.height / 2,
|
|
322
|
-
grp.x + grp.width / 2,
|
|
323
|
-
grp.y + grp.height / 2
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
if (!Number.isFinite(bbMinX)) return { ...layout, nodes: notedNodes };
|
|
327
|
-
|
|
328
|
-
const { shiftX, shiftY } = noteCanvasShift(bbMinX, bbMinY);
|
|
329
|
-
const shifted = shiftX !== 0 || shiftY !== 0;
|
|
330
|
-
const finalNodes = shifted
|
|
331
|
-
? notedNodes.map((n) => ({ ...n, x: n.x + shiftX, y: n.y + shiftY }))
|
|
332
|
-
: notedNodes;
|
|
333
|
-
const finalEdges = shifted
|
|
334
|
-
? layout.edges.map((e) => ({
|
|
335
|
-
...e,
|
|
336
|
-
points: e.points.map((pt) => ({ x: pt.x + shiftX, y: pt.y + shiftY })),
|
|
337
|
-
...(e.labelX !== undefined && { labelX: e.labelX + shiftX }),
|
|
338
|
-
...(e.labelY !== undefined && { labelY: e.labelY + shiftY }),
|
|
339
|
-
// labelWidth/labelHeight/labelLines are shift-invariant — carried via spread.
|
|
340
|
-
}))
|
|
341
|
-
: layout.edges;
|
|
342
|
-
const finalGroups = shifted
|
|
343
|
-
? layout.groups.map((grp) => ({
|
|
344
|
-
...grp,
|
|
345
|
-
x: grp.x + shiftX,
|
|
346
|
-
y: grp.y + shiftY,
|
|
347
|
-
}))
|
|
348
|
-
: layout.groups;
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
nodes: finalNodes,
|
|
352
|
-
edges: finalEdges,
|
|
353
|
-
groups: finalGroups,
|
|
354
|
-
width: bbMaxX + shiftX + MARGIN,
|
|
355
|
-
height: bbMaxY + shiftY + MARGIN,
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Assign parallel-edge fan offsets on any layout (engine-agnostic). Edges sharing
|
|
361
|
-
* an unordered {source,target} pair are bundled at their ports and spread in the
|
|
362
|
-
* middle by the renderer using `yOffset`/`parallelCount`; beyond `MAX_PARALLEL_EDGES`
|
|
363
|
-
* the extras are dropped (`parallelCount: 0` ⇒ renderer skips them). The ELK path
|
|
364
|
-
* computes this inside extractLayout; the search engine produces a single set of
|
|
365
|
-
* points per pair, so it needs the same offsets applied here.
|
|
366
|
-
*/
|
|
367
|
-
function applyParallelEdgeOffsets(layout: BLLayoutResult): BLLayoutResult {
|
|
368
|
-
const groups = new Map<string, number[]>();
|
|
369
|
-
layout.edges.forEach((e, i) => {
|
|
370
|
-
const [a, b] =
|
|
371
|
-
e.source < e.target ? [e.source, e.target] : [e.target, e.source];
|
|
372
|
-
const key = `${a}\x00${b}`;
|
|
373
|
-
const arr = groups.get(key);
|
|
374
|
-
if (arr) arr.push(i);
|
|
375
|
-
else groups.set(key, [i]);
|
|
376
|
-
});
|
|
377
|
-
if ([...groups.values()].every((g) => g.length < 2)) return layout;
|
|
378
|
-
|
|
379
|
-
const yOffset = new Array(layout.edges.length).fill(0);
|
|
380
|
-
const count = new Array(layout.edges.length).fill(1);
|
|
381
|
-
for (const idxs of groups.values()) {
|
|
382
|
-
const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
|
|
383
|
-
for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
|
|
384
|
-
if (capped.length < 2) continue;
|
|
385
|
-
capped.forEach((idx, j) => {
|
|
386
|
-
yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
387
|
-
count[idx] = capped.length;
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
return {
|
|
391
|
-
...layout,
|
|
392
|
-
edges: layout.edges.map((e, i) => ({
|
|
393
|
-
...e,
|
|
394
|
-
yOffset: yOffset[i]!,
|
|
395
|
-
parallelCount: count[i]!,
|
|
396
|
-
})),
|
|
397
|
-
};
|
|
398
|
-
}
|