@diagrammo/dgmo 0.33.0 → 0.35.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/.claude/commands/dgmo.md +17 -32
- package/.github/copilot-instructions.md +1 -1
- package/README.md +7 -1
- package/SKILL.md +1 -1
- package/dist/advanced.cjs +791 -614
- package/dist/advanced.d.cts +8 -51
- package/dist/advanced.d.ts +8 -51
- package/dist/advanced.js +791 -615
- package/dist/auto.cjs +793 -292
- package/dist/auto.js +95 -95
- package/dist/auto.mjs +793 -292
- package/dist/cli.cjs +145 -328
- package/dist/index.cjs +792 -291
- package/dist/index.js +792 -291
- package/docs/ai-integration.md +46 -26
- package/docs/language-reference.md +2 -2
- package/package.json +3 -3
- package/src/advanced.ts +0 -12
- package/src/boxes-and-lines/label-placement.ts +339 -0
- package/src/boxes-and-lines/layout-search.ts +16 -2
- package/src/boxes-and-lines/layout.ts +32 -7
- package/src/boxes-and-lines/renderer.ts +67 -139
- package/src/c4/parser.ts +15 -6
- package/src/chart.ts +1 -1
- package/src/class/parser.ts +6 -2
- package/src/cli-banner.ts +2 -3
- package/src/cli.ts +539 -1223
- package/src/cycle/parser.ts +4 -1
- package/src/dgmo-router.ts +16 -2
- package/src/diagnostics.ts +0 -0
- package/src/echarts.ts +5 -1
- package/src/er/parser.ts +5 -1
- package/src/gantt/parser.ts +13 -4
- package/src/graph/flowchart-parser.ts +5 -1
- package/src/graph/state-parser.ts +5 -1
- package/src/infra/parser.ts +10 -6
- package/src/journey-map/parser.ts +5 -2
- package/src/kanban/parser.ts +6 -2
- package/src/map/context-labels.ts +29 -19
- package/src/map/layout.ts +403 -108
- package/src/map/legend-band.ts +28 -0
- package/src/map/parser.ts +13 -4
- package/src/map/renderer.ts +74 -23
- package/src/map/resolver.ts +2 -2
- package/src/org/parser.ts +8 -2
- package/src/pyramid/parser.ts +4 -1
- package/src/raci/parser.ts +4 -1
- package/src/ring/parser.ts +4 -1
- package/src/sequence/parser.ts +6 -3
- package/src/sitemap/parser.ts +12 -3
- package/src/tech-radar/parser.ts +6 -3
- package/src/utils/parsing.ts +2 -2
- package/src/utils/tag-groups.ts +3 -3
- package/src/visualizations/parse.ts +9 -3
- package/src/migrate/data-comma.ts +0 -117
- package/src/migrate/embedded.ts +0 -238
- package/src/migrate/index.ts +0 -295
- package/src/migrate/line-classifier.ts +0 -128
- package/src/migrate/line-transformer.ts +0 -218
package/dist/index.js
CHANGED
|
@@ -35,6 +35,17 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
35
35
|
function makeDgmoError(line11, message, severity = "error", code) {
|
|
36
36
|
return code !== void 0 ? { line: line11, message, severity, code } : { line: line11, message, severity };
|
|
37
37
|
}
|
|
38
|
+
function dedupeDiagnostics(diagnostics) {
|
|
39
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40
|
+
const out = [];
|
|
41
|
+
for (const d of diagnostics) {
|
|
42
|
+
const key = `${d.line}\0${d.column ?? ""}\0${d.severity}\0${d.code ?? ""}\0${d.message}`;
|
|
43
|
+
if (seen.has(key)) continue;
|
|
44
|
+
seen.add(key);
|
|
45
|
+
out.push(d);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
38
49
|
function formatDgmoError(err) {
|
|
39
50
|
return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
|
|
40
51
|
}
|
|
@@ -92,7 +103,7 @@ function vennAliasKeywordRemovedMessage(args) {
|
|
|
92
103
|
return `Venn 'alias' keyword was removed. Use 'as' instead \u2014 '${args.name} as ${args.alias}'.`;
|
|
93
104
|
}
|
|
94
105
|
function pipeOperatorRemovedMessage() {
|
|
95
|
-
return `'|' removed \u2014 'Node | c: red' \u2192 'Node c: red'
|
|
106
|
+
return `'|' removed \u2014 'Node | c: red' \u2192 'Node c: red'`;
|
|
96
107
|
}
|
|
97
108
|
function ganttBarePercentRemovedMessage(percent) {
|
|
98
109
|
return `Bare-percent progress shorthand '| ${percent}' was removed. Use 'progress: ${percent.replace(/%$/, "")}' (integer 0\u2013100, no '%').`;
|
|
@@ -1148,7 +1159,7 @@ var init_parsing = __esm({
|
|
|
1148
1159
|
"no-title",
|
|
1149
1160
|
"no-notes"
|
|
1150
1161
|
]);
|
|
1151
|
-
MULTIPLE_PIPE_ERROR = '
|
|
1162
|
+
MULTIPLE_PIPE_ERROR = "'|' is no longer metadata syntax \u2014 put metadata on the same line as 'key: value, key2: value2' (no pipes), per \xA71.4.";
|
|
1152
1163
|
RESERVED_KEY_RE = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
1153
1164
|
AS_ALIAS_RE = /\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11})$/;
|
|
1154
1165
|
POTENTIAL_META_RE = /\b([\w-]+)\s*:/;
|
|
@@ -1329,12 +1340,10 @@ function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
|
|
|
1329
1340
|
);
|
|
1330
1341
|
if (!isPrefix) {
|
|
1331
1342
|
const defined = group.entries.map((e) => e.value);
|
|
1332
|
-
let msg = `Unknown value '${value}' for tag group '${group.name}'`;
|
|
1343
|
+
let msg = `Unknown value '${value}' for tag group '${group.name}' \u2014 use one of: ${defined.join(", ")}`;
|
|
1333
1344
|
const hint = suggestFn?.(value, defined);
|
|
1334
1345
|
if (hint) {
|
|
1335
1346
|
msg += `. ${hint}`;
|
|
1336
|
-
} else {
|
|
1337
|
-
msg += ` \u2014 defined values: ${defined.join(", ")}`;
|
|
1338
1347
|
}
|
|
1339
1348
|
pushWarning(entity.lineNumber, msg);
|
|
1340
1349
|
}
|
|
@@ -2110,7 +2119,10 @@ function parseVisualizationFull(content, palette) {
|
|
|
2110
2119
|
lineNumber
|
|
2111
2120
|
});
|
|
2112
2121
|
} else {
|
|
2113
|
-
warn(
|
|
2122
|
+
warn(
|
|
2123
|
+
lineNumber,
|
|
2124
|
+
`Unrecognized era entry: "${line11}" \u2014 use '<start> -> <end> Label [color]' (e.g. '1716 -> 1717 Reign').`
|
|
2125
|
+
);
|
|
2114
2126
|
}
|
|
2115
2127
|
continue;
|
|
2116
2128
|
}
|
|
@@ -2138,7 +2150,10 @@ function parseVisualizationFull(content, palette) {
|
|
|
2138
2150
|
lineNumber
|
|
2139
2151
|
});
|
|
2140
2152
|
} else {
|
|
2141
|
-
warn(
|
|
2153
|
+
warn(
|
|
2154
|
+
lineNumber,
|
|
2155
|
+
`Unrecognized marker entry: "${line11}" \u2014 use '<date> Label [color]' (e.g. '1776 Independence').`
|
|
2156
|
+
);
|
|
2142
2157
|
}
|
|
2143
2158
|
continue;
|
|
2144
2159
|
}
|
|
@@ -2816,7 +2831,7 @@ function parseVisualizationFull(content, palette) {
|
|
|
2816
2831
|
result.diagnostics.push(
|
|
2817
2832
|
makeDgmoError(
|
|
2818
2833
|
ov.lineNumber,
|
|
2819
|
-
`Intersection references unknown set or alias "${ref}"`
|
|
2834
|
+
`Intersection references unknown set or alias "${ref}" \u2014 declare the set first on its own line ('Name [as <alias>] [color]', e.g. 'Navigation as nav blue'), then reference it by that name or alias. (\xA716.4)`
|
|
2820
2835
|
)
|
|
2821
2836
|
);
|
|
2822
2837
|
if (!result.error)
|
|
@@ -6260,10 +6275,10 @@ function parseSequenceDgmo(content, palette) {
|
|
|
6260
6275
|
const rawInside = fallbackMatch[1];
|
|
6261
6276
|
const pipeIdx = rawInside.indexOf("|");
|
|
6262
6277
|
const cleanName = rawInside.substring(0, pipeIdx).trim().replace(/\([^)]*\)$/, "").trim();
|
|
6263
|
-
const metaPart = rawInside.substring(pipeIdx).trim();
|
|
6278
|
+
const metaPart = rawInside.substring(pipeIdx).replace(/^\|\s*/, "").trim();
|
|
6264
6279
|
pushError(
|
|
6265
6280
|
lineNumber,
|
|
6266
|
-
`
|
|
6281
|
+
`Metadata goes after the bracket with no pipe \u2014 use '[${cleanName}] ${metaPart}' (e.g. '[Backend] t: Product'), not '[${rawInside.trim()}]'`
|
|
6267
6282
|
);
|
|
6268
6283
|
continue;
|
|
6269
6284
|
}
|
|
@@ -6873,7 +6888,7 @@ function parseSequenceDgmo(content, palette) {
|
|
|
6873
6888
|
}
|
|
6874
6889
|
pushWarning(
|
|
6875
6890
|
lineNumber,
|
|
6876
|
-
`Unexpected line: '${trimmed}'. Expected a message (A -> B), participant, section (== Name ==), or block
|
|
6891
|
+
`Unexpected line: '${trimmed}'. Expected a message ('A -> B' or 'A -label-> B'), participant ('Name' or 'Name is a actor'), note ('note right of A Text'), section ('== Name =='), or block ('if/loop/parallel <label>').`
|
|
6877
6892
|
);
|
|
6878
6893
|
}
|
|
6879
6894
|
if (!hasExplicitChart && result.messages.length === 0) {
|
|
@@ -8035,7 +8050,11 @@ function parseFlowchart(content, palette) {
|
|
|
8035
8050
|
const value = optMatch[2].trim();
|
|
8036
8051
|
if (key === "no-color") {
|
|
8037
8052
|
result.diagnostics.push(
|
|
8038
|
-
makeDgmoError(
|
|
8053
|
+
makeDgmoError(
|
|
8054
|
+
lineNumber,
|
|
8055
|
+
'"no-color" was removed \u2014 node colors are assigned automatically by shape; delete this line.',
|
|
8056
|
+
"warning"
|
|
8057
|
+
)
|
|
8039
8058
|
);
|
|
8040
8059
|
continue;
|
|
8041
8060
|
}
|
|
@@ -8364,7 +8383,11 @@ function parseState(content, palette) {
|
|
|
8364
8383
|
const value = optMatch[2].trim();
|
|
8365
8384
|
if (key === "no-color") {
|
|
8366
8385
|
result.diagnostics.push(
|
|
8367
|
-
makeDgmoError(
|
|
8386
|
+
makeDgmoError(
|
|
8387
|
+
lineNumber,
|
|
8388
|
+
'"no-color" was removed \u2014 state colors are assigned automatically; delete this line.',
|
|
8389
|
+
"warning"
|
|
8390
|
+
)
|
|
8368
8391
|
);
|
|
8369
8392
|
continue;
|
|
8370
8393
|
}
|
|
@@ -8679,7 +8702,7 @@ function parseClassDiagram(content, palette) {
|
|
|
8679
8702
|
result.diagnostics.push(
|
|
8680
8703
|
makeDgmoError(
|
|
8681
8704
|
lineNumber,
|
|
8682
|
-
'"no-auto-color"
|
|
8705
|
+
'"no-auto-color" was removed \u2014 colors are always auto-assigned from the palette; delete this line.',
|
|
8683
8706
|
"warning"
|
|
8684
8707
|
)
|
|
8685
8708
|
);
|
|
@@ -8794,7 +8817,11 @@ function parseClassDiagram(content, palette) {
|
|
|
8794
8817
|
continue;
|
|
8795
8818
|
}
|
|
8796
8819
|
result.diagnostics.push(
|
|
8797
|
-
makeDgmoError(
|
|
8820
|
+
makeDgmoError(
|
|
8821
|
+
lineNumber,
|
|
8822
|
+
`Unexpected line: '${trimmed}'. Classes start uppercase at indent 0 ('Ship'); members are indented and colon-typed ('+ name: string', '+ sail(): void'); relationships are indented arrows ('--|> Vessel'). (\xA710)`,
|
|
8823
|
+
"warning"
|
|
8824
|
+
)
|
|
8798
8825
|
);
|
|
8799
8826
|
}
|
|
8800
8827
|
if (notes.length > 0) {
|
|
@@ -9272,7 +9299,11 @@ function parseERDiagram(content, palette) {
|
|
|
9272
9299
|
continue;
|
|
9273
9300
|
}
|
|
9274
9301
|
result.diagnostics.push(
|
|
9275
|
-
makeDgmoError(
|
|
9302
|
+
makeDgmoError(
|
|
9303
|
+
lineNumber,
|
|
9304
|
+
`Unexpected line: '${trimmed}'. A table is a bare name at indent 0 ('users'); columns are indented and SPACE-separated, no colon ('id int pk'); relationships are indented ('1--* orders'). (\xA79)`,
|
|
9305
|
+
"warning"
|
|
9306
|
+
)
|
|
9276
9307
|
);
|
|
9277
9308
|
}
|
|
9278
9309
|
if (result.tables.length === 0 && !result.error) {
|
|
@@ -9616,7 +9647,10 @@ function parseOrg(content, palette) {
|
|
|
9616
9647
|
const value = metadataMatch[2].trim();
|
|
9617
9648
|
const parent = findMetadataParent(indent, indentStack);
|
|
9618
9649
|
if (!parent) {
|
|
9619
|
-
pushError(
|
|
9650
|
+
pushError(
|
|
9651
|
+
lineNumber,
|
|
9652
|
+
"Metadata 'key: value' must be indented under a node \u2014 no node precedes it."
|
|
9653
|
+
);
|
|
9620
9654
|
} else {
|
|
9621
9655
|
parent.metadata = { ...parent.metadata, [key]: value };
|
|
9622
9656
|
}
|
|
@@ -9634,7 +9668,10 @@ function parseOrg(content, palette) {
|
|
|
9634
9668
|
);
|
|
9635
9669
|
attachNode(node, indent, indentStack, result);
|
|
9636
9670
|
} else {
|
|
9637
|
-
pushError(
|
|
9671
|
+
pushError(
|
|
9672
|
+
lineNumber,
|
|
9673
|
+
"Metadata 'key: value' must be indented under a node \u2014 no node precedes it."
|
|
9674
|
+
);
|
|
9638
9675
|
}
|
|
9639
9676
|
} else {
|
|
9640
9677
|
const node = parseNodeLabel(
|
|
@@ -9983,7 +10020,10 @@ function parseSitemap(content, palette) {
|
|
|
9983
10020
|
if (arrowInfo) {
|
|
9984
10021
|
const source = findParentNode(indent, indentStack);
|
|
9985
10022
|
if (!source) {
|
|
9986
|
-
pushError(
|
|
10023
|
+
pushError(
|
|
10024
|
+
lineNumber,
|
|
10025
|
+
"Arrow '-> target' must be indented under its source page \u2014 no page precedes it."
|
|
10026
|
+
);
|
|
9987
10027
|
} else {
|
|
9988
10028
|
deferredArrows.push({
|
|
9989
10029
|
sourceNode: source,
|
|
@@ -10053,7 +10093,10 @@ function parseSitemap(content, palette) {
|
|
|
10053
10093
|
const value = metadataMatch[2].trim();
|
|
10054
10094
|
const parent = findParentNode(indent, indentStack);
|
|
10055
10095
|
if (!parent) {
|
|
10056
|
-
pushError(
|
|
10096
|
+
pushError(
|
|
10097
|
+
lineNumber,
|
|
10098
|
+
"Metadata 'key: value' must be indented under a page \u2014 no page precedes it."
|
|
10099
|
+
);
|
|
10057
10100
|
} else {
|
|
10058
10101
|
parent.metadata = { ...parent.metadata, [key]: value };
|
|
10059
10102
|
}
|
|
@@ -10073,7 +10116,10 @@ function parseSitemap(content, palette) {
|
|
|
10073
10116
|
const key = normalizeName(node.label);
|
|
10074
10117
|
labelToNode.set(key, node);
|
|
10075
10118
|
} else {
|
|
10076
|
-
pushError(
|
|
10119
|
+
pushError(
|
|
10120
|
+
lineNumber,
|
|
10121
|
+
"Metadata 'key: value' must be indented under a page \u2014 no page precedes it."
|
|
10122
|
+
);
|
|
10077
10123
|
}
|
|
10078
10124
|
} else {
|
|
10079
10125
|
const descResult = tryStripDescriptionKeyword(trimmed);
|
|
@@ -11891,7 +11937,7 @@ function parseChart(content, palette) {
|
|
|
11891
11937
|
result.diagnostics.push(
|
|
11892
11938
|
makeDgmoError(
|
|
11893
11939
|
lineNumber,
|
|
11894
|
-
`'${trimmed}' \u2014 ##
|
|
11940
|
+
`'${trimmed}' removed \u2014 use a bracket container '[${trimmed.replace(/^#+\s*/, "")}]' instead (## headers are no longer supported).`
|
|
11895
11941
|
)
|
|
11896
11942
|
);
|
|
11897
11943
|
continue;
|
|
@@ -12870,7 +12916,11 @@ function parseExtendedChartFull(content, palette) {
|
|
|
12870
12916
|
continue;
|
|
12871
12917
|
}
|
|
12872
12918
|
result.diagnostics.push(
|
|
12873
|
-
makeDgmoError(
|
|
12919
|
+
makeDgmoError(
|
|
12920
|
+
lineNumber,
|
|
12921
|
+
`Unexpected line: '${trimmed}'. Expected a data row ('Label value', space-separated) or a known option.`,
|
|
12922
|
+
"warning"
|
|
12923
|
+
)
|
|
12874
12924
|
);
|
|
12875
12925
|
}
|
|
12876
12926
|
const warn = (line11, message) => {
|
|
@@ -15228,7 +15278,10 @@ function parseKanban(content, palette) {
|
|
|
15228
15278
|
continue;
|
|
15229
15279
|
}
|
|
15230
15280
|
if (!currentColumn) {
|
|
15231
|
-
warn(
|
|
15281
|
+
warn(
|
|
15282
|
+
lineNumber,
|
|
15283
|
+
"Card line found before any column. Declare a column first with '[Column Name]', then indent cards beneath it. (\xA711)"
|
|
15284
|
+
);
|
|
15232
15285
|
continue;
|
|
15233
15286
|
}
|
|
15234
15287
|
if (currentCard && indent > cardBaseIndent) {
|
|
@@ -15367,7 +15420,7 @@ var init_parser13 = __esm({
|
|
|
15367
15420
|
KNOWN_OPTIONS4 = /* @__PURE__ */ new Set(["hide", "active-tag"]);
|
|
15368
15421
|
KNOWN_BOOLEANS3 = /* @__PURE__ */ new Set(["solid-fill", "no-title"]);
|
|
15369
15422
|
REMOVED_BOOLEANS = {
|
|
15370
|
-
"no-auto-color": '"no-auto-color"
|
|
15423
|
+
"no-auto-color": '"no-auto-color" was removed \u2014 colors are always auto-assigned from the palette; delete this line.'
|
|
15371
15424
|
};
|
|
15372
15425
|
}
|
|
15373
15426
|
});
|
|
@@ -15604,7 +15657,7 @@ function parseC4(content, palette) {
|
|
|
15604
15657
|
} else {
|
|
15605
15658
|
pushError(
|
|
15606
15659
|
lineNumber,
|
|
15607
|
-
`"container ${refName}" must be
|
|
15660
|
+
`"container ${refName}" must be indented under a deployment node (e.g. under 'Prod is a deployment-node')`
|
|
15608
15661
|
);
|
|
15609
15662
|
}
|
|
15610
15663
|
continue;
|
|
@@ -15659,7 +15712,10 @@ function parseC4(content, palette) {
|
|
|
15659
15712
|
indent
|
|
15660
15713
|
});
|
|
15661
15714
|
} else {
|
|
15662
|
-
pushError(
|
|
15715
|
+
pushError(
|
|
15716
|
+
lineNumber,
|
|
15717
|
+
`"${sectionType}" must be indented under an element (e.g. under 'Web App is a container')`
|
|
15718
|
+
);
|
|
15663
15719
|
}
|
|
15664
15720
|
continue;
|
|
15665
15721
|
}
|
|
@@ -15686,7 +15742,10 @@ function parseC4(content, palette) {
|
|
|
15686
15742
|
indent
|
|
15687
15743
|
});
|
|
15688
15744
|
} else {
|
|
15689
|
-
pushError(
|
|
15745
|
+
pushError(
|
|
15746
|
+
lineNumber,
|
|
15747
|
+
`Group [${groupName}] must be indented under an element (e.g. under 'Web App is a container')`
|
|
15748
|
+
);
|
|
15690
15749
|
}
|
|
15691
15750
|
continue;
|
|
15692
15751
|
}
|
|
@@ -16016,7 +16075,10 @@ function parseC4(content, palette) {
|
|
|
16016
16075
|
}
|
|
16017
16076
|
desc.push(descResult.text);
|
|
16018
16077
|
} else {
|
|
16019
|
-
pushError(
|
|
16078
|
+
pushError(
|
|
16079
|
+
lineNumber,
|
|
16080
|
+
`Unexpected content: "${trimmed}". Expected an element ('Web App is a container'), a relationship ('-Uses-> Database'), indented metadata ('tech: React'), or a group ('[Backend]'). (\xA78)`
|
|
16081
|
+
);
|
|
16020
16082
|
}
|
|
16021
16083
|
}
|
|
16022
16084
|
finalizeAutoTagColors(result.tagGroups, palette);
|
|
@@ -16078,7 +16140,7 @@ function validateRelationshipTargets(result, knownNames, pushWarning) {
|
|
|
16078
16140
|
for (const el of elements) {
|
|
16079
16141
|
for (const rel of el.relationships) {
|
|
16080
16142
|
if (!knownNames.has(normalizeName(rel.target))) {
|
|
16081
|
-
let msg = `Relationship target "${rel.target}" not found`;
|
|
16143
|
+
let msg = `Relationship target "${rel.target}" not found \u2014 declare it first (e.g. '${rel.target} is a container'), then reference it in the arrow`;
|
|
16082
16144
|
const hint = suggest(normalizeName(rel.target), allNames);
|
|
16083
16145
|
if (hint) msg += `. ${hint}`;
|
|
16084
16146
|
pushWarning(rel.lineNumber, msg, "warning");
|
|
@@ -16106,7 +16168,7 @@ function validateDeploymentRefs(result, knownNames, pushWarning) {
|
|
|
16106
16168
|
for (const node of nodes) {
|
|
16107
16169
|
for (const ref of node.containerRefs) {
|
|
16108
16170
|
if (!knownNames.has(normalizeName(ref))) {
|
|
16109
|
-
let msg = `Deployment reference "container ${ref}" not found`;
|
|
16171
|
+
let msg = `Deployment reference "container ${ref}" not found \u2014 declare the container element first, then reference it under the deployment node`;
|
|
16110
16172
|
const hint = suggest(normalizeName(ref), allNames);
|
|
16111
16173
|
if (hint) msg += `. ${hint}`;
|
|
16112
16174
|
pushWarning(node.lineNumber, msg, "warning");
|
|
@@ -16825,7 +16887,7 @@ function parseInfra(content) {
|
|
|
16825
16887
|
}
|
|
16826
16888
|
warn(
|
|
16827
16889
|
lineNumber,
|
|
16828
|
-
`Invalid tag value '${trimmed}' in tag group '${currentTagGroup.name}'.`
|
|
16890
|
+
`Invalid tag value '${trimmed}' in tag group '${currentTagGroup.name}' \u2014 a value is a bare word with an optional trailing color (e.g. 'Production red'); no ':' or '|'.`
|
|
16829
16891
|
);
|
|
16830
16892
|
continue;
|
|
16831
16893
|
}
|
|
@@ -16898,7 +16960,7 @@ function parseInfra(content) {
|
|
|
16898
16960
|
const n = deprecatedFanout[1];
|
|
16899
16961
|
setError(
|
|
16900
16962
|
lineNumber,
|
|
16901
|
-
`'x${n}' fanout syntax is no longer supported \u2014 use '
|
|
16963
|
+
`'x${n}' fanout syntax is no longer supported \u2014 use 'fanout: ${n}' instead`
|
|
16902
16964
|
);
|
|
16903
16965
|
continue;
|
|
16904
16966
|
}
|
|
@@ -17072,8 +17134,10 @@ function parseInfra(content) {
|
|
|
17072
17134
|
const hint = suggest(key, allKeys);
|
|
17073
17135
|
const valueLooksNumeric = /^[\d.]+%?$/.test(rawVal);
|
|
17074
17136
|
if (hint || valueLooksNumeric) {
|
|
17075
|
-
let msg = `Unknown property '${key}'.`;
|
|
17137
|
+
let msg = `Unknown infra property '${key}'.`;
|
|
17076
17138
|
if (hint) msg += ` ${hint}`;
|
|
17139
|
+
else
|
|
17140
|
+
msg += ` Properties are colon-keyed 'key: value' from the known schema (e.g. latency-ms, max-rps, cache-hit, instances, concurrency).`;
|
|
17077
17141
|
warn(lineNumber, msg);
|
|
17078
17142
|
} else if (!currentNode.isEdge) {
|
|
17079
17143
|
pushDescription(currentNode, trimmed);
|
|
@@ -17084,7 +17148,7 @@ function parseInfra(content) {
|
|
|
17084
17148
|
if (EDGE_ONLY_KEYS.has(key) && !currentNode.isEdge) {
|
|
17085
17149
|
warn(
|
|
17086
17150
|
lineNumber,
|
|
17087
|
-
`Property '${key}'
|
|
17151
|
+
`Property '${key}' belongs on the entry point \u2014 declare it indented under an 'internet' or 'edge' node, not on '${currentNode.label}'.`
|
|
17088
17152
|
);
|
|
17089
17153
|
}
|
|
17090
17154
|
const value = parsePropertyValue(rawVal);
|
|
@@ -17191,7 +17255,7 @@ function parseInfra(content) {
|
|
|
17191
17255
|
}
|
|
17192
17256
|
warn(
|
|
17193
17257
|
lineNumber,
|
|
17194
|
-
currentGroup ? `Unexpected line: '${trimmed}'.
|
|
17258
|
+
currentGroup ? `Unexpected line: '${trimmed}'. Inside a group, indent a component name under the '[Group]'.` : `Unexpected line: '${trimmed}'. Expected a node ('Api Gateway'), a group ('[Backend]'), a tag group ('tag Tier as t'), or an option ('default-rps 100'); node properties go indented under a node as 'key: value'.`
|
|
17195
17259
|
);
|
|
17196
17260
|
}
|
|
17197
17261
|
finishCurrentNode();
|
|
@@ -17567,7 +17631,10 @@ function parseGantt(content, palette) {
|
|
|
17567
17631
|
continue;
|
|
17568
17632
|
}
|
|
17569
17633
|
if (COMMENT_RE.test(line11)) continue;
|
|
17570
|
-
warn(
|
|
17634
|
+
warn(
|
|
17635
|
+
lineNumber,
|
|
17636
|
+
`Unrecognized holiday entry: "${line11}". Use a date '2024-02-19 Presidents Day' or a range '2024-05-27 -> 2024-05-29 Memorial Weekend'. (\xA713.3)`
|
|
17637
|
+
);
|
|
17571
17638
|
continue;
|
|
17572
17639
|
}
|
|
17573
17640
|
}
|
|
@@ -17593,7 +17660,10 @@ function parseGantt(content, palette) {
|
|
|
17593
17660
|
lineNumber
|
|
17594
17661
|
});
|
|
17595
17662
|
} else {
|
|
17596
|
-
warn(
|
|
17663
|
+
warn(
|
|
17664
|
+
lineNumber,
|
|
17665
|
+
`Unrecognized era entry: "${line11}". Use 'YYYY-MM-DD -> YYYY-MM-DD Label [color]' (e.g. '2026-04-06 -> 2026-04-10 Conference purple'). (\xA713.5)`
|
|
17666
|
+
);
|
|
17597
17667
|
}
|
|
17598
17668
|
continue;
|
|
17599
17669
|
}
|
|
@@ -17619,7 +17689,10 @@ function parseGantt(content, palette) {
|
|
|
17619
17689
|
lineNumber
|
|
17620
17690
|
});
|
|
17621
17691
|
} else {
|
|
17622
|
-
warn(
|
|
17692
|
+
warn(
|
|
17693
|
+
lineNumber,
|
|
17694
|
+
`Unrecognized marker entry: "${line11}". Use 'YYYY-MM-DD Label [color]' (e.g. '2026-03-27 Board Review' or '2026-06-15 Release green'). (\xA713.6)`
|
|
17695
|
+
);
|
|
17623
17696
|
}
|
|
17624
17697
|
continue;
|
|
17625
17698
|
}
|
|
@@ -17785,7 +17858,7 @@ function parseGantt(content, palette) {
|
|
|
17785
17858
|
} else {
|
|
17786
17859
|
softError(
|
|
17787
17860
|
lineNumber,
|
|
17788
|
-
`Arrow
|
|
17861
|
+
`Arrow '-> ${content2.name}' has no source task \u2014 indent it under the task it follows (e.g. 'Design 5bd' then indented '-> ${content2.name} 3bd'). (\xA713)`
|
|
17789
17862
|
);
|
|
17790
17863
|
}
|
|
17791
17864
|
}
|
|
@@ -19005,7 +19078,10 @@ function parseMap(content, palette) {
|
|
|
19005
19078
|
function handleTag(trimmed, line11) {
|
|
19006
19079
|
const m = matchTagBlockHeading(trimmed);
|
|
19007
19080
|
if (!m) {
|
|
19008
|
-
pushError(
|
|
19081
|
+
pushError(
|
|
19082
|
+
line11,
|
|
19083
|
+
`Malformed tag declaration: "${trimmed}" \u2014 write 'tag <Group>' (optionally 'tag <Group> as <alias>'), e.g. 'tag Market as m', with indented 'Value color' entries below.`
|
|
19084
|
+
);
|
|
19009
19085
|
return;
|
|
19010
19086
|
}
|
|
19011
19087
|
emitTagLegacyDiagnostic(m, line11, diagnostics);
|
|
@@ -19117,7 +19193,7 @@ function parseMap(content, palette) {
|
|
|
19117
19193
|
if (!pos || pos.kind === "name" && !pos.name) {
|
|
19118
19194
|
pushError(
|
|
19119
19195
|
line11,
|
|
19120
|
-
"route
|
|
19196
|
+
"route needs an origin place \u2014 `route <origin>` then indented legs (`-> dest`); origin may be a city name, IATA code, or `<lat> <lon>`. (\xA724B)"
|
|
19121
19197
|
);
|
|
19122
19198
|
return;
|
|
19123
19199
|
}
|
|
@@ -19180,7 +19256,10 @@ function parseMap(content, palette) {
|
|
|
19180
19256
|
function handleEdges(trimmed, line11) {
|
|
19181
19257
|
const parts = trimmed.split(ARROW_SPLIT);
|
|
19182
19258
|
if (parts.length < 3) {
|
|
19183
|
-
pushError(
|
|
19259
|
+
pushError(
|
|
19260
|
+
line11,
|
|
19261
|
+
`Malformed edge: "${trimmed}" \u2014 an edge is 'A -> B' (or 'A -label-> B', '~>' for arc, '--' undirected), both endpoints non-empty. (\xA724B)`
|
|
19262
|
+
);
|
|
19184
19263
|
return;
|
|
19185
19264
|
}
|
|
19186
19265
|
const endpoints = [];
|
|
@@ -19203,7 +19282,10 @@ function parseMap(content, palette) {
|
|
|
19203
19282
|
const from = endpoints[k];
|
|
19204
19283
|
const to = endpoints[k + 1];
|
|
19205
19284
|
if (!from || !to) {
|
|
19206
|
-
pushError(
|
|
19285
|
+
pushError(
|
|
19286
|
+
line11,
|
|
19287
|
+
`Edge has an empty endpoint: "${trimmed}" \u2014 both sides of the arrow need a place, e.g. 'Paris -> Berlin'. (\xA724B)`
|
|
19288
|
+
);
|
|
19207
19289
|
continue;
|
|
19208
19290
|
}
|
|
19209
19291
|
const isLast = k === links.length - 1;
|
|
@@ -21322,7 +21404,10 @@ function parseTechRadar(content) {
|
|
|
21322
21404
|
}
|
|
21323
21405
|
if (indent === 0 && trimmed.toLowerCase() === "rings") {
|
|
21324
21406
|
if (result.rings.length > 0) {
|
|
21325
|
-
warn(
|
|
21407
|
+
warn(
|
|
21408
|
+
lineNumber,
|
|
21409
|
+
'Duplicate "rings" block \u2014 only the last is kept; merge all ring names into one "rings" block.'
|
|
21410
|
+
);
|
|
21326
21411
|
result.rings = [];
|
|
21327
21412
|
}
|
|
21328
21413
|
inRingsBlock = true;
|
|
@@ -21541,7 +21626,7 @@ function parseTechRadar(content) {
|
|
|
21541
21626
|
result.diagnostics.push(
|
|
21542
21627
|
makeDgmoError(
|
|
21543
21628
|
lineNumber,
|
|
21544
|
-
`Blip "${trimmed}" has no ring
|
|
21629
|
+
`Blip "${trimmed}" has no ring. Put it under a ring section header, or add a colon-keyed ring \u2014 '${trimmed} ring: RingName' (per \xA720).`
|
|
21545
21630
|
)
|
|
21546
21631
|
);
|
|
21547
21632
|
continue;
|
|
@@ -21550,7 +21635,7 @@ function parseTechRadar(content) {
|
|
|
21550
21635
|
if (!trimmed.includes("|") && headerParsed && result.rings.length > 0) {
|
|
21551
21636
|
warn(
|
|
21552
21637
|
lineNumber,
|
|
21553
|
-
`Unrecognized line "${trimmed}".
|
|
21638
|
+
`Unrecognized line "${trimmed}". A quadrant header is 'Name quadrant: <top-left|top-right|bottom-left|bottom-right>' (per \xA720).`
|
|
21554
21639
|
);
|
|
21555
21640
|
}
|
|
21556
21641
|
}
|
|
@@ -21843,7 +21928,10 @@ function parseCycle(content) {
|
|
|
21843
21928
|
currentNode.description.push(descLine);
|
|
21844
21929
|
continue;
|
|
21845
21930
|
}
|
|
21846
|
-
warn(
|
|
21931
|
+
warn(
|
|
21932
|
+
lineNum,
|
|
21933
|
+
`Indented line "${trimmed}" has no step above it \u2014 add a step on a non-indented line first, then indent its details. (\xA721)`
|
|
21934
|
+
);
|
|
21847
21935
|
continue;
|
|
21848
21936
|
}
|
|
21849
21937
|
}
|
|
@@ -22146,7 +22234,7 @@ function parseJourneyMap(content, palette) {
|
|
|
22146
22234
|
if (trimmed.includes("|") || !PHASE_RE.test(trimmed)) {
|
|
22147
22235
|
warn(
|
|
22148
22236
|
lineNumber,
|
|
22149
|
-
"
|
|
22237
|
+
`Step "${trimmed}" sits outside any phase and is ignored \u2014 indent it under a '[Phase]' header (per \xA722).`
|
|
22150
22238
|
);
|
|
22151
22239
|
continue;
|
|
22152
22240
|
}
|
|
@@ -22203,7 +22291,10 @@ function parseJourneyMap(content, palette) {
|
|
|
22203
22291
|
continue;
|
|
22204
22292
|
}
|
|
22205
22293
|
if (indent > 0 && !currentPhase && hasPhases) {
|
|
22206
|
-
warn(
|
|
22294
|
+
warn(
|
|
22295
|
+
lineNumber,
|
|
22296
|
+
`Indented line outside any phase \u2014 add a '[Phase]' header above it, or unindent it to a top-level step. (\xA722)`
|
|
22297
|
+
);
|
|
22207
22298
|
}
|
|
22208
22299
|
}
|
|
22209
22300
|
if (currentStep) {
|
|
@@ -22549,7 +22640,10 @@ function parsePyramid(content) {
|
|
|
22549
22640
|
continue;
|
|
22550
22641
|
}
|
|
22551
22642
|
if (!currentLayer) {
|
|
22552
|
-
warn(
|
|
22643
|
+
warn(
|
|
22644
|
+
lineNum,
|
|
22645
|
+
`Indented description "${trimmed}" has no layer above it \u2014 add a layer name on a non-indented line first. (\xA723)`
|
|
22646
|
+
);
|
|
22553
22647
|
continue;
|
|
22554
22648
|
}
|
|
22555
22649
|
const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
|
|
@@ -22723,7 +22817,10 @@ function parseRing(content) {
|
|
|
22723
22817
|
continue;
|
|
22724
22818
|
}
|
|
22725
22819
|
if (!currentLayer) {
|
|
22726
|
-
warn(
|
|
22820
|
+
warn(
|
|
22821
|
+
lineNum,
|
|
22822
|
+
`Indented description "${trimmed}" has no layer above it \u2014 add a layer name on a non-indented line first. (\xA724)`
|
|
22823
|
+
);
|
|
22727
22824
|
continue;
|
|
22728
22825
|
}
|
|
22729
22826
|
const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
|
|
@@ -23449,7 +23546,10 @@ function parseRaci(content, palette) {
|
|
|
23449
23546
|
continue;
|
|
23450
23547
|
}
|
|
23451
23548
|
if (bodyStarted) {
|
|
23452
|
-
errorAt(
|
|
23549
|
+
errorAt(
|
|
23550
|
+
lineNumber,
|
|
23551
|
+
`Unexpected line: '${trimmed}'. In the body expect a '[Phase]' header, an indented task name, or an assignment 'Role: A R C I' under a task. (\xA724A)`
|
|
23552
|
+
);
|
|
23453
23553
|
} else {
|
|
23454
23554
|
warn(lineNumber, `Unrecognized directive: '${trimmed}'.`);
|
|
23455
23555
|
}
|
|
@@ -23996,6 +24096,13 @@ function getAllChartTypes() {
|
|
|
23996
24096
|
return chartTypes.map((c) => c.id);
|
|
23997
24097
|
}
|
|
23998
24098
|
function parseDgmo(content) {
|
|
24099
|
+
const result = parseDgmoUndeduped(content);
|
|
24100
|
+
return {
|
|
24101
|
+
diagnostics: dedupeDiagnostics(result.diagnostics),
|
|
24102
|
+
chartType: result.chartType
|
|
24103
|
+
};
|
|
24104
|
+
}
|
|
24105
|
+
function parseDgmoUndeduped(content) {
|
|
23999
24106
|
const chartType = parseDgmoChartType(content);
|
|
24000
24107
|
if (!chartType) {
|
|
24001
24108
|
const colonDiag = detectColonChartType(content);
|
|
@@ -29062,63 +29169,6 @@ function ensureArrowMarkers(defs, colors) {
|
|
|
29062
29169
|
).attr("fill", color);
|
|
29063
29170
|
}
|
|
29064
29171
|
}
|
|
29065
|
-
function edgePolylineMidpoint(points) {
|
|
29066
|
-
if (points.length === 0) return { x: 0, y: 0 };
|
|
29067
|
-
if (points.length === 1) return { x: points[0].x, y: points[0].y };
|
|
29068
|
-
let total = 0;
|
|
29069
|
-
const segLen = [];
|
|
29070
|
-
for (let k = 1; k < points.length; k++) {
|
|
29071
|
-
const len = Math.hypot(
|
|
29072
|
-
points[k].x - points[k - 1].x,
|
|
29073
|
-
points[k].y - points[k - 1].y
|
|
29074
|
-
);
|
|
29075
|
-
segLen.push(len);
|
|
29076
|
-
total += len;
|
|
29077
|
-
}
|
|
29078
|
-
let half = total / 2;
|
|
29079
|
-
for (let k = 1; k < points.length; k++) {
|
|
29080
|
-
const len = segLen[k - 1];
|
|
29081
|
-
if (half <= len || k === points.length - 1) {
|
|
29082
|
-
const t = len === 0 ? 0 : Math.min(1, half / len);
|
|
29083
|
-
return {
|
|
29084
|
-
x: points[k - 1].x + (points[k].x - points[k - 1].x) * t,
|
|
29085
|
-
y: points[k - 1].y + (points[k].y - points[k - 1].y) * t
|
|
29086
|
-
};
|
|
29087
|
-
}
|
|
29088
|
-
half -= len;
|
|
29089
|
-
}
|
|
29090
|
-
const last = points[points.length - 1];
|
|
29091
|
-
return { x: last.x, y: last.y };
|
|
29092
|
-
}
|
|
29093
|
-
function resolveEdgeLabelOverlaps(labels) {
|
|
29094
|
-
const MAX_PASSES = 8;
|
|
29095
|
-
const PAD = 4;
|
|
29096
|
-
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
29097
|
-
let moved = false;
|
|
29098
|
-
for (let i = 0; i < labels.length; i++) {
|
|
29099
|
-
for (let j = i + 1; j < labels.length; j++) {
|
|
29100
|
-
const a = labels[i];
|
|
29101
|
-
const b = labels[j];
|
|
29102
|
-
const dx = Math.abs(a.x - b.x);
|
|
29103
|
-
const dy = Math.abs(a.y - b.y);
|
|
29104
|
-
const overlapX = (a.width + b.width) / 2 + PAD - dx;
|
|
29105
|
-
const overlapY = (a.height + b.height) / 2 + PAD - dy;
|
|
29106
|
-
if (overlapX > 0 && overlapY > 0) {
|
|
29107
|
-
const shift = overlapY / 2 + 1;
|
|
29108
|
-
if (a.y < b.y) {
|
|
29109
|
-
a.y -= shift;
|
|
29110
|
-
b.y += shift;
|
|
29111
|
-
} else {
|
|
29112
|
-
a.y += shift;
|
|
29113
|
-
b.y -= shift;
|
|
29114
|
-
}
|
|
29115
|
-
moved = true;
|
|
29116
|
-
}
|
|
29117
|
-
}
|
|
29118
|
-
}
|
|
29119
|
-
if (!moved) break;
|
|
29120
|
-
}
|
|
29121
|
-
}
|
|
29122
29172
|
function renderBoxesAndLines(container, parsed, layout, palette, isDark, options) {
|
|
29123
29173
|
const {
|
|
29124
29174
|
onClickItem,
|
|
@@ -29325,8 +29375,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29325
29375
|
groupG.append("text").attr("class", "bl-group-label").attr("x", gx + group.width / 2).attr("y", gy + 18).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sGroupLabelFontSize).attr("font-weight", "600").attr("fill", palette.text).text(group.label);
|
|
29326
29376
|
}
|
|
29327
29377
|
}
|
|
29328
|
-
const labelPositions = [];
|
|
29329
|
-
const edgeGroups = /* @__PURE__ */ new Map();
|
|
29330
29378
|
for (let i = 0; i < layout.edges.length; i++) {
|
|
29331
29379
|
const le = layout.edges[i];
|
|
29332
29380
|
const color = edgeColorMap.get(i) ?? palette.textMuted;
|
|
@@ -29345,7 +29393,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29345
29393
|
const nodeLayout = layoutNodeMap.get(le.source);
|
|
29346
29394
|
if (nodeLayout) {
|
|
29347
29395
|
const edgeG2 = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
|
|
29348
|
-
edgeGroups.set(i, edgeG2);
|
|
29349
29396
|
const markerId2 = `bl-arrow-${color.replace("#", "")}`;
|
|
29350
29397
|
const cx = nodeLayout.x;
|
|
29351
29398
|
const cy = nodeLayout.y;
|
|
@@ -29394,7 +29441,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29394
29441
|
}
|
|
29395
29442
|
if (points.length < 2) continue;
|
|
29396
29443
|
const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
|
|
29397
|
-
edgeGroups.set(i, edgeG);
|
|
29398
29444
|
const markerId = `bl-arrow-${color.replace("#", "")}`;
|
|
29399
29445
|
const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
|
|
29400
29446
|
const path = edgeG.append("path").attr("class", "bl-edge").attr("d", gen(points) ?? "").attr("fill", "none").attr("stroke", color).attr("stroke-width", sEdgeStrokeWidth).attr("marker-end", `url(#${markerId})`);
|
|
@@ -29402,31 +29448,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29402
29448
|
const revId = `bl-arrow-rev-${color.replace("#", "")}`;
|
|
29403
29449
|
path.attr("marker-start", `url(#${revId})`);
|
|
29404
29450
|
}
|
|
29405
|
-
if (le.label && le.points.length > 0) {
|
|
29406
|
-
const lw = measureText(le.label, sEdgeLabelFontSize);
|
|
29407
|
-
const labelH = sEdgeLabelFontSize + 6;
|
|
29408
|
-
const mid = edgePolylineMidpoint(le.points);
|
|
29409
|
-
let ly = mid.y;
|
|
29410
|
-
if (le.parallelCount > 1 && le.yOffset !== 0) {
|
|
29411
|
-
ly += (le.yOffset < 0 ? -labelH : labelH) * 0.5;
|
|
29412
|
-
}
|
|
29413
|
-
labelPositions.push({
|
|
29414
|
-
x: mid.x,
|
|
29415
|
-
y: ly,
|
|
29416
|
-
width: lw + 8,
|
|
29417
|
-
height: labelH,
|
|
29418
|
-
idx: i
|
|
29419
|
-
});
|
|
29420
|
-
}
|
|
29421
|
-
}
|
|
29422
|
-
resolveEdgeLabelOverlaps(labelPositions);
|
|
29423
|
-
for (const lp of labelPositions) {
|
|
29424
|
-
const le = layout.edges[lp.idx];
|
|
29425
|
-
if (!le.label) continue;
|
|
29426
|
-
const edgeG = edgeGroups.get(lp.idx);
|
|
29427
|
-
const target = edgeG ?? diagramG;
|
|
29428
|
-
target.append("rect").attr("x", lp.x - lp.width / 2).attr("y", lp.y - lp.height / 2).attr("width", lp.width).attr("height", lp.height).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
|
|
29429
|
-
target.append("text").attr("x", lp.x).attr("y", lp.y + sEdgeLabelFontSize / 3).attr("text-anchor", "middle").attr("font-size", sEdgeLabelFontSize).attr("fill", palette.textMuted).text(le.label);
|
|
29430
29451
|
}
|
|
29431
29452
|
for (const ln of layout.nodes) {
|
|
29432
29453
|
const node = nodeMap.get(ln.label);
|
|
@@ -29477,8 +29498,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29477
29498
|
nodeG.append("rect").attr("x", x).attr("y", y).attr("width", ln.width).attr("height", ln.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", colors.fill).attr("stroke", colors.stroke).attr("stroke-width", sNodeStrokeWidth);
|
|
29478
29499
|
const desc = node.description;
|
|
29479
29500
|
if (desc && desc.length > 0 && !hideDescriptions) {
|
|
29480
|
-
const
|
|
29481
|
-
const fitted = fitLabelToHeader(node.label, ln.width,
|
|
29501
|
+
const MAX_LABEL_LINES4 = 3;
|
|
29502
|
+
const fitted = fitLabelToHeader(node.label, ln.width, MAX_LABEL_LINES4);
|
|
29482
29503
|
const labelLines = fitted.lines;
|
|
29483
29504
|
const labelLineH = fitted.fontSize * 1.3;
|
|
29484
29505
|
const labelTotalH = labelLines.length * labelLineH;
|
|
@@ -29612,6 +29633,35 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29612
29633
|
}
|
|
29613
29634
|
}
|
|
29614
29635
|
}
|
|
29636
|
+
const labelLayer = diagramG.append("g").attr("class", "bl-edge-labels");
|
|
29637
|
+
const labelLineHeight = sEdgeLabelFontSize * 1.3;
|
|
29638
|
+
for (const le of layout.edges) {
|
|
29639
|
+
if (!le.labelLines || le.labelLines.length === 0) continue;
|
|
29640
|
+
if (le.labelX === void 0 || le.labelY === void 0) continue;
|
|
29641
|
+
const lx = le.labelX;
|
|
29642
|
+
const ly = le.labelY;
|
|
29643
|
+
if (hidden.size > 0) {
|
|
29644
|
+
let isHidden = false;
|
|
29645
|
+
for (const [groupKey, hiddenVals] of hidden) {
|
|
29646
|
+
const val = le.metadata[groupKey];
|
|
29647
|
+
if (val && hiddenVals.has(val.toLowerCase())) {
|
|
29648
|
+
isHidden = true;
|
|
29649
|
+
break;
|
|
29650
|
+
}
|
|
29651
|
+
}
|
|
29652
|
+
if (isHidden) continue;
|
|
29653
|
+
}
|
|
29654
|
+
const lw = le.labelWidth ?? 0;
|
|
29655
|
+
const lh = le.labelHeight ?? sEdgeLabelFontSize + 6;
|
|
29656
|
+
const lines = le.labelLines;
|
|
29657
|
+
const labelG = labelLayer.append("g").attr("class", "bl-edge-label").attr("data-line-number", String(le.lineNumber));
|
|
29658
|
+
labelG.append("rect").attr("x", lx - lw / 2).attr("y", ly - lh / 2).attr("width", lw).attr("height", lh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.72);
|
|
29659
|
+
const text = labelG.append("text").attr("x", lx).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sEdgeLabelFontSize).attr("fill", palette.textMuted);
|
|
29660
|
+
const firstY = ly - (lines.length - 1) / 2 * labelLineHeight + sEdgeLabelFontSize / 3;
|
|
29661
|
+
lines.forEach((line11, k) => {
|
|
29662
|
+
text.append("tspan").attr("x", lx).attr("y", firstY + k * labelLineHeight).text(line11);
|
|
29663
|
+
});
|
|
29664
|
+
}
|
|
29615
29665
|
const hasDescriptions = parsed.nodes.some(
|
|
29616
29666
|
(n) => n.description && n.description.length > 0
|
|
29617
29667
|
);
|
|
@@ -29726,6 +29776,222 @@ var init_renderer6 = __esm({
|
|
|
29726
29776
|
}
|
|
29727
29777
|
});
|
|
29728
29778
|
|
|
29779
|
+
// src/boxes-and-lines/label-placement.ts
|
|
29780
|
+
function measureEdgeLabel(label, fontSize = EDGE_LABEL_FONT_SIZE6, maxWidth = LABEL_MAX_WIDTH) {
|
|
29781
|
+
let lines = wrapTextToWidth(label, fontSize, maxWidth, { hardBreak: true });
|
|
29782
|
+
if (lines.length > MAX_LABEL_LINES) {
|
|
29783
|
+
const kept = lines.slice(0, MAX_LABEL_LINES - 1);
|
|
29784
|
+
const rest = lines.slice(MAX_LABEL_LINES - 1).join(" ");
|
|
29785
|
+
kept.push(truncateText(rest, fontSize, maxWidth));
|
|
29786
|
+
lines = kept;
|
|
29787
|
+
}
|
|
29788
|
+
let textW = 0;
|
|
29789
|
+
for (const ln of lines) {
|
|
29790
|
+
const w = measureText(ln, fontSize);
|
|
29791
|
+
if (w > textW) textW = w;
|
|
29792
|
+
}
|
|
29793
|
+
return {
|
|
29794
|
+
lines,
|
|
29795
|
+
width: textW + 2 * H_PAD,
|
|
29796
|
+
height: lines.length * fontSize * LABEL_LINE_HEIGHT + 2 * V_PAD
|
|
29797
|
+
};
|
|
29798
|
+
}
|
|
29799
|
+
function edgePolylineMidpoint(points) {
|
|
29800
|
+
return pointAtArcFraction(points, 0.5).p;
|
|
29801
|
+
}
|
|
29802
|
+
function pointAtArcFraction(points, t) {
|
|
29803
|
+
if (points.length === 0) return { p: { x: 0, y: 0 }, nx: 0, ny: 1 };
|
|
29804
|
+
if (points.length === 1)
|
|
29805
|
+
return { p: { x: points[0].x, y: points[0].y }, nx: 0, ny: 1 };
|
|
29806
|
+
const segLen = [];
|
|
29807
|
+
let total = 0;
|
|
29808
|
+
for (let k = 1; k < points.length; k++) {
|
|
29809
|
+
const len = Math.hypot(
|
|
29810
|
+
points[k].x - points[k - 1].x,
|
|
29811
|
+
points[k].y - points[k - 1].y
|
|
29812
|
+
);
|
|
29813
|
+
segLen.push(len);
|
|
29814
|
+
total += len;
|
|
29815
|
+
}
|
|
29816
|
+
let want = total * Math.max(0, Math.min(1, t));
|
|
29817
|
+
for (let k = 1; k < points.length; k++) {
|
|
29818
|
+
const len = segLen[k - 1];
|
|
29819
|
+
if (want <= len || k === points.length - 1) {
|
|
29820
|
+
const f = len === 0 ? 0 : Math.min(1, want / len);
|
|
29821
|
+
const ax = points[k - 1].x;
|
|
29822
|
+
const ay = points[k - 1].y;
|
|
29823
|
+
const bx = points[k].x;
|
|
29824
|
+
const by = points[k].y;
|
|
29825
|
+
const dx = bx - ax;
|
|
29826
|
+
const dy = by - ay;
|
|
29827
|
+
const dlen = Math.hypot(dx, dy) || 1;
|
|
29828
|
+
return {
|
|
29829
|
+
p: { x: ax + dx * f, y: ay + dy * f },
|
|
29830
|
+
nx: -dy / dlen,
|
|
29831
|
+
ny: dx / dlen
|
|
29832
|
+
};
|
|
29833
|
+
}
|
|
29834
|
+
want -= len;
|
|
29835
|
+
}
|
|
29836
|
+
const last = points[points.length - 1];
|
|
29837
|
+
return { p: { x: last.x, y: last.y }, nx: 0, ny: 1 };
|
|
29838
|
+
}
|
|
29839
|
+
function rectFromCenter(cx, cy, w, h) {
|
|
29840
|
+
return {
|
|
29841
|
+
minX: cx - w / 2,
|
|
29842
|
+
minY: cy - h / 2,
|
|
29843
|
+
maxX: cx + w / 2,
|
|
29844
|
+
maxY: cy + h / 2
|
|
29845
|
+
};
|
|
29846
|
+
}
|
|
29847
|
+
function overlapsAny(cx, cy, w, h, obstacles, pad2) {
|
|
29848
|
+
const minX = cx - w / 2 - pad2;
|
|
29849
|
+
const minY = cy - h / 2 - pad2;
|
|
29850
|
+
const maxX = cx + w / 2 + pad2;
|
|
29851
|
+
const maxY = cy + h / 2 + pad2;
|
|
29852
|
+
for (const o of obstacles) {
|
|
29853
|
+
if (minX < o.maxX && maxX > o.minX && minY < o.maxY && maxY > o.minY)
|
|
29854
|
+
return true;
|
|
29855
|
+
}
|
|
29856
|
+
return false;
|
|
29857
|
+
}
|
|
29858
|
+
function slideFractions() {
|
|
29859
|
+
const out = [0.5];
|
|
29860
|
+
for (let k = 1; k <= SLIDE_SAMPLES; k++) {
|
|
29861
|
+
const off = k * 0.08;
|
|
29862
|
+
if (0.5 - off >= 0.12) out.push(0.5 - off);
|
|
29863
|
+
if (0.5 + off <= 0.88) out.push(0.5 + off);
|
|
29864
|
+
}
|
|
29865
|
+
return out;
|
|
29866
|
+
}
|
|
29867
|
+
function findClearPosition(w, h, points, obstacles) {
|
|
29868
|
+
const ts = slideFractions();
|
|
29869
|
+
for (const t of ts) {
|
|
29870
|
+
const { p } = pointAtArcFraction(points, t);
|
|
29871
|
+
if (!overlapsAny(p.x, p.y, w, h, obstacles, BOX_CLEAR_PAD)) return p;
|
|
29872
|
+
}
|
|
29873
|
+
for (let mag = PERP_STEP; mag <= PERP_MAX; mag += PERP_STEP) {
|
|
29874
|
+
for (const t of ts) {
|
|
29875
|
+
const { p, nx, ny } = pointAtArcFraction(points, t);
|
|
29876
|
+
for (const sign of [-1, 1]) {
|
|
29877
|
+
const x = p.x + nx * mag * sign;
|
|
29878
|
+
const y = p.y + ny * mag * sign;
|
|
29879
|
+
if (!overlapsAny(x, y, w, h, obstacles, BOX_CLEAR_PAD)) return { x, y };
|
|
29880
|
+
}
|
|
29881
|
+
}
|
|
29882
|
+
}
|
|
29883
|
+
return null;
|
|
29884
|
+
}
|
|
29885
|
+
function resolveLabelOverlaps(boxes) {
|
|
29886
|
+
const MAX_PASSES = 8;
|
|
29887
|
+
const PAD = 4;
|
|
29888
|
+
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
29889
|
+
let moved = false;
|
|
29890
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
29891
|
+
for (let j = i + 1; j < boxes.length; j++) {
|
|
29892
|
+
const a = boxes[i];
|
|
29893
|
+
const b = boxes[j];
|
|
29894
|
+
const dx = Math.abs(a.cx - b.cx);
|
|
29895
|
+
const dy = Math.abs(a.cy - b.cy);
|
|
29896
|
+
const overlapX = (a.w + b.w) / 2 + PAD - dx;
|
|
29897
|
+
const overlapY = (a.h + b.h) / 2 + PAD - dy;
|
|
29898
|
+
if (overlapX > 0 && overlapY > 0) {
|
|
29899
|
+
const shift = overlapY / 2 + 1;
|
|
29900
|
+
if (a.cy < b.cy) {
|
|
29901
|
+
a.cy -= shift;
|
|
29902
|
+
b.cy += shift;
|
|
29903
|
+
} else {
|
|
29904
|
+
a.cy += shift;
|
|
29905
|
+
b.cy -= shift;
|
|
29906
|
+
}
|
|
29907
|
+
moved = true;
|
|
29908
|
+
}
|
|
29909
|
+
}
|
|
29910
|
+
}
|
|
29911
|
+
if (!moved) break;
|
|
29912
|
+
}
|
|
29913
|
+
}
|
|
29914
|
+
function placeEdgeLabels(layout, opts) {
|
|
29915
|
+
const fontSize = opts?.fontSize ?? EDGE_LABEL_FONT_SIZE6;
|
|
29916
|
+
const maxWidth = opts?.maxWidth ?? LABEL_MAX_WIDTH;
|
|
29917
|
+
const obstacles = [];
|
|
29918
|
+
for (const n of layout.nodes)
|
|
29919
|
+
obstacles.push(rectFromCenter(n.x, n.y, n.width, n.height));
|
|
29920
|
+
for (const g of layout.groups)
|
|
29921
|
+
if (g.collapsed)
|
|
29922
|
+
obstacles.push(rectFromCenter(g.x, g.y, g.width, g.height));
|
|
29923
|
+
const boxes = [];
|
|
29924
|
+
layout.edges.forEach((e, idx) => {
|
|
29925
|
+
if (!e.label || e.points.length < 2) return;
|
|
29926
|
+
const m = measureEdgeLabel(e.label, fontSize, maxWidth);
|
|
29927
|
+
const mid = edgePolylineMidpoint(e.points);
|
|
29928
|
+
let cy = mid.y;
|
|
29929
|
+
if (e.parallelCount > 1 && e.yOffset !== 0)
|
|
29930
|
+
cy += (e.yOffset < 0 ? -1 : 1) * (m.height / 2);
|
|
29931
|
+
boxes.push({
|
|
29932
|
+
edgeIdx: idx,
|
|
29933
|
+
cx: mid.x,
|
|
29934
|
+
cy,
|
|
29935
|
+
w: m.width,
|
|
29936
|
+
h: m.height,
|
|
29937
|
+
lines: m.lines,
|
|
29938
|
+
resolved: true
|
|
29939
|
+
});
|
|
29940
|
+
});
|
|
29941
|
+
for (const box of boxes) {
|
|
29942
|
+
if (!overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
|
|
29943
|
+
continue;
|
|
29944
|
+
const e = layout.edges[box.edgeIdx];
|
|
29945
|
+
const clear = findClearPosition(box.w, box.h, e.points, obstacles);
|
|
29946
|
+
if (clear) {
|
|
29947
|
+
box.cx = clear.x;
|
|
29948
|
+
box.cy = clear.y;
|
|
29949
|
+
} else {
|
|
29950
|
+
box.resolved = false;
|
|
29951
|
+
}
|
|
29952
|
+
}
|
|
29953
|
+
resolveLabelOverlaps(boxes);
|
|
29954
|
+
for (const box of boxes) {
|
|
29955
|
+
if (overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
|
|
29956
|
+
box.resolved = false;
|
|
29957
|
+
}
|
|
29958
|
+
const boxByIdx = /* @__PURE__ */ new Map();
|
|
29959
|
+
for (const b of boxes) boxByIdx.set(b.edgeIdx, b);
|
|
29960
|
+
const edges = layout.edges.map((e, idx) => {
|
|
29961
|
+
const b = boxByIdx.get(idx);
|
|
29962
|
+
if (!b) return e;
|
|
29963
|
+
return {
|
|
29964
|
+
...e,
|
|
29965
|
+
labelX: b.cx,
|
|
29966
|
+
labelY: b.cy,
|
|
29967
|
+
labelWidth: b.w,
|
|
29968
|
+
labelHeight: b.h,
|
|
29969
|
+
labelLines: b.lines
|
|
29970
|
+
};
|
|
29971
|
+
});
|
|
29972
|
+
return {
|
|
29973
|
+
layout: { ...layout, edges },
|
|
29974
|
+
unresolved: boxes.filter((b) => !b.resolved).map((b) => b.edgeIdx)
|
|
29975
|
+
};
|
|
29976
|
+
}
|
|
29977
|
+
var EDGE_LABEL_FONT_SIZE6, LABEL_MAX_WIDTH, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, H_PAD, V_PAD, BOX_CLEAR_PAD, PERP_STEP, PERP_MAX, SLIDE_SAMPLES;
|
|
29978
|
+
var init_label_placement = __esm({
|
|
29979
|
+
"src/boxes-and-lines/label-placement.ts"() {
|
|
29980
|
+
"use strict";
|
|
29981
|
+
init_text_measure();
|
|
29982
|
+
EDGE_LABEL_FONT_SIZE6 = 11;
|
|
29983
|
+
LABEL_MAX_WIDTH = 160;
|
|
29984
|
+
MAX_LABEL_LINES = 3;
|
|
29985
|
+
LABEL_LINE_HEIGHT = 1.3;
|
|
29986
|
+
H_PAD = 6;
|
|
29987
|
+
V_PAD = 3;
|
|
29988
|
+
BOX_CLEAR_PAD = 4;
|
|
29989
|
+
PERP_STEP = 8;
|
|
29990
|
+
PERP_MAX = 40;
|
|
29991
|
+
SLIDE_SAMPLES = 9;
|
|
29992
|
+
}
|
|
29993
|
+
});
|
|
29994
|
+
|
|
29729
29995
|
// src/boxes-and-lines/layout-layered.ts
|
|
29730
29996
|
function rng(s) {
|
|
29731
29997
|
return () => {
|
|
@@ -31515,6 +31781,7 @@ function edgeLength(layout) {
|
|
|
31515
31781
|
}
|
|
31516
31782
|
async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
31517
31783
|
const hideDescriptions = opts?.hideDescriptions ?? false;
|
|
31784
|
+
const reserveEdgeLabels = opts?.reserveEdgeLabels ?? false;
|
|
31518
31785
|
const onProgress = opts?.onProgress;
|
|
31519
31786
|
const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
|
|
31520
31787
|
const collapsedGroupLabels = /* @__PURE__ */ new Set();
|
|
@@ -31807,8 +32074,14 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
|
31807
32074
|
for (const e of ord(parsed.edges)) {
|
|
31808
32075
|
const s = srcRep(e.source);
|
|
31809
32076
|
const t = tgtRep(e.target);
|
|
31810
|
-
if (s !== t && g.hasNode(s) && g.hasNode(t))
|
|
31811
|
-
|
|
32077
|
+
if (s !== t && g.hasNode(s) && g.hasNode(t)) {
|
|
32078
|
+
let edgeLabel = {};
|
|
32079
|
+
if (reserveEdgeLabels && e.label) {
|
|
32080
|
+
const m = measureEdgeLabel(e.label, EDGE_LABEL_FONT_SIZE6);
|
|
32081
|
+
edgeLabel = { width: m.width, height: m.height, labelpos: "c" };
|
|
32082
|
+
}
|
|
32083
|
+
g.setEdge(s, t, edgeLabel, edgeKey(e));
|
|
32084
|
+
}
|
|
31812
32085
|
}
|
|
31813
32086
|
dagre4.layout(g);
|
|
31814
32087
|
const nodes = parsed.nodes.map((n2) => {
|
|
@@ -32037,6 +32310,7 @@ var init_layout_search = __esm({
|
|
|
32037
32310
|
init_layout5();
|
|
32038
32311
|
init_layout_layered();
|
|
32039
32312
|
init_layout_grouped();
|
|
32313
|
+
init_label_placement();
|
|
32040
32314
|
DEFAULT_LAMBDA = 4;
|
|
32041
32315
|
ESCALATE_THRESHOLD = 4;
|
|
32042
32316
|
ESCALATE_MAX_N = 45;
|
|
@@ -32095,9 +32369,9 @@ function estimateLabelLines(label, nodeWidth = NODE_WIDTH) {
|
|
|
32095
32369
|
current = word;
|
|
32096
32370
|
}
|
|
32097
32371
|
}
|
|
32098
|
-
if (lines <=
|
|
32372
|
+
if (lines <= MAX_LABEL_LINES2) return Math.min(lines, MAX_LABEL_LINES2);
|
|
32099
32373
|
}
|
|
32100
|
-
return
|
|
32374
|
+
return MAX_LABEL_LINES2;
|
|
32101
32375
|
}
|
|
32102
32376
|
function computeNodeSize(node, reserveValueRow) {
|
|
32103
32377
|
if (!node.description || node.description.length === 0) {
|
|
@@ -32105,7 +32379,7 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
32105
32379
|
}
|
|
32106
32380
|
const w = DESC_NODE_WIDTH;
|
|
32107
32381
|
const labelLines = estimateLabelLines(node.label, w);
|
|
32108
|
-
const labelHeight = labelLines * 13 *
|
|
32382
|
+
const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT2 + LABEL_PAD;
|
|
32109
32383
|
const maxTextWidth = w - 24;
|
|
32110
32384
|
let totalRenderedLines = 0;
|
|
32111
32385
|
for (const line11 of node.description) {
|
|
@@ -32129,7 +32403,7 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
32129
32403
|
}
|
|
32130
32404
|
async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
32131
32405
|
const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
|
|
32132
|
-
const
|
|
32406
|
+
const searchOpts = {
|
|
32133
32407
|
...layoutOptions?.hideDescriptions !== void 0 && {
|
|
32134
32408
|
hideDescriptions: layoutOptions.hideDescriptions
|
|
32135
32409
|
},
|
|
@@ -32139,12 +32413,23 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
|
32139
32413
|
...layoutOptions?.onProgress !== void 0 && {
|
|
32140
32414
|
onProgress: layoutOptions.onProgress
|
|
32141
32415
|
}
|
|
32142
|
-
}
|
|
32143
|
-
|
|
32144
|
-
applyParallelEdgeOffsets(searched),
|
|
32416
|
+
};
|
|
32417
|
+
const searched = await layoutBoxesAndLinesSearch2(
|
|
32145
32418
|
parsed,
|
|
32146
|
-
|
|
32147
|
-
|
|
32419
|
+
collapseInfo,
|
|
32420
|
+
searchOpts
|
|
32421
|
+
);
|
|
32422
|
+
let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
|
|
32423
|
+
if (placed.unresolved.length > 0) {
|
|
32424
|
+
const relaid = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
|
|
32425
|
+
...searchOpts,
|
|
32426
|
+
reserveEdgeLabels: true
|
|
32427
|
+
});
|
|
32428
|
+
const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
|
|
32429
|
+
if (relaidPlaced.unresolved.length < placed.unresolved.length)
|
|
32430
|
+
placed = relaidPlaced;
|
|
32431
|
+
}
|
|
32432
|
+
return attachNotes(placed.layout, parsed, layoutOptions?.collapsedNotes);
|
|
32148
32433
|
}
|
|
32149
32434
|
function attachNotes(layout, parsed, collapsedNotes) {
|
|
32150
32435
|
const notesSuppressed = parsed.options?.["no-notes"] === "on";
|
|
@@ -32212,6 +32497,7 @@ function attachNotes(layout, parsed, collapsedNotes) {
|
|
|
32212
32497
|
points: e.points.map((pt) => ({ x: pt.x + shiftX, y: pt.y + shiftY })),
|
|
32213
32498
|
...e.labelX !== void 0 && { labelX: e.labelX + shiftX },
|
|
32214
32499
|
...e.labelY !== void 0 && { labelY: e.labelY + shiftY }
|
|
32500
|
+
// labelWidth/labelHeight/labelLines are shift-invariant — carried via spread.
|
|
32215
32501
|
})) : layout.edges;
|
|
32216
32502
|
const finalGroups = shifted ? layout.groups.map((grp) => ({
|
|
32217
32503
|
...grp,
|
|
@@ -32256,11 +32542,12 @@ function applyParallelEdgeOffsets(layout) {
|
|
|
32256
32542
|
}))
|
|
32257
32543
|
};
|
|
32258
32544
|
}
|
|
32259
|
-
var MARGIN5, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2,
|
|
32545
|
+
var MARGIN5, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES2, LABEL_LINE_HEIGHT2, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
|
|
32260
32546
|
var init_layout5 = __esm({
|
|
32261
32547
|
"src/boxes-and-lines/layout.ts"() {
|
|
32262
32548
|
"use strict";
|
|
32263
32549
|
init_text_measure();
|
|
32550
|
+
init_label_placement();
|
|
32264
32551
|
init_notes();
|
|
32265
32552
|
MARGIN5 = 40;
|
|
32266
32553
|
MAX_PARALLEL_EDGES = 5;
|
|
@@ -32274,8 +32561,8 @@ var init_layout5 = __esm({
|
|
|
32274
32561
|
DESC_PADDING = 8;
|
|
32275
32562
|
SEPARATOR_GAP4 = 4;
|
|
32276
32563
|
MAX_DESC_LINES2 = 6;
|
|
32277
|
-
|
|
32278
|
-
|
|
32564
|
+
MAX_LABEL_LINES2 = 3;
|
|
32565
|
+
LABEL_LINE_HEIGHT2 = 1.3;
|
|
32279
32566
|
LABEL_PAD = 12;
|
|
32280
32567
|
VALUE_ROW_FONT = 11;
|
|
32281
32568
|
VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
|
|
@@ -32294,7 +32581,7 @@ function tokenize(text) {
|
|
|
32294
32581
|
return tokens;
|
|
32295
32582
|
}
|
|
32296
32583
|
function tryWrap(tokens, maxWidth, fontSize, maxLines) {
|
|
32297
|
-
const availWidth = maxWidth -
|
|
32584
|
+
const availWidth = maxWidth - H_PAD2;
|
|
32298
32585
|
const lines = [];
|
|
32299
32586
|
let currentLine = "";
|
|
32300
32587
|
for (const token of tokens) {
|
|
@@ -32319,7 +32606,7 @@ function tryWrap(tokens, maxWidth, fontSize, maxLines) {
|
|
|
32319
32606
|
return lines;
|
|
32320
32607
|
}
|
|
32321
32608
|
function truncateLastLine(lines, maxWidth, fontSize) {
|
|
32322
|
-
const availWidth = maxWidth -
|
|
32609
|
+
const availWidth = maxWidth - H_PAD2;
|
|
32323
32610
|
const result = [...lines];
|
|
32324
32611
|
const last = result[result.length - 1];
|
|
32325
32612
|
if (measureText(last, fontSize) > availWidth) {
|
|
@@ -32327,7 +32614,7 @@ function truncateLastLine(lines, maxWidth, fontSize) {
|
|
|
32327
32614
|
}
|
|
32328
32615
|
return result;
|
|
32329
32616
|
}
|
|
32330
|
-
function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines =
|
|
32617
|
+
function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES3) {
|
|
32331
32618
|
if (!text) return { lines: [""], fontSize: baseFontSize };
|
|
32332
32619
|
const tokens = tokenize(text);
|
|
32333
32620
|
for (let fs = baseFontSize; fs >= minFontSize; fs--) {
|
|
@@ -32342,7 +32629,7 @@ function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABE
|
|
|
32342
32629
|
if (allLines.length > maxLines) {
|
|
32343
32630
|
const last = truncated[truncated.length - 1];
|
|
32344
32631
|
if (!last.endsWith("\u2026")) {
|
|
32345
|
-
const availWidth = maxWidth -
|
|
32632
|
+
const availWidth = maxWidth - H_PAD2;
|
|
32346
32633
|
if (measureText(last + "\u2026", minFontSize) > availWidth) {
|
|
32347
32634
|
truncated[truncated.length - 1] = truncateText(
|
|
32348
32635
|
last,
|
|
@@ -32369,7 +32656,7 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
|
|
|
32369
32656
|
nodeWidth,
|
|
32370
32657
|
baseFontSize,
|
|
32371
32658
|
MIN_FONT_SIZE,
|
|
32372
|
-
|
|
32659
|
+
MAX_LABEL_LINES3
|
|
32373
32660
|
);
|
|
32374
32661
|
const descLines = [];
|
|
32375
32662
|
let descFontSize = DESC_FONT_SIZE3;
|
|
@@ -32397,14 +32684,14 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
|
|
|
32397
32684
|
descFontSize
|
|
32398
32685
|
};
|
|
32399
32686
|
}
|
|
32400
|
-
var
|
|
32687
|
+
var H_PAD2, MAX_LABEL_LINES3, MAX_DESC_LINES3, ROOT_FONT_SIZE, MIN_FONT_SIZE, FONT_STEP, DESC_FONT_SIZE3;
|
|
32401
32688
|
var init_text_wrap = __esm({
|
|
32402
32689
|
"src/mindmap/text-wrap.ts"() {
|
|
32403
32690
|
"use strict";
|
|
32404
32691
|
init_description_helpers();
|
|
32405
32692
|
init_text_measure();
|
|
32406
|
-
|
|
32407
|
-
|
|
32693
|
+
H_PAD2 = 16;
|
|
32694
|
+
MAX_LABEL_LINES3 = 3;
|
|
32408
32695
|
MAX_DESC_LINES3 = 2;
|
|
32409
32696
|
ROOT_FONT_SIZE = 17;
|
|
32410
32697
|
MIN_FONT_SIZE = 9;
|
|
@@ -32791,7 +33078,7 @@ function nodeHeightS(node, hideDescriptions, sc) {
|
|
|
32791
33078
|
hideDescriptions
|
|
32792
33079
|
);
|
|
32793
33080
|
const labelLineCount = text.labelLines.length;
|
|
32794
|
-
const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT :
|
|
33081
|
+
const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT : LABEL_LINE_HEIGHT3 * labelLineCount;
|
|
32795
33082
|
let h = labelH + NODE_V_PAD;
|
|
32796
33083
|
if (text.descLines.length > 0) {
|
|
32797
33084
|
h += DESC_LINE_HEIGHT3 * text.descLines.length + 4;
|
|
@@ -32811,7 +33098,7 @@ function populateDepthCache(roots) {
|
|
|
32811
33098
|
};
|
|
32812
33099
|
walk(roots, 0);
|
|
32813
33100
|
}
|
|
32814
|
-
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT,
|
|
33101
|
+
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN6, MULTI_ROOT_GAP, nodeDepthCache;
|
|
32815
33102
|
var init_layout6 = __esm({
|
|
32816
33103
|
"src/mindmap/layout.ts"() {
|
|
32817
33104
|
"use strict";
|
|
@@ -32822,7 +33109,7 @@ var init_layout6 = __esm({
|
|
|
32822
33109
|
DEPTH1_WIDTH = 150;
|
|
32823
33110
|
LEAF_WIDTH = 120;
|
|
32824
33111
|
SINGLE_LABEL_HEIGHT = 28;
|
|
32825
|
-
|
|
33112
|
+
LABEL_LINE_HEIGHT3 = 18;
|
|
32826
33113
|
DESC_LINE_HEIGHT3 = 14;
|
|
32827
33114
|
NODE_V_PAD = 10;
|
|
32828
33115
|
H_GAP2 = 40;
|
|
@@ -33114,18 +33401,18 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
33114
33401
|
descFontSize
|
|
33115
33402
|
} = textLayout;
|
|
33116
33403
|
const labelLineCount = labelLines.length;
|
|
33117
|
-
const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 :
|
|
33404
|
+
const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 : LABEL_LINE_HEIGHT4 * labelLineCount;
|
|
33118
33405
|
const labelZoneHeight = showDesc ? labelZoneH : node.height;
|
|
33119
33406
|
const centerX = node.x + node.width / 2;
|
|
33120
33407
|
if (labelLineCount <= 1) {
|
|
33121
33408
|
const labelY = node.y + labelZoneHeight / 2 + fontSize * 0.35;
|
|
33122
33409
|
nodeG.append("text").attr("x", centerX).attr("y", labelY).attr("text-anchor", "middle").attr("font-size", fontSize).attr("font-weight", isRoot ? "bold" : "normal").attr("fill", onFillText).text(labelLines[0]);
|
|
33123
33410
|
} else {
|
|
33124
|
-
const blockH =
|
|
33411
|
+
const blockH = LABEL_LINE_HEIGHT4 * (labelLineCount - 1);
|
|
33125
33412
|
const firstBaselineY = node.y + labelZoneHeight / 2 - blockH / 2 + fontSize * 0.35;
|
|
33126
33413
|
const textEl = nodeG.append("text").attr("x", centerX).attr("text-anchor", "middle").attr("font-size", fontSize).attr("font-weight", isRoot ? "bold" : "normal").attr("fill", onFillText);
|
|
33127
33414
|
for (let i = 0; i < labelLines.length; i++) {
|
|
33128
|
-
textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i *
|
|
33415
|
+
textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i * LABEL_LINE_HEIGHT4).text(labelLines[i]);
|
|
33129
33416
|
}
|
|
33130
33417
|
}
|
|
33131
33418
|
if (labelLines.length > 1 || labelLines[0] !== node.label) {
|
|
@@ -33200,7 +33487,7 @@ function renderMindmapForExport(content, theme, palette) {
|
|
|
33200
33487
|
return extractExportSvg(container, theme);
|
|
33201
33488
|
});
|
|
33202
33489
|
}
|
|
33203
|
-
var DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2,
|
|
33490
|
+
var DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT4, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
|
|
33204
33491
|
var init_renderer7 = __esm({
|
|
33205
33492
|
"src/mindmap/renderer.ts"() {
|
|
33206
33493
|
"use strict";
|
|
@@ -33222,7 +33509,7 @@ var init_renderer7 = __esm({
|
|
|
33222
33509
|
DIAGRAM_PADDING7 = 20;
|
|
33223
33510
|
TITLE_HEIGHT4 = 30;
|
|
33224
33511
|
SINGLE_LABEL_HEIGHT2 = 28;
|
|
33225
|
-
|
|
33512
|
+
LABEL_LINE_HEIGHT4 = 18;
|
|
33226
33513
|
DESC_LINE_HEIGHT4 = 14;
|
|
33227
33514
|
NODE_RADIUS = 6;
|
|
33228
33515
|
ROOT_STROKE_WIDTH = 2.5;
|
|
@@ -35891,7 +36178,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
35891
36178
|
const labelText = edge.label ?? "";
|
|
35892
36179
|
const techText = edge.technology ? `[${edge.technology}]` : "";
|
|
35893
36180
|
const textW = Math.max(
|
|
35894
|
-
measureText(labelText,
|
|
36181
|
+
measureText(labelText, EDGE_LABEL_FONT_SIZE7),
|
|
35895
36182
|
measureText(techText, TECH_FONT_SIZE)
|
|
35896
36183
|
);
|
|
35897
36184
|
const bgW = textW + 12;
|
|
@@ -35902,7 +36189,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
35902
36189
|
textY = midPt.y - 4;
|
|
35903
36190
|
}
|
|
35904
36191
|
if (labelText) {
|
|
35905
|
-
edgeG.append("text").attr("x", midPt.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size",
|
|
36192
|
+
edgeG.append("text").attr("x", midPt.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("class", "c4-edge-label").text(labelText);
|
|
35906
36193
|
}
|
|
35907
36194
|
if (techText) {
|
|
35908
36195
|
edgeG.append("text").attr("x", midPt.x).attr("y", labelText ? textY + 18 : textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", TECH_FONT_SIZE).attr("font-style", "italic").attr("class", "c4-edge-tech").text(techText);
|
|
@@ -36086,7 +36373,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
|
|
|
36086
36373
|
const labelText = edge.label ?? "";
|
|
36087
36374
|
const techText = edge.technology ? `[${edge.technology}]` : "";
|
|
36088
36375
|
const textW = Math.max(
|
|
36089
|
-
measureText(labelText,
|
|
36376
|
+
measureText(labelText, EDGE_LABEL_FONT_SIZE7),
|
|
36090
36377
|
measureText(techText, TECH_FONT_SIZE)
|
|
36091
36378
|
);
|
|
36092
36379
|
const bgW = textW + 12;
|
|
@@ -36104,7 +36391,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
|
|
|
36104
36391
|
});
|
|
36105
36392
|
}
|
|
36106
36393
|
}
|
|
36107
|
-
|
|
36394
|
+
placeEdgeLabels2(pendingLabels, edges, obstacleRects);
|
|
36108
36395
|
for (const lbl of pendingLabels) {
|
|
36109
36396
|
lbl.edgeG.append("rect").attr("x", lbl.x - lbl.bgW / 2).attr("y", lbl.y - lbl.bgH / 2).attr("width", lbl.bgW).attr("height", lbl.bgH).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.9).attr("class", "c4-edge-label-bg");
|
|
36110
36397
|
let textY = lbl.y;
|
|
@@ -36112,7 +36399,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
|
|
|
36112
36399
|
textY = lbl.y - 4;
|
|
36113
36400
|
}
|
|
36114
36401
|
if (lbl.labelText) {
|
|
36115
|
-
lbl.edgeG.append("text").attr("x", lbl.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size",
|
|
36402
|
+
lbl.edgeG.append("text").attr("x", lbl.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("class", "c4-edge-label").text(lbl.labelText);
|
|
36116
36403
|
}
|
|
36117
36404
|
if (lbl.techText) {
|
|
36118
36405
|
lbl.edgeG.append("text").attr("x", lbl.x).attr("y", lbl.labelText ? textY + 18 : textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", TECH_FONT_SIZE).attr("font-style", "italic").attr("class", "c4-edge-tech").text(lbl.techText);
|
|
@@ -36193,7 +36480,7 @@ function tangentAt(points, t) {
|
|
|
36193
36480
|
}
|
|
36194
36481
|
return { x: 0, y: 1 };
|
|
36195
36482
|
}
|
|
36196
|
-
function
|
|
36483
|
+
function placeEdgeLabels2(labels, edges, obstacleRects) {
|
|
36197
36484
|
if (labels.length === 0) return;
|
|
36198
36485
|
const allPaths = edges.map((e) => e.points);
|
|
36199
36486
|
const placedRects = [];
|
|
@@ -36731,7 +37018,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
|
|
|
36731
37018
|
document.body.removeChild(el);
|
|
36732
37019
|
}
|
|
36733
37020
|
}
|
|
36734
|
-
var DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6,
|
|
37021
|
+
var DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE7, TECH_FONT_SIZE, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
|
|
36735
37022
|
var init_renderer9 = __esm({
|
|
36736
37023
|
"src/c4/renderer.ts"() {
|
|
36737
37024
|
"use strict";
|
|
@@ -36754,7 +37041,7 @@ var init_renderer9 = __esm({
|
|
|
36754
37041
|
NAME_FONT_SIZE2 = 14;
|
|
36755
37042
|
DESC_FONT_SIZE5 = 11;
|
|
36756
37043
|
DESC_LINE_HEIGHT6 = 16;
|
|
36757
|
-
|
|
37044
|
+
EDGE_LABEL_FONT_SIZE7 = 11;
|
|
36758
37045
|
TECH_FONT_SIZE = 10;
|
|
36759
37046
|
CARD_H_PAD4 = 20;
|
|
36760
37047
|
CARD_V_PAD4 = 14;
|
|
@@ -37379,7 +37666,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
|
|
|
37379
37666
|
const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
|
|
37380
37667
|
const sTitleY = ctx.structural(TITLE_Y);
|
|
37381
37668
|
const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
|
|
37382
|
-
const sEdgeLabelFontSize = ctx.text(
|
|
37669
|
+
const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE8);
|
|
37383
37670
|
const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
|
|
37384
37671
|
const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
|
|
37385
37672
|
const sArrowheadW = ctx.structural(ARROWHEAD_W3);
|
|
@@ -37605,7 +37892,7 @@ function renderFlowchartForExport(content, theme, palette) {
|
|
|
37605
37892
|
document.body.removeChild(container);
|
|
37606
37893
|
}
|
|
37607
37894
|
}
|
|
37608
|
-
var DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2,
|
|
37895
|
+
var DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE8, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
|
|
37609
37896
|
var init_flowchart_renderer = __esm({
|
|
37610
37897
|
"src/graph/flowchart-renderer.ts"() {
|
|
37611
37898
|
"use strict";
|
|
@@ -37624,7 +37911,7 @@ var init_flowchart_renderer = __esm({
|
|
|
37624
37911
|
DIAGRAM_PADDING9 = 20;
|
|
37625
37912
|
MAX_SCALE6 = 3;
|
|
37626
37913
|
NODE_FONT_SIZE2 = 13;
|
|
37627
|
-
|
|
37914
|
+
EDGE_LABEL_FONT_SIZE8 = 11;
|
|
37628
37915
|
ARROWHEAD_W3 = 10;
|
|
37629
37916
|
ARROWHEAD_H3 = 7;
|
|
37630
37917
|
IO_SKEW = 15;
|
|
@@ -39059,7 +39346,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
39059
39346
|
const midIdx = Math.floor(edge.points.length / 2);
|
|
39060
39347
|
const midPt = edge.points[midIdx];
|
|
39061
39348
|
if (midPt) {
|
|
39062
|
-
const halfWidth = (measureText(edge.label,
|
|
39349
|
+
const halfWidth = (measureText(edge.label, EDGE_LABEL_FONT_SIZE9) + 8) / 2;
|
|
39063
39350
|
if (midPt.x - halfWidth < minX) minX = midPt.x - halfWidth;
|
|
39064
39351
|
if (midPt.x + halfWidth > maxX) maxX = midPt.x + halfWidth;
|
|
39065
39352
|
}
|
|
@@ -39093,7 +39380,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
39093
39380
|
height: totalHeight
|
|
39094
39381
|
};
|
|
39095
39382
|
}
|
|
39096
|
-
var MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5,
|
|
39383
|
+
var MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP2;
|
|
39097
39384
|
var init_layout10 = __esm({
|
|
39098
39385
|
"src/infra/layout.ts"() {
|
|
39099
39386
|
"use strict";
|
|
@@ -39107,7 +39394,7 @@ var init_layout10 = __esm({
|
|
|
39107
39394
|
COLLAPSE_BAR_HEIGHT3 = 6;
|
|
39108
39395
|
NODE_FONT_SIZE3 = 13;
|
|
39109
39396
|
META_FONT_SIZE5 = 10;
|
|
39110
|
-
|
|
39397
|
+
EDGE_LABEL_FONT_SIZE9 = 11;
|
|
39111
39398
|
PADDING_X3 = 24;
|
|
39112
39399
|
GROUP_PADDING2 = 20;
|
|
39113
39400
|
GROUP_HEADER_HEIGHT = 24;
|
|
@@ -39214,7 +39501,7 @@ function buildScaledConstants(ctx) {
|
|
|
39214
39501
|
sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
|
|
39215
39502
|
sMetaFontSize: ctx.text(META_FONT_SIZE6),
|
|
39216
39503
|
sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
|
|
39217
|
-
sEdgeLabelFontSize: ctx.text(
|
|
39504
|
+
sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE10),
|
|
39218
39505
|
sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
|
|
39219
39506
|
sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
|
|
39220
39507
|
sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
|
|
@@ -39943,7 +40230,7 @@ function renderEdgeLabels(svg, edges, nodes, groups, palette, _isDark, animate,
|
|
|
39943
40230
|
if (!midPt) continue;
|
|
39944
40231
|
const labelText = edge.label;
|
|
39945
40232
|
const g = svg.append("g").attr("class", animate ? "infra-edge-label" : "");
|
|
39946
|
-
const textWidth = measureText(labelText,
|
|
40233
|
+
const textWidth = measureText(labelText, EDGE_LABEL_FONT_SIZE10) + 8;
|
|
39947
40234
|
g.append("rect").attr("x", midPt.x - textWidth / 2).attr("y", midPt.y - 8).attr("width", textWidth).attr("height", 16).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.9);
|
|
39948
40235
|
g.append("text").attr("x", midPt.x).attr("y", midPt.y + 4).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sEdgeLabelFontSize).attr("fill", palette.textMuted).text(labelText);
|
|
39949
40236
|
if (animate) {
|
|
@@ -40553,7 +40840,7 @@ function parseAndLayoutInfra(content) {
|
|
|
40553
40840
|
const layout = layoutInfra(computed);
|
|
40554
40841
|
return { parsed, computed, layout };
|
|
40555
40842
|
}
|
|
40556
|
-
var NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6,
|
|
40843
|
+
var NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
|
|
40557
40844
|
var init_renderer10 = __esm({
|
|
40558
40845
|
"src/infra/renderer.ts"() {
|
|
40559
40846
|
"use strict";
|
|
@@ -40577,7 +40864,7 @@ var init_renderer10 = __esm({
|
|
|
40577
40864
|
NODE_FONT_SIZE4 = 13;
|
|
40578
40865
|
META_FONT_SIZE6 = 10;
|
|
40579
40866
|
META_LINE_HEIGHT6 = 14;
|
|
40580
|
-
|
|
40867
|
+
EDGE_LABEL_FONT_SIZE10 = 11;
|
|
40581
40868
|
GROUP_LABEL_FONT_SIZE2 = 14;
|
|
40582
40869
|
NODE_BORDER_RADIUS = 8;
|
|
40583
40870
|
OVERLOAD_STROKE_WIDTH = 3;
|
|
@@ -46966,7 +47253,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
|
|
|
46966
47253
|
const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
|
|
46967
47254
|
const sTitleY = ctx.structural(TITLE_Y);
|
|
46968
47255
|
const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
|
|
46969
|
-
const sEdgeLabelFontSize = ctx.text(
|
|
47256
|
+
const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE11);
|
|
46970
47257
|
const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
|
|
46971
47258
|
const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
|
|
46972
47259
|
const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
|
|
@@ -47246,7 +47533,7 @@ function renderStateForExport(content, theme, palette) {
|
|
|
47246
47533
|
document.body.removeChild(container);
|
|
47247
47534
|
}
|
|
47248
47535
|
}
|
|
47249
|
-
var DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6,
|
|
47536
|
+
var DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, GROUP_LABEL_FONT_SIZE3, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
|
|
47250
47537
|
var init_state_renderer = __esm({
|
|
47251
47538
|
"src/graph/state-renderer.ts"() {
|
|
47252
47539
|
"use strict";
|
|
@@ -47265,7 +47552,7 @@ var init_state_renderer = __esm({
|
|
|
47265
47552
|
DIAGRAM_PADDING12 = 20;
|
|
47266
47553
|
MAX_SCALE7 = 3;
|
|
47267
47554
|
NODE_FONT_SIZE6 = 13;
|
|
47268
|
-
|
|
47555
|
+
EDGE_LABEL_FONT_SIZE11 = 11;
|
|
47269
47556
|
GROUP_LABEL_FONT_SIZE3 = 11;
|
|
47270
47557
|
ARROWHEAD_W5 = 10;
|
|
47271
47558
|
ARROWHEAD_H5 = 7;
|
|
@@ -50087,10 +50374,10 @@ function renderedDescNodeHeight(numLines, scale) {
|
|
|
50087
50374
|
return headerH + textBlockH + bottomPad;
|
|
50088
50375
|
}
|
|
50089
50376
|
function wrapEdgeLabelText(label, description, maxWidth = EDGE_LABEL_MAX_WIDTH) {
|
|
50090
|
-
const labelLines = label ? wrapTextToWidth(label,
|
|
50377
|
+
const labelLines = label ? wrapTextToWidth(label, EDGE_LABEL_FONT_SIZE12, maxWidth) : [];
|
|
50091
50378
|
const descLines = [];
|
|
50092
50379
|
for (const d of description) {
|
|
50093
|
-
descLines.push(...wrapTextToWidth(d,
|
|
50380
|
+
descLines.push(...wrapTextToWidth(d, EDGE_LABEL_FONT_SIZE12, maxWidth));
|
|
50094
50381
|
}
|
|
50095
50382
|
return { labelLines, descLines };
|
|
50096
50383
|
}
|
|
@@ -50228,9 +50515,9 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
|
|
|
50228
50515
|
const lineCount = labelLines.length + descLines.length;
|
|
50229
50516
|
let labelPxW = 0;
|
|
50230
50517
|
for (const l of labelLines)
|
|
50231
|
-
labelPxW = Math.max(labelPxW, measureText(l,
|
|
50518
|
+
labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50232
50519
|
for (const l of descLines)
|
|
50233
|
-
labelPxW = Math.max(labelPxW, measureText(l,
|
|
50520
|
+
labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50234
50521
|
const { labelX, labelY, labelAngle } = computeEdgeLabelPosition(
|
|
50235
50522
|
midAngle,
|
|
50236
50523
|
radius,
|
|
@@ -50332,9 +50619,9 @@ function fitToCanvas(nodes, edges, parsed, _cx, _cy, radius, width, height, _isC
|
|
|
50332
50619
|
);
|
|
50333
50620
|
let textWidth = 0;
|
|
50334
50621
|
for (const l of labelLines)
|
|
50335
|
-
textWidth = Math.max(textWidth, measureText(l,
|
|
50622
|
+
textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50336
50623
|
for (const l of descLines)
|
|
50337
|
-
textWidth = Math.max(textWidth, measureText(l,
|
|
50624
|
+
textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50338
50625
|
if (textWidth === 0) continue;
|
|
50339
50626
|
const normAngle = (le.labelAngle % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
|
50340
50627
|
const isRight = normAngle < Math.PI * 0.4 || normAngle > Math.PI * 1.6;
|
|
@@ -50408,7 +50695,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
|
|
|
50408
50695
|
const midAngle = srcExitAngle + dir * visibleSweep / 2;
|
|
50409
50696
|
return { path, midAngle };
|
|
50410
50697
|
}
|
|
50411
|
-
var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6,
|
|
50698
|
+
var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE12, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT4, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
|
|
50412
50699
|
var init_layout14 = __esm({
|
|
50413
50700
|
"src/cycle/layout.ts"() {
|
|
50414
50701
|
"use strict";
|
|
@@ -50419,7 +50706,7 @@ var init_layout14 = __esm({
|
|
|
50419
50706
|
LABEL_FONT_SIZE4 = 13;
|
|
50420
50707
|
CIRCLE_LABEL_FONT_SIZE = 16;
|
|
50421
50708
|
DESC_FONT_SIZE6 = 11;
|
|
50422
|
-
|
|
50709
|
+
EDGE_LABEL_FONT_SIZE12 = 11;
|
|
50423
50710
|
MIN_NODE_WIDTH4 = 70;
|
|
50424
50711
|
MAX_NODE_WIDTH3 = 260;
|
|
50425
50712
|
DESC_MIN_WIDTH = 140;
|
|
@@ -50565,7 +50852,7 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
50565
50852
|
const scaledNodeFont = Math.max(6, Math.round(NODE_FONT_SIZE7 * layout.scale));
|
|
50566
50853
|
const scaledEdgeLabelFont = Math.max(
|
|
50567
50854
|
6,
|
|
50568
|
-
Math.round(
|
|
50855
|
+
Math.round(EDGE_LABEL_FONT_SIZE13 * layout.scale)
|
|
50569
50856
|
);
|
|
50570
50857
|
const CIRCLE_LABEL_FONT_SIZE2 = 16;
|
|
50571
50858
|
const scaledCircleLabelFont = Math.max(
|
|
@@ -50728,7 +51015,7 @@ function ensureArrowMarker(defs, color, strokeWidth) {
|
|
|
50728
51015
|
const mh = Math.max(1.5, mw * 0.5);
|
|
50729
51016
|
defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${mw} ${mh}`).attr("refX", mw * 0.1).attr("refY", mh / 2).attr("markerWidth", mw).attr("markerHeight", mh).attr("orient", "auto").append("polygon").attr("points", `0,0 ${mw},${mh / 2} 0,${mh}`).attr("fill", color);
|
|
50730
51017
|
}
|
|
50731
|
-
var NODE_FONT_SIZE7, DESC_FONT_SIZE7,
|
|
51018
|
+
var NODE_FONT_SIZE7, DESC_FONT_SIZE7, EDGE_LABEL_FONT_SIZE13, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
|
|
50732
51019
|
var init_renderer15 = __esm({
|
|
50733
51020
|
"src/cycle/renderer.ts"() {
|
|
50734
51021
|
"use strict";
|
|
@@ -50745,7 +51032,7 @@ var init_renderer15 = __esm({
|
|
|
50745
51032
|
init_text_measure();
|
|
50746
51033
|
NODE_FONT_SIZE7 = 13;
|
|
50747
51034
|
DESC_FONT_SIZE7 = 11;
|
|
50748
|
-
|
|
51035
|
+
EDGE_LABEL_FONT_SIZE13 = 11;
|
|
50749
51036
|
DESC_LINE_HEIGHT8 = 15;
|
|
50750
51037
|
TITLE_AREA_HEIGHT = 50;
|
|
50751
51038
|
}
|
|
@@ -51143,7 +51430,7 @@ function resolveMap(parsed, data) {
|
|
|
51143
51430
|
const hint = suggest(r.name, allNames);
|
|
51144
51431
|
err(
|
|
51145
51432
|
r.lineNumber,
|
|
51146
|
-
`Unknown
|
|
51433
|
+
`Unknown region "${r.name}" \u2014 not a known country or US state.${hint ? " " + hint : ""} Search the exact token with \`dgmo map-search "${r.name}"\` (or the lookup_map_location tool), use an ISO code (e.g. FR, US-CA), or coordinates.`,
|
|
51147
51434
|
"E_MAP_UNKNOWN_SUBDIVISION"
|
|
51148
51435
|
);
|
|
51149
51436
|
continue;
|
|
@@ -51223,7 +51510,7 @@ function resolveMap(parsed, data) {
|
|
|
51223
51510
|
const hint = suggest(name, cityNames);
|
|
51224
51511
|
err(
|
|
51225
51512
|
line11,
|
|
51226
|
-
`Unknown place "${name}"
|
|
51513
|
+
`Unknown place "${name}" \u2014 not in the gazetteer.${hint ? " " + hint : ""} Search the exact token with \`dgmo map-search "${name}"\` (or the lookup_map_location tool), or use coordinates \`poi <lat> <lon>\`.`,
|
|
51227
51514
|
"E_MAP_UNKNOWN_PLACE"
|
|
51228
51515
|
);
|
|
51229
51516
|
return { kind: "miss" };
|
|
@@ -51765,6 +52052,21 @@ function mapLegendBand(legend, opts) {
|
|
|
51765
52052
|
if (height <= 0) return 0;
|
|
51766
52053
|
return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
|
|
51767
52054
|
}
|
|
52055
|
+
function mapLegendBox(legend, opts) {
|
|
52056
|
+
if (!legend) return null;
|
|
52057
|
+
const groups = mapLegendGroups(legend);
|
|
52058
|
+
if (groups.length === 0) return null;
|
|
52059
|
+
const config = mapLegendConfig(groups, opts.mode);
|
|
52060
|
+
const state = { activeGroup: legend.activeGroup };
|
|
52061
|
+
const { width, height } = computeLegendLayout(config, state, opts.width);
|
|
52062
|
+
if (height <= 0 || width <= 0) return null;
|
|
52063
|
+
return {
|
|
52064
|
+
x: (opts.width - width) / 2,
|
|
52065
|
+
y: mapLegendTop(opts.hasTitle, opts.hasSubtitle),
|
|
52066
|
+
width,
|
|
52067
|
+
height
|
|
52068
|
+
};
|
|
52069
|
+
}
|
|
51768
52070
|
var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
|
|
51769
52071
|
var init_legend_band = __esm({
|
|
51770
52072
|
"src/map/legend-band.ts"() {
|
|
@@ -51889,6 +52191,8 @@ function placeContextLabels(args) {
|
|
|
51889
52191
|
waterBodies,
|
|
51890
52192
|
countries,
|
|
51891
52193
|
palette,
|
|
52194
|
+
countryTone,
|
|
52195
|
+
waterTone,
|
|
51892
52196
|
project,
|
|
51893
52197
|
collides,
|
|
51894
52198
|
contentPoints,
|
|
@@ -51898,8 +52202,8 @@ function placeContextLabels(args) {
|
|
|
51898
52202
|
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
51899
52203
|
const budget = labelBudget(width, height, band);
|
|
51900
52204
|
if (budget <= 0) return [];
|
|
51901
|
-
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
51902
|
-
const countryColor = palette.textMuted;
|
|
52205
|
+
const waterColor = waterTone ?? mix(palette.colors.blue, palette.textMuted, 50);
|
|
52206
|
+
const countryColor = countryTone ?? palette.textMuted;
|
|
51903
52207
|
const haloColor = palette.bg;
|
|
51904
52208
|
const candidates = [];
|
|
51905
52209
|
const center = [width / 2, height / 2];
|
|
@@ -51988,8 +52292,7 @@ function placeContextLabels(args) {
|
|
|
51988
52292
|
)
|
|
51989
52293
|
);
|
|
51990
52294
|
const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
|
|
51991
|
-
const
|
|
51992
|
-
const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
|
|
52295
|
+
const color = countryColor;
|
|
51993
52296
|
const text = c.name;
|
|
51994
52297
|
const tw = labelWidth(text, 0, fontSize);
|
|
51995
52298
|
if (tw > w || fontSize + 2 * PADY > h) continue;
|
|
@@ -52083,7 +52386,7 @@ function placeContextLabels(args) {
|
|
|
52083
52386
|
}
|
|
52084
52387
|
return placed;
|
|
52085
52388
|
}
|
|
52086
|
-
var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX,
|
|
52389
|
+
var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, MAX_COUNTRY_POSITIONS, COUNTRY_POS_GRID, COUNTRY_POS_TOPN_MARGIN, KIND_ORDER;
|
|
52087
52390
|
var init_context_labels = __esm({
|
|
52088
52391
|
"src/map/context-labels.ts"() {
|
|
52089
52392
|
"use strict";
|
|
@@ -52100,7 +52403,6 @@ var init_context_labels = __esm({
|
|
|
52100
52403
|
COUNTRY_FONT_MAX = 22;
|
|
52101
52404
|
COUNTRY_SIZE_FRAC_MIN = 0.06;
|
|
52102
52405
|
COUNTRY_SIZE_FRAC_MAX = 0.32;
|
|
52103
|
-
COUNTRY_FADE_MAX = 45;
|
|
52104
52406
|
MAX_COUNTRY_POSITIONS = 4;
|
|
52105
52407
|
COUNTRY_POS_GRID = 5;
|
|
52106
52408
|
COUNTRY_POS_TOPN_MARGIN = 3;
|
|
@@ -52238,36 +52540,23 @@ function countryLabelPositions(args) {
|
|
|
52238
52540
|
return n;
|
|
52239
52541
|
};
|
|
52240
52542
|
const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
|
|
52241
|
-
const
|
|
52242
|
-
|
|
52243
|
-
|
|
52244
|
-
|
|
52245
|
-
|
|
52246
|
-
|
|
52247
|
-
|
|
52248
|
-
let bestIdx = 0;
|
|
52249
|
-
let bestMin = -1;
|
|
52250
|
-
for (let k = 0; k < pool.length; k++) {
|
|
52251
|
-
const c = pool[k];
|
|
52252
|
-
let minD = Infinity;
|
|
52253
|
-
for (const o of ordered) {
|
|
52254
|
-
const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
|
|
52255
|
-
if (d < minD) minD = d;
|
|
52256
|
-
}
|
|
52257
|
-
if (minD > bestMin) {
|
|
52258
|
-
bestMin = minD;
|
|
52259
|
-
bestIdx = k;
|
|
52260
|
-
}
|
|
52543
|
+
const centreLon = kept.reduce((s, c) => s + c.lon, 0) / kept.length;
|
|
52544
|
+
const centreLat = kept.reduce((s, c) => s + c.lat, 0) / kept.length;
|
|
52545
|
+
const centreScreen = project(centreLon, centreLat);
|
|
52546
|
+
const lead = pointInGeometry(geometry, centreLon, centreLat) && centreScreen && Number.isFinite(centreScreen[0]) && Number.isFinite(centreScreen[1]) && centreScreen[0] >= 0 && centreScreen[0] <= width && centreScreen[1] >= 0 && centreScreen[1] <= height ? [
|
|
52547
|
+
{
|
|
52548
|
+
lonLat: [centreLon, centreLat],
|
|
52549
|
+
screen: [centreScreen[0], centreScreen[1]]
|
|
52261
52550
|
}
|
|
52262
|
-
|
|
52263
|
-
|
|
52264
|
-
|
|
52551
|
+
] : [];
|
|
52552
|
+
const grid = [...kept].sort(
|
|
52553
|
+
(a, b) => interiorness(b) - interiorness(a) || dist2ToCentre(a) - dist2ToCentre(b)
|
|
52554
|
+
).map((c) => ({
|
|
52265
52555
|
lonLat: [c.lon, c.lat],
|
|
52266
52556
|
screen: [c.sx, c.sy]
|
|
52267
52557
|
}));
|
|
52268
52558
|
const curatedPos = curated ? mkCurated(curated, project) : [];
|
|
52269
|
-
|
|
52270
|
-
return out;
|
|
52559
|
+
return [...curatedPos, ...lead, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
|
|
52271
52560
|
}
|
|
52272
52561
|
function mkCurated(curated, project) {
|
|
52273
52562
|
if (!curated) return [];
|
|
@@ -52533,6 +52822,25 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52533
52822
|
palette.bg,
|
|
52534
52823
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
52535
52824
|
);
|
|
52825
|
+
const contrastFloorTone = (tone, ...substrates) => {
|
|
52826
|
+
const clears = (t) => substrates.every((s) => contrastRatio(t, s) >= REGION_LABEL_HALO_RATIO);
|
|
52827
|
+
if (clears(tone)) return tone;
|
|
52828
|
+
for (let s = 10; s <= 100; s += 10) {
|
|
52829
|
+
const t = mix(palette.text, tone, s);
|
|
52830
|
+
if (clears(t)) return t;
|
|
52831
|
+
}
|
|
52832
|
+
return palette.text;
|
|
52833
|
+
};
|
|
52834
|
+
const backdropLandTone = contrastFloorTone(
|
|
52835
|
+
palette.textMuted,
|
|
52836
|
+
neutralFill,
|
|
52837
|
+
water
|
|
52838
|
+
);
|
|
52839
|
+
const backdropWaterTone = contrastFloorTone(
|
|
52840
|
+
mix(palette.colors.blue, palette.textMuted, 50),
|
|
52841
|
+
water,
|
|
52842
|
+
neutralFill
|
|
52843
|
+
);
|
|
52536
52844
|
const hasDirectColor = resolved.regions.some((r) => r.color !== void 0);
|
|
52537
52845
|
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && !hasDirectColor && fillGroupNames.size === 0;
|
|
52538
52846
|
const colorByIso = /* @__PURE__ */ new Map();
|
|
@@ -52647,6 +52955,33 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52647
52955
|
hasSubtitle: Boolean(resolved.subtitle)
|
|
52648
52956
|
});
|
|
52649
52957
|
if (legendBand > topPad) topPad = legendBand;
|
|
52958
|
+
const topReserved = [];
|
|
52959
|
+
if (shownTitle) {
|
|
52960
|
+
const lines = resolved.subtitle ? 2 : 1;
|
|
52961
|
+
const tw = Math.max(
|
|
52962
|
+
measureLegendText(shownTitle, TITLE_FONT_SIZE),
|
|
52963
|
+
resolved.subtitle ? measureLegendText(resolved.subtitle, TITLE_FONT_SIZE) : 0
|
|
52964
|
+
) + 2 * TITLE_FONT_SIZE;
|
|
52965
|
+
topReserved.push({
|
|
52966
|
+
x: width / 2 - tw / 2,
|
|
52967
|
+
y: TITLE_Y - TITLE_FONT_SIZE,
|
|
52968
|
+
w: tw,
|
|
52969
|
+
h: TITLE_FONT_SIZE * (lines + 0.5)
|
|
52970
|
+
});
|
|
52971
|
+
}
|
|
52972
|
+
const legendBox = mapLegendBox(legend, {
|
|
52973
|
+
width,
|
|
52974
|
+
mode: opts.legendMode ?? "preview",
|
|
52975
|
+
hasTitle: Boolean(shownTitle),
|
|
52976
|
+
hasSubtitle: Boolean(resolved.subtitle)
|
|
52977
|
+
});
|
|
52978
|
+
if (legendBox)
|
|
52979
|
+
topReserved.push({
|
|
52980
|
+
x: legendBox.x,
|
|
52981
|
+
y: legendBox.y,
|
|
52982
|
+
w: legendBox.width,
|
|
52983
|
+
h: legendBox.height
|
|
52984
|
+
});
|
|
52650
52985
|
const reserve = opts._calloutReserve;
|
|
52651
52986
|
const fitLeft = FIT_PAD + (reserve?.left ?? 0);
|
|
52652
52987
|
const fitRight = width - FIT_PAD - (reserve?.right ?? 0);
|
|
@@ -53376,6 +53711,19 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53376
53711
|
const by = b.cy - (b.cy - py) / tb * trimB;
|
|
53377
53712
|
return `M${ax},${ay}Q${px},${py} ${bx},${by}`;
|
|
53378
53713
|
};
|
|
53714
|
+
const legLabelPoint = (a, b, curved, offset) => {
|
|
53715
|
+
const mx = (a.cx + b.cx) / 2;
|
|
53716
|
+
const my = (a.cy + b.cy) / 2;
|
|
53717
|
+
if (!curved && offset === 0) return { x: mx, y: my - 4 };
|
|
53718
|
+
const dx = b.cx - a.cx;
|
|
53719
|
+
const dy = b.cy - a.cy;
|
|
53720
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
53721
|
+
const nx = -dy / len;
|
|
53722
|
+
const ny = dx / len;
|
|
53723
|
+
const bow = offset !== 0 ? offset : len * ARC_CURVE_FRAC;
|
|
53724
|
+
const off = bow * 0.5 + Math.sign(bow || 1) * 8;
|
|
53725
|
+
return { x: mx + nx * off, y: my + ny * off };
|
|
53726
|
+
};
|
|
53379
53727
|
const routeLegVals = resolved.routes.flatMap((rt) => rt.legs).map((l) => Number(l.value)).filter((n) => Number.isFinite(n) && n > 0);
|
|
53380
53728
|
const rlMin = routeLegVals.length ? Math.min(...routeLegVals) : 0;
|
|
53381
53729
|
const rlMax = routeLegVals.length ? Math.max(...routeLegVals) : 0;
|
|
@@ -53389,13 +53737,12 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53389
53737
|
const a = poiScreen.get(leg.fromId);
|
|
53390
53738
|
const b = poiScreen.get(leg.toId);
|
|
53391
53739
|
if (!a || !b) continue;
|
|
53392
|
-
const
|
|
53393
|
-
const my = (a.cy + b.cy) / 2;
|
|
53740
|
+
const lp = legLabelPoint(a, b, leg.style === "arc", 0);
|
|
53394
53741
|
const bow = {
|
|
53395
53742
|
curved: leg.style === "arc",
|
|
53396
53743
|
offset: 0,
|
|
53397
|
-
labelX:
|
|
53398
|
-
labelY:
|
|
53744
|
+
labelX: lp.x,
|
|
53745
|
+
labelY: lp.y
|
|
53399
53746
|
};
|
|
53400
53747
|
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53401
53748
|
legs.push({
|
|
@@ -53442,13 +53789,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53442
53789
|
const b = poiScreen.get(e.toId);
|
|
53443
53790
|
if (!a || !b) return;
|
|
53444
53791
|
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
53445
|
-
const
|
|
53446
|
-
const
|
|
53792
|
+
const curved = e.style === "arc" || n > 1;
|
|
53793
|
+
const lp = legLabelPoint(a, b, curved, fanOffset);
|
|
53447
53794
|
const bow = {
|
|
53448
|
-
curved
|
|
53795
|
+
curved,
|
|
53449
53796
|
offset: fanOffset,
|
|
53450
|
-
labelX:
|
|
53451
|
-
labelY:
|
|
53797
|
+
labelX: lp.x,
|
|
53798
|
+
labelY: lp.y
|
|
53452
53799
|
};
|
|
53453
53800
|
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53454
53801
|
legs.push({
|
|
@@ -53472,7 +53819,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53472
53819
|
});
|
|
53473
53820
|
}
|
|
53474
53821
|
const labels = [];
|
|
53475
|
-
const obstacles = [];
|
|
53822
|
+
const obstacles = [...topReserved];
|
|
53476
53823
|
const regionLabelGuards = [];
|
|
53477
53824
|
const markers = pois.map((p) => ({
|
|
53478
53825
|
cx: p.cx,
|
|
@@ -53510,6 +53857,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53510
53857
|
}
|
|
53511
53858
|
const collides = (rect) => markers.some((m) => rectCircleOverlap(rect, m)) || obstacles.some((o) => rectsOverlap(rect, o)) || legSegments.some((s) => segmentRectOverlap(s[0], s[1], s[2], s[3], rect));
|
|
53512
53859
|
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
53860
|
+
const labeledRegionIds = /* @__PURE__ */ new Set();
|
|
53513
53861
|
const showRegionValues = resolved.directives.noRegionValue !== true && activeIsScore;
|
|
53514
53862
|
const regionValueStr = (value) => showRegionValues && value !== void 0 ? compactNumber(value) : void 0;
|
|
53515
53863
|
const isCompact = width < COMPACT_WIDTH_PX;
|
|
@@ -53530,7 +53878,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53530
53878
|
const REGION_FONT_MAX_ORIENT = 22;
|
|
53531
53879
|
const REGION_SIZE_FRAC_MIN = 0.06;
|
|
53532
53880
|
const REGION_SIZE_FRAC_MAX = 0.32;
|
|
53533
|
-
const
|
|
53881
|
+
const SUBJECT_FONT_MAX = 18;
|
|
53882
|
+
const SUBJECT_MIN_PROMINENCE = 13;
|
|
53883
|
+
const SUBJECT_LEADER_FONT = 15;
|
|
53534
53884
|
const canvasLinear = Math.sqrt(Math.max(1, width * height));
|
|
53535
53885
|
const sizeT = (boxW, boxH) => {
|
|
53536
53886
|
const frac = Math.sqrt(Math.max(0, boxW * boxH)) / canvasLinear;
|
|
@@ -53542,9 +53892,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53542
53892
|
)
|
|
53543
53893
|
);
|
|
53544
53894
|
};
|
|
53545
|
-
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2,
|
|
53895
|
+
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2, colorOverride) => {
|
|
53546
53896
|
const { color: baseColor, haloColor } = labelOnFill(fill2);
|
|
53547
|
-
const color =
|
|
53897
|
+
const color = colorOverride ?? baseColor;
|
|
53548
53898
|
const vf = Math.round(fontSize * 0.82);
|
|
53549
53899
|
const halfW = Math.max(
|
|
53550
53900
|
measureLegendText(text, fontSize),
|
|
@@ -53559,7 +53909,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53559
53909
|
text,
|
|
53560
53910
|
anchor: "middle",
|
|
53561
53911
|
color,
|
|
53562
|
-
|
|
53912
|
+
// A backdrop label (colorOverride) NEVER haloes: the placement gate above
|
|
53913
|
+
// guarantees it sits on a floored-readable substrate (own fill, neutral
|
|
53914
|
+
// land, foreign land, or water), so a halo is pure noise. Data labels keep
|
|
53915
|
+
// the containment-gated halo until the role-tier pass (Phase 3).
|
|
53916
|
+
halo: colorOverride ? false : overflows,
|
|
53563
53917
|
haloColor,
|
|
53564
53918
|
...fontSize !== FONT2 && { fontSize },
|
|
53565
53919
|
...valueLine !== void 0 && { valueLine },
|
|
@@ -53595,7 +53949,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53595
53949
|
if (!c || !Number.isFinite(c[0])) return null;
|
|
53596
53950
|
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
53597
53951
|
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
53598
|
-
const placedRegionRects = [];
|
|
53952
|
+
const placedRegionRects = [...topReserved];
|
|
53599
53953
|
const placedLeaders = [];
|
|
53600
53954
|
const isEmptyFill = (f) => f === water || f === neutralFill || f === foreignFill;
|
|
53601
53955
|
const POI_LABEL_PAD = 14;
|
|
@@ -53608,9 +53962,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53608
53962
|
const SHORT_HOP_MAX = Math.max(46, Math.min(width, height) * 0.11);
|
|
53609
53963
|
const STEP = 4;
|
|
53610
53964
|
const HOP_GAP = 9;
|
|
53611
|
-
const tryShortHopCallout = (cx, cy, fill2, name, value) => {
|
|
53612
|
-
const chipW = stackW(name, value);
|
|
53613
|
-
const chipH = stackH(value !== void 0);
|
|
53965
|
+
const tryShortHopCallout = (cx, cy, fill2, name, value, font = FONT2, gap = HOP_GAP, maxLen = SHORT_HOP_MAX) => {
|
|
53966
|
+
const chipW = stackW(name, value, font);
|
|
53967
|
+
const chipH = stackH(value !== void 0, font);
|
|
53614
53968
|
const dirs = [
|
|
53615
53969
|
[0, -1],
|
|
53616
53970
|
// north (over Canada)
|
|
@@ -53626,7 +53980,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53626
53980
|
let ex = cx;
|
|
53627
53981
|
let ey = cy;
|
|
53628
53982
|
let steps = 0;
|
|
53629
|
-
const maxSteps = Math.ceil(
|
|
53983
|
+
const maxSteps = Math.ceil(maxLen / STEP) + 1;
|
|
53630
53984
|
while (steps < maxSteps && fillAt(ex, ey) === fill2) {
|
|
53631
53985
|
ex += dx * STEP;
|
|
53632
53986
|
ey += dy * STEP;
|
|
@@ -53634,8 +53988,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53634
53988
|
}
|
|
53635
53989
|
if (fillAt(ex, ey) === fill2) continue;
|
|
53636
53990
|
const halfAlong = (Math.abs(dx) * chipW + Math.abs(dy) * chipH) / 2;
|
|
53637
|
-
const ccx = ex + dx * (
|
|
53638
|
-
const ccy = ey + dy * (
|
|
53991
|
+
const ccx = ex + dx * (gap + halfAlong);
|
|
53992
|
+
const ccy = ey + dy * (gap + halfAlong);
|
|
53639
53993
|
const rect = {
|
|
53640
53994
|
x: ccx - chipW / 2,
|
|
53641
53995
|
y: ccy - chipH / 2,
|
|
@@ -53660,7 +54014,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53660
54014
|
const innerX = ccx - dx * (chipW / 2);
|
|
53661
54015
|
const innerY = ccy - dy * (chipH / 2);
|
|
53662
54016
|
const len = Math.hypot(innerX - cx, innerY - cy);
|
|
53663
|
-
if (len >
|
|
54017
|
+
if (len > maxLen) continue;
|
|
53664
54018
|
if (placedLeaders.some(
|
|
53665
54019
|
(l) => segmentsCross(cx, cy, innerX, innerY, l[0], l[1], l[2], l[3])
|
|
53666
54020
|
))
|
|
@@ -53684,6 +54038,78 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53684
54038
|
const valStr = regionValueStr(r.value);
|
|
53685
54039
|
const fitsRegions = (rect) => !placedRegionRects.some((p) => rectsOverlap(rect, p));
|
|
53686
54040
|
const fitsPois = (rect) => !poiObstacles.some((o) => rectsOverlap(rect, o));
|
|
54041
|
+
const isSubject = regionById.has(r.id);
|
|
54042
|
+
const extremesOnFill = (ax, ay, text) => {
|
|
54043
|
+
const halfW = measureLegendText(text, FONT2) / 2;
|
|
54044
|
+
return fillAt(ax - halfW, ay) === r.fill && fillAt(ax + halfW, ay) === r.fill;
|
|
54045
|
+
};
|
|
54046
|
+
if (isSubject && valStr === void 0 && r.label !== void 0) {
|
|
54047
|
+
const name = candidates[0];
|
|
54048
|
+
let best = -1;
|
|
54049
|
+
for (let f = SUBJECT_FONT_MAX; f >= FONT2; f--) {
|
|
54050
|
+
if (measureLegendText(name, f) > boxW || f + 2 * LABEL_PADY > boxH)
|
|
54051
|
+
continue;
|
|
54052
|
+
const rect = regionLabelRect(c[0], c[1], name, void 0, f);
|
|
54053
|
+
if (!fitsRegions(rect) || !fitsPois(rect) || collides(rect)) continue;
|
|
54054
|
+
const halfW = measureLegendText(name, f) / 2;
|
|
54055
|
+
if (fillAt(c[0] - halfW, c[1]) !== r.fill || fillAt(c[0] + halfW, c[1]) !== r.fill)
|
|
54056
|
+
continue;
|
|
54057
|
+
best = f;
|
|
54058
|
+
break;
|
|
54059
|
+
}
|
|
54060
|
+
if (best >= SUBJECT_MIN_PROMINENCE) {
|
|
54061
|
+
const rect = regionLabelRect(c[0], c[1], name, void 0, best);
|
|
54062
|
+
placedRegionRects.push(rect);
|
|
54063
|
+
pushRegionLabel(
|
|
54064
|
+
c[0],
|
|
54065
|
+
c[1],
|
|
54066
|
+
name,
|
|
54067
|
+
r.fill,
|
|
54068
|
+
r.lineNumber,
|
|
54069
|
+
void 0,
|
|
54070
|
+
best
|
|
54071
|
+
);
|
|
54072
|
+
labeledRegionIds.add(r.id);
|
|
54073
|
+
regionLabelGuards.push({ label: labels[labels.length - 1], rect });
|
|
54074
|
+
} else {
|
|
54075
|
+
const hop = tryShortHopCallout(
|
|
54076
|
+
c[0],
|
|
54077
|
+
c[1],
|
|
54078
|
+
r.fill,
|
|
54079
|
+
r.label,
|
|
54080
|
+
void 0,
|
|
54081
|
+
SUBJECT_LEADER_FONT,
|
|
54082
|
+
HOP_GAP + SUBJECT_LEADER_FONT,
|
|
54083
|
+
SHORT_HOP_MAX * 2.2
|
|
54084
|
+
);
|
|
54085
|
+
if (hop) {
|
|
54086
|
+
placedRegionRects.push(hop.rect);
|
|
54087
|
+
placedLeaders.push(hop.leader);
|
|
54088
|
+
const dark = mix(r.fill, palette.text, 60);
|
|
54089
|
+
labels.push({
|
|
54090
|
+
x: hop.x,
|
|
54091
|
+
y: hop.y,
|
|
54092
|
+
text: r.label,
|
|
54093
|
+
anchor: "middle",
|
|
54094
|
+
color: palette.text,
|
|
54095
|
+
halo: false,
|
|
54096
|
+
haloColor: palette.bg,
|
|
54097
|
+
fontSize: SUBJECT_LEADER_FONT,
|
|
54098
|
+
leader: {
|
|
54099
|
+
x1: hop.leader[0],
|
|
54100
|
+
y1: hop.leader[1],
|
|
54101
|
+
x2: hop.leader[2],
|
|
54102
|
+
y2: hop.leader[3]
|
|
54103
|
+
},
|
|
54104
|
+
leaderColor: dark,
|
|
54105
|
+
calloutDot: { x: c[0], y: c[1], color: dark },
|
|
54106
|
+
lineNumber: r.lineNumber
|
|
54107
|
+
});
|
|
54108
|
+
labeledRegionIds.add(r.id);
|
|
54109
|
+
}
|
|
54110
|
+
}
|
|
54111
|
+
continue;
|
|
54112
|
+
}
|
|
53687
54113
|
const seekAnchors = [
|
|
53688
54114
|
{ x: c[0], y: c[1], guard: false }
|
|
53689
54115
|
];
|
|
@@ -53706,13 +54132,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53706
54132
|
const nameRect = regionLabelRect(a.x, a.y, t);
|
|
53707
54133
|
if (valStr && stackW(t, valStr) <= boxW && stackH(true) <= boxH) {
|
|
53708
54134
|
const stackRect = regionLabelRect(a.x, a.y, t, valStr);
|
|
53709
|
-
if (fitsRegions(stackRect) && fitsPois(nameRect)) {
|
|
54135
|
+
if (fitsRegions(stackRect) && fitsPois(nameRect) && extremesOnFill(a.x, a.y, t)) {
|
|
53710
54136
|
chosen = { text: t, valueLine: valStr, ax: a.x, ay: a.y };
|
|
53711
54137
|
break;
|
|
53712
54138
|
}
|
|
53713
54139
|
}
|
|
53714
54140
|
if (labelW(t) <= boxW && labelH <= boxH) {
|
|
53715
|
-
if (fitsRegions(nameRect) && fitsPois(nameRect)) {
|
|
54141
|
+
if (fitsRegions(nameRect) && fitsPois(nameRect) && (valStr === void 0 || extremesOnFill(a.x, a.y, t))) {
|
|
53716
54142
|
chosen = { text: t, ax: a.x, ay: a.y };
|
|
53717
54143
|
break;
|
|
53718
54144
|
}
|
|
@@ -53720,7 +54146,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53720
54146
|
}
|
|
53721
54147
|
if (chosen) break;
|
|
53722
54148
|
}
|
|
53723
|
-
if (chosen === void 0 &&
|
|
54149
|
+
if (chosen === void 0 && r.label !== void 0 && isSubject) {
|
|
53724
54150
|
const hop = tryShortHopCallout(c[0], c[1], r.fill, r.label, valStr);
|
|
53725
54151
|
if (hop) {
|
|
53726
54152
|
placedRegionRects.push(hop.rect);
|
|
@@ -53732,9 +54158,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53732
54158
|
text: r.label,
|
|
53733
54159
|
anchor: "middle",
|
|
53734
54160
|
color: palette.text,
|
|
53735
|
-
halo:
|
|
54161
|
+
halo: false,
|
|
53736
54162
|
haloColor: palette.bg,
|
|
53737
|
-
valueLine: valStr,
|
|
54163
|
+
...valStr !== void 0 && { valueLine: valStr },
|
|
53738
54164
|
leader: {
|
|
53739
54165
|
x1: hop.leader[0],
|
|
53740
54166
|
y1: hop.leader[1],
|
|
@@ -53745,20 +54171,28 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53745
54171
|
calloutDot: { x: c[0], y: c[1], color: dark },
|
|
53746
54172
|
lineNumber: r.lineNumber
|
|
53747
54173
|
});
|
|
54174
|
+
labeledRegionIds.add(r.id);
|
|
53748
54175
|
}
|
|
53749
54176
|
continue;
|
|
53750
54177
|
}
|
|
53751
54178
|
if (chosen === void 0) continue;
|
|
53752
54179
|
const isOrient = r.value === void 0 && r.layer === "base";
|
|
53753
54180
|
let font = FONT2;
|
|
53754
|
-
let fade = 0;
|
|
53755
54181
|
if (isOrient) {
|
|
53756
54182
|
const growT = sizeT(boxW, boxH);
|
|
53757
54183
|
const desiredFont = Math.round(
|
|
53758
54184
|
FONT2 + growT * (REGION_FONT_MAX_ORIENT - FONT2)
|
|
53759
54185
|
);
|
|
53760
54186
|
const hasVal = chosen.valueLine !== void 0;
|
|
53761
|
-
|
|
54187
|
+
const extremesClean = (f) => {
|
|
54188
|
+
const halfW = measureLegendText(chosen.text, f) / 2;
|
|
54189
|
+
return [chosen.ax - halfW, chosen.ax + halfW].every((sx) => {
|
|
54190
|
+
const fAt = fillAt(sx, chosen.ay);
|
|
54191
|
+
return fAt === r.fill || isEmptyFill(fAt);
|
|
54192
|
+
});
|
|
54193
|
+
};
|
|
54194
|
+
let fit = -1;
|
|
54195
|
+
for (let f = desiredFont; f >= FONT2; f--) {
|
|
53762
54196
|
if (stackW(chosen.text, chosen.valueLine, f) > boxW || stackH(hasVal, f) > boxH)
|
|
53763
54197
|
continue;
|
|
53764
54198
|
const gRect = regionLabelRect(
|
|
@@ -53775,14 +54209,18 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53775
54209
|
void 0,
|
|
53776
54210
|
f
|
|
53777
54211
|
);
|
|
53778
|
-
if (!fitsRegions(gRect) || !fitsPois(gName))
|
|
53779
|
-
const halfW = measureLegendText(chosen.text, f) / 2;
|
|
53780
|
-
if (fillAt(chosen.ax - halfW, chosen.ay) !== r.fill || fillAt(chosen.ax + halfW, chosen.ay) !== r.fill)
|
|
54212
|
+
if (!fitsRegions(gRect) || !fitsPois(gName) || collides(gName))
|
|
53781
54213
|
continue;
|
|
53782
|
-
|
|
54214
|
+
if (!extremesClean(f)) continue;
|
|
54215
|
+
fit = f;
|
|
53783
54216
|
break;
|
|
53784
54217
|
}
|
|
53785
|
-
|
|
54218
|
+
if (fit < 0) {
|
|
54219
|
+
if (!frameContainers.has(r.id)) continue;
|
|
54220
|
+
font = FONT2;
|
|
54221
|
+
} else {
|
|
54222
|
+
font = fit;
|
|
54223
|
+
}
|
|
53786
54224
|
}
|
|
53787
54225
|
const rRect = regionLabelRect(
|
|
53788
54226
|
chosen.ax,
|
|
@@ -53800,8 +54238,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53800
54238
|
r.lineNumber,
|
|
53801
54239
|
chosen.valueLine,
|
|
53802
54240
|
font,
|
|
53803
|
-
|
|
54241
|
+
isOrient ? backdropLandTone : void 0
|
|
53804
54242
|
);
|
|
54243
|
+
labeledRegionIds.add(r.id);
|
|
53805
54244
|
regionLabelGuards.push({
|
|
53806
54245
|
label: labels[labels.length - 1],
|
|
53807
54246
|
rect: rRect
|
|
@@ -53819,6 +54258,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53819
54258
|
seed.lineNumber,
|
|
53820
54259
|
valStr
|
|
53821
54260
|
);
|
|
54261
|
+
labeledRegionIds.add(seed.iso);
|
|
53822
54262
|
regionLabelGuards.push({
|
|
53823
54263
|
label: labels[labels.length - 1],
|
|
53824
54264
|
rect: regionLabelRect(seed.x, seed.y, text, valStr)
|
|
@@ -54250,13 +54690,30 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54250
54690
|
const nx = -dy / len;
|
|
54251
54691
|
const ny = dx / len;
|
|
54252
54692
|
const perp = labelH + 2;
|
|
54693
|
+
const pm = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
54694
|
+
lg.d
|
|
54695
|
+
);
|
|
54696
|
+
const pointAt = (t) => {
|
|
54697
|
+
if (pm?.[5] !== void 0) {
|
|
54698
|
+
const u = 1 - t;
|
|
54699
|
+
return [
|
|
54700
|
+
u * u * +pm[1] + 2 * u * t * +pm[5] + t * t * +pm[7],
|
|
54701
|
+
u * u * +pm[2] + 2 * u * t * +pm[6] + t * t * +pm[8]
|
|
54702
|
+
];
|
|
54703
|
+
}
|
|
54704
|
+
if (pm?.[3] !== void 0)
|
|
54705
|
+
return [
|
|
54706
|
+
+pm[1] + (+pm[3] - +pm[1]) * t,
|
|
54707
|
+
+pm[2] + (+pm[4] - +pm[2]) * t
|
|
54708
|
+
];
|
|
54709
|
+
return [a.cx + dx * t, a.cy + dy * t];
|
|
54710
|
+
};
|
|
54253
54711
|
const candidates = [];
|
|
54254
54712
|
for (const off of [0, perp, -perp, 2 * perp, -2 * perp])
|
|
54255
|
-
for (const t of T_LIST)
|
|
54256
|
-
|
|
54257
|
-
|
|
54258
|
-
|
|
54259
|
-
]);
|
|
54713
|
+
for (const t of T_LIST) {
|
|
54714
|
+
const [bx, by] = pointAt(t);
|
|
54715
|
+
candidates.push([bx + nx * off, by + ny * off - 4]);
|
|
54716
|
+
}
|
|
54260
54717
|
const clean = ([cx2, cy2]) => {
|
|
54261
54718
|
const rect2 = { x: cx2 - w / 2, y: cy2 - labelH / 2, w, h: labelH };
|
|
54262
54719
|
if (rect2.x < 0 || rect2.x + w > width || rect2.y < 0 || rect2.y + rect2.h > height)
|
|
@@ -54266,10 +54723,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54266
54723
|
if (placedEdge.some((o) => rectsOverlap(o, rect2))) return false;
|
|
54267
54724
|
return true;
|
|
54268
54725
|
};
|
|
54269
|
-
const
|
|
54270
|
-
|
|
54271
|
-
a.cy + dy * 0.5 - 4
|
|
54272
|
-
];
|
|
54726
|
+
const mid = pointAt(0.5);
|
|
54727
|
+
const [cx, cy] = candidates.find(clean) ?? [mid[0], mid[1] - 4];
|
|
54273
54728
|
const style = labelOnFill(fillAt(cx, cy));
|
|
54274
54729
|
legs[i] = {
|
|
54275
54730
|
...lg,
|
|
@@ -54297,7 +54752,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54297
54752
|
const rawCountries = [];
|
|
54298
54753
|
for (const f of worldLayer.values()) {
|
|
54299
54754
|
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
54300
|
-
if (!iso || regionById.has(iso)) continue;
|
|
54755
|
+
if (!iso || regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54301
54756
|
let hasReferencedSub = false;
|
|
54302
54757
|
for (const k of regionById.keys())
|
|
54303
54758
|
if (k.startsWith(iso + "-")) {
|
|
@@ -54371,7 +54826,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54371
54826
|
);
|
|
54372
54827
|
if (usLayer && framedStateContainers) {
|
|
54373
54828
|
for (const [iso, f] of usLayer) {
|
|
54374
|
-
if (regionById.has(iso)) continue;
|
|
54829
|
+
if (regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54375
54830
|
const viewF = cullFeatureToView(f);
|
|
54376
54831
|
if (!viewF) continue;
|
|
54377
54832
|
const b = path.bounds(viewF);
|
|
@@ -54395,6 +54850,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54395
54850
|
waterBodies: data.waterBodies,
|
|
54396
54851
|
countries: countryCandidates,
|
|
54397
54852
|
palette,
|
|
54853
|
+
// One muted, contrast-floored tone shared with the orientation backdrop
|
|
54854
|
+
// (region pass) so a context country (Canada) and an orientation region
|
|
54855
|
+
// (Minnesota) read identically — never below the WCAG-AA floor, no fade.
|
|
54856
|
+
countryTone: backdropLandTone,
|
|
54857
|
+
waterTone: backdropWaterTone,
|
|
54398
54858
|
project,
|
|
54399
54859
|
collides,
|
|
54400
54860
|
contentPoints,
|
|
@@ -54948,19 +55408,60 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
54948
55408
|
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
54949
55409
|
};
|
|
54950
55410
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
54951
|
-
|
|
54952
|
-
const
|
|
55411
|
+
const legEnd = (d) => {
|
|
55412
|
+
const m = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
55413
|
+
d
|
|
55414
|
+
);
|
|
55415
|
+
if (!m) return null;
|
|
55416
|
+
const x0 = +m[1];
|
|
55417
|
+
const y0 = +m[2];
|
|
55418
|
+
let ex;
|
|
55419
|
+
let ey;
|
|
55420
|
+
let fromX;
|
|
55421
|
+
let fromY;
|
|
55422
|
+
if (m[3] !== void 0) {
|
|
55423
|
+
ex = +m[3];
|
|
55424
|
+
ey = +m[4];
|
|
55425
|
+
fromX = x0;
|
|
55426
|
+
fromY = y0;
|
|
55427
|
+
} else {
|
|
55428
|
+
ex = +m[7];
|
|
55429
|
+
ey = +m[8];
|
|
55430
|
+
fromX = +m[5];
|
|
55431
|
+
fromY = +m[6];
|
|
55432
|
+
}
|
|
55433
|
+
const dx = ex - fromX;
|
|
55434
|
+
const dy = ey - fromY;
|
|
55435
|
+
const len = Math.hypot(dx, dy);
|
|
55436
|
+
if (!(len > 0)) return null;
|
|
55437
|
+
return { ex, ey, tx: dx / len, ty: dy / len };
|
|
55438
|
+
};
|
|
55439
|
+
layout.legs.forEach((leg) => {
|
|
55440
|
+
const end = leg.arrow ? legEnd(leg.d) : null;
|
|
55441
|
+
let drawD = leg.d;
|
|
55442
|
+
let arrowTri = null;
|
|
55443
|
+
if (end) {
|
|
55444
|
+
const aLen = arrowSize(leg.width);
|
|
55445
|
+
const aHalf = Math.max(aLen * 0.5, leg.width * 0.6 + 1);
|
|
55446
|
+
const bx = end.ex - end.tx * aLen;
|
|
55447
|
+
const by = end.ey - end.ty * aLen;
|
|
55448
|
+
const px = -end.ty;
|
|
55449
|
+
const py = end.tx;
|
|
55450
|
+
arrowTri = `M${bx + px * aHalf},${by + py * aHalf}L${end.ex},${end.ey}L${bx - px * aHalf},${by - py * aHalf}z`;
|
|
55451
|
+
drawD = leg.d.replace(
|
|
55452
|
+
/(-?[\d.]+),(-?[\d.]+)$/,
|
|
55453
|
+
`${bx.toFixed(2)},${by.toFixed(2)}`
|
|
55454
|
+
);
|
|
55455
|
+
}
|
|
55456
|
+
const p = gLegs.append("path").attr("d", drawD).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round").attr("data-from-id", leg.fromId).attr("data-to-id", leg.toId);
|
|
54953
55457
|
if (leg.tags) {
|
|
54954
55458
|
for (const [group, value] of Object.entries(leg.tags)) {
|
|
54955
55459
|
p.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
54956
55460
|
}
|
|
54957
55461
|
}
|
|
54958
55462
|
wireSync(p, leg.lineNumber);
|
|
54959
|
-
if (
|
|
54960
|
-
|
|
54961
|
-
const s = arrowSize(leg.width);
|
|
54962
|
-
defs.append("marker").attr("id", id).attr("viewBox", "0 0 10 10").attr("refX", 10).attr("refY", 5).attr("markerUnits", "userSpaceOnUse").attr("markerWidth", s).attr("markerHeight", s).attr("orient", "auto-start-reverse").append("path").attr("d", "M0,0L10,5L0,10z").attr("fill", leg.color);
|
|
54963
|
-
p.attr("marker-end", `url(#${id})`);
|
|
55463
|
+
if (arrowTri) {
|
|
55464
|
+
gLegs.append("path").attr("d", arrowTri).attr("fill", leg.color).attr("stroke", leg.color).attr("stroke-width", 1).attr("stroke-linejoin", "round").attr("data-from-id", leg.fromId).attr("data-to-id", leg.toId);
|
|
54964
55465
|
}
|
|
54965
55466
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
54966
55467
|
const lt = emitText(
|
|
@@ -58587,9 +59088,9 @@ var init_renderer20 = __esm({
|
|
|
58587
59088
|
}
|
|
58588
59089
|
});
|
|
58589
59090
|
|
|
58590
|
-
// node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js
|
|
59091
|
+
// ../../diagrammo/dgmo/node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js
|
|
58591
59092
|
var require_lz_string = __commonJS({
|
|
58592
|
-
"node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js"(exports, module) {
|
|
59093
|
+
"../../diagrammo/dgmo/node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js"(exports, module) {
|
|
58593
59094
|
"use strict";
|
|
58594
59095
|
var LZString = (function() {
|
|
58595
59096
|
var f = String.fromCharCode;
|