@diagrammo/dgmo 0.34.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 +462 -465
- package/dist/advanced.d.cts +2 -51
- package/dist/advanced.d.ts +2 -51
- package/dist/advanced.js +462 -466
- package/dist/auto.cjs +463 -142
- package/dist/auto.js +91 -91
- package/dist/auto.mjs +463 -142
- package/dist/cli.cjs +143 -326
- package/dist/index.cjs +462 -141
- package/dist/index.js +462 -141
- package/docs/ai-integration.md +46 -26
- package/docs/language-reference.md +2 -2
- package/package.json +2 -2
- package/src/advanced.ts +0 -12
- 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);
|
|
@@ -51323,7 +51430,7 @@ function resolveMap(parsed, data) {
|
|
|
51323
51430
|
const hint = suggest(r.name, allNames);
|
|
51324
51431
|
err(
|
|
51325
51432
|
r.lineNumber,
|
|
51326
|
-
`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.`,
|
|
51327
51434
|
"E_MAP_UNKNOWN_SUBDIVISION"
|
|
51328
51435
|
);
|
|
51329
51436
|
continue;
|
|
@@ -51403,7 +51510,7 @@ function resolveMap(parsed, data) {
|
|
|
51403
51510
|
const hint = suggest(name, cityNames);
|
|
51404
51511
|
err(
|
|
51405
51512
|
line11,
|
|
51406
|
-
`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>\`.`,
|
|
51407
51514
|
"E_MAP_UNKNOWN_PLACE"
|
|
51408
51515
|
);
|
|
51409
51516
|
return { kind: "miss" };
|
|
@@ -51945,6 +52052,21 @@ function mapLegendBand(legend, opts) {
|
|
|
51945
52052
|
if (height <= 0) return 0;
|
|
51946
52053
|
return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
|
|
51947
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
|
+
}
|
|
51948
52070
|
var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
|
|
51949
52071
|
var init_legend_band = __esm({
|
|
51950
52072
|
"src/map/legend-band.ts"() {
|
|
@@ -52069,6 +52191,8 @@ function placeContextLabels(args) {
|
|
|
52069
52191
|
waterBodies,
|
|
52070
52192
|
countries,
|
|
52071
52193
|
palette,
|
|
52194
|
+
countryTone,
|
|
52195
|
+
waterTone,
|
|
52072
52196
|
project,
|
|
52073
52197
|
collides,
|
|
52074
52198
|
contentPoints,
|
|
@@ -52078,8 +52202,8 @@ function placeContextLabels(args) {
|
|
|
52078
52202
|
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
52079
52203
|
const budget = labelBudget(width, height, band);
|
|
52080
52204
|
if (budget <= 0) return [];
|
|
52081
|
-
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
52082
|
-
const countryColor = palette.textMuted;
|
|
52205
|
+
const waterColor = waterTone ?? mix(palette.colors.blue, palette.textMuted, 50);
|
|
52206
|
+
const countryColor = countryTone ?? palette.textMuted;
|
|
52083
52207
|
const haloColor = palette.bg;
|
|
52084
52208
|
const candidates = [];
|
|
52085
52209
|
const center = [width / 2, height / 2];
|
|
@@ -52168,8 +52292,7 @@ function placeContextLabels(args) {
|
|
|
52168
52292
|
)
|
|
52169
52293
|
);
|
|
52170
52294
|
const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
|
|
52171
|
-
const
|
|
52172
|
-
const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
|
|
52295
|
+
const color = countryColor;
|
|
52173
52296
|
const text = c.name;
|
|
52174
52297
|
const tw = labelWidth(text, 0, fontSize);
|
|
52175
52298
|
if (tw > w || fontSize + 2 * PADY > h) continue;
|
|
@@ -52263,7 +52386,7 @@ function placeContextLabels(args) {
|
|
|
52263
52386
|
}
|
|
52264
52387
|
return placed;
|
|
52265
52388
|
}
|
|
52266
|
-
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;
|
|
52267
52390
|
var init_context_labels = __esm({
|
|
52268
52391
|
"src/map/context-labels.ts"() {
|
|
52269
52392
|
"use strict";
|
|
@@ -52280,7 +52403,6 @@ var init_context_labels = __esm({
|
|
|
52280
52403
|
COUNTRY_FONT_MAX = 22;
|
|
52281
52404
|
COUNTRY_SIZE_FRAC_MIN = 0.06;
|
|
52282
52405
|
COUNTRY_SIZE_FRAC_MAX = 0.32;
|
|
52283
|
-
COUNTRY_FADE_MAX = 45;
|
|
52284
52406
|
MAX_COUNTRY_POSITIONS = 4;
|
|
52285
52407
|
COUNTRY_POS_GRID = 5;
|
|
52286
52408
|
COUNTRY_POS_TOPN_MARGIN = 3;
|
|
@@ -52418,36 +52540,23 @@ function countryLabelPositions(args) {
|
|
|
52418
52540
|
return n;
|
|
52419
52541
|
};
|
|
52420
52542
|
const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
|
|
52421
|
-
const
|
|
52422
|
-
|
|
52423
|
-
|
|
52424
|
-
|
|
52425
|
-
|
|
52426
|
-
|
|
52427
|
-
|
|
52428
|
-
let bestIdx = 0;
|
|
52429
|
-
let bestMin = -1;
|
|
52430
|
-
for (let k = 0; k < pool.length; k++) {
|
|
52431
|
-
const c = pool[k];
|
|
52432
|
-
let minD = Infinity;
|
|
52433
|
-
for (const o of ordered) {
|
|
52434
|
-
const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
|
|
52435
|
-
if (d < minD) minD = d;
|
|
52436
|
-
}
|
|
52437
|
-
if (minD > bestMin) {
|
|
52438
|
-
bestMin = minD;
|
|
52439
|
-
bestIdx = k;
|
|
52440
|
-
}
|
|
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]]
|
|
52441
52550
|
}
|
|
52442
|
-
|
|
52443
|
-
|
|
52444
|
-
|
|
52551
|
+
] : [];
|
|
52552
|
+
const grid = [...kept].sort(
|
|
52553
|
+
(a, b) => interiorness(b) - interiorness(a) || dist2ToCentre(a) - dist2ToCentre(b)
|
|
52554
|
+
).map((c) => ({
|
|
52445
52555
|
lonLat: [c.lon, c.lat],
|
|
52446
52556
|
screen: [c.sx, c.sy]
|
|
52447
52557
|
}));
|
|
52448
52558
|
const curatedPos = curated ? mkCurated(curated, project) : [];
|
|
52449
|
-
|
|
52450
|
-
return out;
|
|
52559
|
+
return [...curatedPos, ...lead, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
|
|
52451
52560
|
}
|
|
52452
52561
|
function mkCurated(curated, project) {
|
|
52453
52562
|
if (!curated) return [];
|
|
@@ -52713,6 +52822,25 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52713
52822
|
palette.bg,
|
|
52714
52823
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
52715
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
|
+
);
|
|
52716
52844
|
const hasDirectColor = resolved.regions.some((r) => r.color !== void 0);
|
|
52717
52845
|
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && !hasDirectColor && fillGroupNames.size === 0;
|
|
52718
52846
|
const colorByIso = /* @__PURE__ */ new Map();
|
|
@@ -52827,6 +52955,33 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52827
52955
|
hasSubtitle: Boolean(resolved.subtitle)
|
|
52828
52956
|
});
|
|
52829
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
|
+
});
|
|
52830
52985
|
const reserve = opts._calloutReserve;
|
|
52831
52986
|
const fitLeft = FIT_PAD + (reserve?.left ?? 0);
|
|
52832
52987
|
const fitRight = width - FIT_PAD - (reserve?.right ?? 0);
|
|
@@ -53556,6 +53711,19 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53556
53711
|
const by = b.cy - (b.cy - py) / tb * trimB;
|
|
53557
53712
|
return `M${ax},${ay}Q${px},${py} ${bx},${by}`;
|
|
53558
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
|
+
};
|
|
53559
53727
|
const routeLegVals = resolved.routes.flatMap((rt) => rt.legs).map((l) => Number(l.value)).filter((n) => Number.isFinite(n) && n > 0);
|
|
53560
53728
|
const rlMin = routeLegVals.length ? Math.min(...routeLegVals) : 0;
|
|
53561
53729
|
const rlMax = routeLegVals.length ? Math.max(...routeLegVals) : 0;
|
|
@@ -53569,13 +53737,12 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53569
53737
|
const a = poiScreen.get(leg.fromId);
|
|
53570
53738
|
const b = poiScreen.get(leg.toId);
|
|
53571
53739
|
if (!a || !b) continue;
|
|
53572
|
-
const
|
|
53573
|
-
const my = (a.cy + b.cy) / 2;
|
|
53740
|
+
const lp = legLabelPoint(a, b, leg.style === "arc", 0);
|
|
53574
53741
|
const bow = {
|
|
53575
53742
|
curved: leg.style === "arc",
|
|
53576
53743
|
offset: 0,
|
|
53577
|
-
labelX:
|
|
53578
|
-
labelY:
|
|
53744
|
+
labelX: lp.x,
|
|
53745
|
+
labelY: lp.y
|
|
53579
53746
|
};
|
|
53580
53747
|
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53581
53748
|
legs.push({
|
|
@@ -53622,13 +53789,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53622
53789
|
const b = poiScreen.get(e.toId);
|
|
53623
53790
|
if (!a || !b) return;
|
|
53624
53791
|
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
53625
|
-
const
|
|
53626
|
-
const
|
|
53792
|
+
const curved = e.style === "arc" || n > 1;
|
|
53793
|
+
const lp = legLabelPoint(a, b, curved, fanOffset);
|
|
53627
53794
|
const bow = {
|
|
53628
|
-
curved
|
|
53795
|
+
curved,
|
|
53629
53796
|
offset: fanOffset,
|
|
53630
|
-
labelX:
|
|
53631
|
-
labelY:
|
|
53797
|
+
labelX: lp.x,
|
|
53798
|
+
labelY: lp.y
|
|
53632
53799
|
};
|
|
53633
53800
|
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53634
53801
|
legs.push({
|
|
@@ -53652,7 +53819,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53652
53819
|
});
|
|
53653
53820
|
}
|
|
53654
53821
|
const labels = [];
|
|
53655
|
-
const obstacles = [];
|
|
53822
|
+
const obstacles = [...topReserved];
|
|
53656
53823
|
const regionLabelGuards = [];
|
|
53657
53824
|
const markers = pois.map((p) => ({
|
|
53658
53825
|
cx: p.cx,
|
|
@@ -53690,6 +53857,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53690
53857
|
}
|
|
53691
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));
|
|
53692
53859
|
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
53860
|
+
const labeledRegionIds = /* @__PURE__ */ new Set();
|
|
53693
53861
|
const showRegionValues = resolved.directives.noRegionValue !== true && activeIsScore;
|
|
53694
53862
|
const regionValueStr = (value) => showRegionValues && value !== void 0 ? compactNumber(value) : void 0;
|
|
53695
53863
|
const isCompact = width < COMPACT_WIDTH_PX;
|
|
@@ -53710,7 +53878,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53710
53878
|
const REGION_FONT_MAX_ORIENT = 22;
|
|
53711
53879
|
const REGION_SIZE_FRAC_MIN = 0.06;
|
|
53712
53880
|
const REGION_SIZE_FRAC_MAX = 0.32;
|
|
53713
|
-
const
|
|
53881
|
+
const SUBJECT_FONT_MAX = 18;
|
|
53882
|
+
const SUBJECT_MIN_PROMINENCE = 13;
|
|
53883
|
+
const SUBJECT_LEADER_FONT = 15;
|
|
53714
53884
|
const canvasLinear = Math.sqrt(Math.max(1, width * height));
|
|
53715
53885
|
const sizeT = (boxW, boxH) => {
|
|
53716
53886
|
const frac = Math.sqrt(Math.max(0, boxW * boxH)) / canvasLinear;
|
|
@@ -53722,9 +53892,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53722
53892
|
)
|
|
53723
53893
|
);
|
|
53724
53894
|
};
|
|
53725
|
-
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2,
|
|
53895
|
+
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2, colorOverride) => {
|
|
53726
53896
|
const { color: baseColor, haloColor } = labelOnFill(fill2);
|
|
53727
|
-
const color =
|
|
53897
|
+
const color = colorOverride ?? baseColor;
|
|
53728
53898
|
const vf = Math.round(fontSize * 0.82);
|
|
53729
53899
|
const halfW = Math.max(
|
|
53730
53900
|
measureLegendText(text, fontSize),
|
|
@@ -53739,7 +53909,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53739
53909
|
text,
|
|
53740
53910
|
anchor: "middle",
|
|
53741
53911
|
color,
|
|
53742
|
-
|
|
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,
|
|
53743
53917
|
haloColor,
|
|
53744
53918
|
...fontSize !== FONT2 && { fontSize },
|
|
53745
53919
|
...valueLine !== void 0 && { valueLine },
|
|
@@ -53775,7 +53949,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53775
53949
|
if (!c || !Number.isFinite(c[0])) return null;
|
|
53776
53950
|
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
53777
53951
|
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
53778
|
-
const placedRegionRects = [];
|
|
53952
|
+
const placedRegionRects = [...topReserved];
|
|
53779
53953
|
const placedLeaders = [];
|
|
53780
53954
|
const isEmptyFill = (f) => f === water || f === neutralFill || f === foreignFill;
|
|
53781
53955
|
const POI_LABEL_PAD = 14;
|
|
@@ -53788,9 +53962,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53788
53962
|
const SHORT_HOP_MAX = Math.max(46, Math.min(width, height) * 0.11);
|
|
53789
53963
|
const STEP = 4;
|
|
53790
53964
|
const HOP_GAP = 9;
|
|
53791
|
-
const tryShortHopCallout = (cx, cy, fill2, name, value) => {
|
|
53792
|
-
const chipW = stackW(name, value);
|
|
53793
|
-
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);
|
|
53794
53968
|
const dirs = [
|
|
53795
53969
|
[0, -1],
|
|
53796
53970
|
// north (over Canada)
|
|
@@ -53806,7 +53980,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53806
53980
|
let ex = cx;
|
|
53807
53981
|
let ey = cy;
|
|
53808
53982
|
let steps = 0;
|
|
53809
|
-
const maxSteps = Math.ceil(
|
|
53983
|
+
const maxSteps = Math.ceil(maxLen / STEP) + 1;
|
|
53810
53984
|
while (steps < maxSteps && fillAt(ex, ey) === fill2) {
|
|
53811
53985
|
ex += dx * STEP;
|
|
53812
53986
|
ey += dy * STEP;
|
|
@@ -53814,8 +53988,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53814
53988
|
}
|
|
53815
53989
|
if (fillAt(ex, ey) === fill2) continue;
|
|
53816
53990
|
const halfAlong = (Math.abs(dx) * chipW + Math.abs(dy) * chipH) / 2;
|
|
53817
|
-
const ccx = ex + dx * (
|
|
53818
|
-
const ccy = ey + dy * (
|
|
53991
|
+
const ccx = ex + dx * (gap + halfAlong);
|
|
53992
|
+
const ccy = ey + dy * (gap + halfAlong);
|
|
53819
53993
|
const rect = {
|
|
53820
53994
|
x: ccx - chipW / 2,
|
|
53821
53995
|
y: ccy - chipH / 2,
|
|
@@ -53840,7 +54014,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53840
54014
|
const innerX = ccx - dx * (chipW / 2);
|
|
53841
54015
|
const innerY = ccy - dy * (chipH / 2);
|
|
53842
54016
|
const len = Math.hypot(innerX - cx, innerY - cy);
|
|
53843
|
-
if (len >
|
|
54017
|
+
if (len > maxLen) continue;
|
|
53844
54018
|
if (placedLeaders.some(
|
|
53845
54019
|
(l) => segmentsCross(cx, cy, innerX, innerY, l[0], l[1], l[2], l[3])
|
|
53846
54020
|
))
|
|
@@ -53864,6 +54038,78 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53864
54038
|
const valStr = regionValueStr(r.value);
|
|
53865
54039
|
const fitsRegions = (rect) => !placedRegionRects.some((p) => rectsOverlap(rect, p));
|
|
53866
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
|
+
}
|
|
53867
54113
|
const seekAnchors = [
|
|
53868
54114
|
{ x: c[0], y: c[1], guard: false }
|
|
53869
54115
|
];
|
|
@@ -53886,13 +54132,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53886
54132
|
const nameRect = regionLabelRect(a.x, a.y, t);
|
|
53887
54133
|
if (valStr && stackW(t, valStr) <= boxW && stackH(true) <= boxH) {
|
|
53888
54134
|
const stackRect = regionLabelRect(a.x, a.y, t, valStr);
|
|
53889
|
-
if (fitsRegions(stackRect) && fitsPois(nameRect)) {
|
|
54135
|
+
if (fitsRegions(stackRect) && fitsPois(nameRect) && extremesOnFill(a.x, a.y, t)) {
|
|
53890
54136
|
chosen = { text: t, valueLine: valStr, ax: a.x, ay: a.y };
|
|
53891
54137
|
break;
|
|
53892
54138
|
}
|
|
53893
54139
|
}
|
|
53894
54140
|
if (labelW(t) <= boxW && labelH <= boxH) {
|
|
53895
|
-
if (fitsRegions(nameRect) && fitsPois(nameRect)) {
|
|
54141
|
+
if (fitsRegions(nameRect) && fitsPois(nameRect) && (valStr === void 0 || extremesOnFill(a.x, a.y, t))) {
|
|
53896
54142
|
chosen = { text: t, ax: a.x, ay: a.y };
|
|
53897
54143
|
break;
|
|
53898
54144
|
}
|
|
@@ -53900,7 +54146,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53900
54146
|
}
|
|
53901
54147
|
if (chosen) break;
|
|
53902
54148
|
}
|
|
53903
|
-
if (chosen === void 0 &&
|
|
54149
|
+
if (chosen === void 0 && r.label !== void 0 && isSubject) {
|
|
53904
54150
|
const hop = tryShortHopCallout(c[0], c[1], r.fill, r.label, valStr);
|
|
53905
54151
|
if (hop) {
|
|
53906
54152
|
placedRegionRects.push(hop.rect);
|
|
@@ -53912,9 +54158,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53912
54158
|
text: r.label,
|
|
53913
54159
|
anchor: "middle",
|
|
53914
54160
|
color: palette.text,
|
|
53915
|
-
halo:
|
|
54161
|
+
halo: false,
|
|
53916
54162
|
haloColor: palette.bg,
|
|
53917
|
-
valueLine: valStr,
|
|
54163
|
+
...valStr !== void 0 && { valueLine: valStr },
|
|
53918
54164
|
leader: {
|
|
53919
54165
|
x1: hop.leader[0],
|
|
53920
54166
|
y1: hop.leader[1],
|
|
@@ -53925,20 +54171,28 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53925
54171
|
calloutDot: { x: c[0], y: c[1], color: dark },
|
|
53926
54172
|
lineNumber: r.lineNumber
|
|
53927
54173
|
});
|
|
54174
|
+
labeledRegionIds.add(r.id);
|
|
53928
54175
|
}
|
|
53929
54176
|
continue;
|
|
53930
54177
|
}
|
|
53931
54178
|
if (chosen === void 0) continue;
|
|
53932
54179
|
const isOrient = r.value === void 0 && r.layer === "base";
|
|
53933
54180
|
let font = FONT2;
|
|
53934
|
-
let fade = 0;
|
|
53935
54181
|
if (isOrient) {
|
|
53936
54182
|
const growT = sizeT(boxW, boxH);
|
|
53937
54183
|
const desiredFont = Math.round(
|
|
53938
54184
|
FONT2 + growT * (REGION_FONT_MAX_ORIENT - FONT2)
|
|
53939
54185
|
);
|
|
53940
54186
|
const hasVal = chosen.valueLine !== void 0;
|
|
53941
|
-
|
|
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--) {
|
|
53942
54196
|
if (stackW(chosen.text, chosen.valueLine, f) > boxW || stackH(hasVal, f) > boxH)
|
|
53943
54197
|
continue;
|
|
53944
54198
|
const gRect = regionLabelRect(
|
|
@@ -53955,14 +54209,18 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53955
54209
|
void 0,
|
|
53956
54210
|
f
|
|
53957
54211
|
);
|
|
53958
|
-
if (!fitsRegions(gRect) || !fitsPois(gName))
|
|
53959
|
-
const halfW = measureLegendText(chosen.text, f) / 2;
|
|
53960
|
-
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))
|
|
53961
54213
|
continue;
|
|
53962
|
-
|
|
54214
|
+
if (!extremesClean(f)) continue;
|
|
54215
|
+
fit = f;
|
|
53963
54216
|
break;
|
|
53964
54217
|
}
|
|
53965
|
-
|
|
54218
|
+
if (fit < 0) {
|
|
54219
|
+
if (!frameContainers.has(r.id)) continue;
|
|
54220
|
+
font = FONT2;
|
|
54221
|
+
} else {
|
|
54222
|
+
font = fit;
|
|
54223
|
+
}
|
|
53966
54224
|
}
|
|
53967
54225
|
const rRect = regionLabelRect(
|
|
53968
54226
|
chosen.ax,
|
|
@@ -53980,8 +54238,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53980
54238
|
r.lineNumber,
|
|
53981
54239
|
chosen.valueLine,
|
|
53982
54240
|
font,
|
|
53983
|
-
|
|
54241
|
+
isOrient ? backdropLandTone : void 0
|
|
53984
54242
|
);
|
|
54243
|
+
labeledRegionIds.add(r.id);
|
|
53985
54244
|
regionLabelGuards.push({
|
|
53986
54245
|
label: labels[labels.length - 1],
|
|
53987
54246
|
rect: rRect
|
|
@@ -53999,6 +54258,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53999
54258
|
seed.lineNumber,
|
|
54000
54259
|
valStr
|
|
54001
54260
|
);
|
|
54261
|
+
labeledRegionIds.add(seed.iso);
|
|
54002
54262
|
regionLabelGuards.push({
|
|
54003
54263
|
label: labels[labels.length - 1],
|
|
54004
54264
|
rect: regionLabelRect(seed.x, seed.y, text, valStr)
|
|
@@ -54430,13 +54690,30 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54430
54690
|
const nx = -dy / len;
|
|
54431
54691
|
const ny = dx / len;
|
|
54432
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
|
+
};
|
|
54433
54711
|
const candidates = [];
|
|
54434
54712
|
for (const off of [0, perp, -perp, 2 * perp, -2 * perp])
|
|
54435
|
-
for (const t of T_LIST)
|
|
54436
|
-
|
|
54437
|
-
|
|
54438
|
-
|
|
54439
|
-
]);
|
|
54713
|
+
for (const t of T_LIST) {
|
|
54714
|
+
const [bx, by] = pointAt(t);
|
|
54715
|
+
candidates.push([bx + nx * off, by + ny * off - 4]);
|
|
54716
|
+
}
|
|
54440
54717
|
const clean = ([cx2, cy2]) => {
|
|
54441
54718
|
const rect2 = { x: cx2 - w / 2, y: cy2 - labelH / 2, w, h: labelH };
|
|
54442
54719
|
if (rect2.x < 0 || rect2.x + w > width || rect2.y < 0 || rect2.y + rect2.h > height)
|
|
@@ -54446,10 +54723,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54446
54723
|
if (placedEdge.some((o) => rectsOverlap(o, rect2))) return false;
|
|
54447
54724
|
return true;
|
|
54448
54725
|
};
|
|
54449
|
-
const
|
|
54450
|
-
|
|
54451
|
-
a.cy + dy * 0.5 - 4
|
|
54452
|
-
];
|
|
54726
|
+
const mid = pointAt(0.5);
|
|
54727
|
+
const [cx, cy] = candidates.find(clean) ?? [mid[0], mid[1] - 4];
|
|
54453
54728
|
const style = labelOnFill(fillAt(cx, cy));
|
|
54454
54729
|
legs[i] = {
|
|
54455
54730
|
...lg,
|
|
@@ -54477,7 +54752,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54477
54752
|
const rawCountries = [];
|
|
54478
54753
|
for (const f of worldLayer.values()) {
|
|
54479
54754
|
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
54480
|
-
if (!iso || regionById.has(iso)) continue;
|
|
54755
|
+
if (!iso || regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54481
54756
|
let hasReferencedSub = false;
|
|
54482
54757
|
for (const k of regionById.keys())
|
|
54483
54758
|
if (k.startsWith(iso + "-")) {
|
|
@@ -54551,7 +54826,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54551
54826
|
);
|
|
54552
54827
|
if (usLayer && framedStateContainers) {
|
|
54553
54828
|
for (const [iso, f] of usLayer) {
|
|
54554
|
-
if (regionById.has(iso)) continue;
|
|
54829
|
+
if (regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54555
54830
|
const viewF = cullFeatureToView(f);
|
|
54556
54831
|
if (!viewF) continue;
|
|
54557
54832
|
const b = path.bounds(viewF);
|
|
@@ -54575,6 +54850,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54575
54850
|
waterBodies: data.waterBodies,
|
|
54576
54851
|
countries: countryCandidates,
|
|
54577
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,
|
|
54578
54858
|
project,
|
|
54579
54859
|
collides,
|
|
54580
54860
|
contentPoints,
|
|
@@ -55128,19 +55408,60 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
55128
55408
|
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
55129
55409
|
};
|
|
55130
55410
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
55131
|
-
|
|
55132
|
-
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);
|
|
55133
55457
|
if (leg.tags) {
|
|
55134
55458
|
for (const [group, value] of Object.entries(leg.tags)) {
|
|
55135
55459
|
p.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
55136
55460
|
}
|
|
55137
55461
|
}
|
|
55138
55462
|
wireSync(p, leg.lineNumber);
|
|
55139
|
-
if (
|
|
55140
|
-
|
|
55141
|
-
const s = arrowSize(leg.width);
|
|
55142
|
-
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);
|
|
55143
|
-
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);
|
|
55144
55465
|
}
|
|
55145
55466
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
55146
55467
|
const lt = emitText(
|
|
@@ -58767,9 +59088,9 @@ var init_renderer20 = __esm({
|
|
|
58767
59088
|
}
|
|
58768
59089
|
});
|
|
58769
59090
|
|
|
58770
|
-
// 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
|
|
58771
59092
|
var require_lz_string = __commonJS({
|
|
58772
|
-
"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) {
|
|
58773
59094
|
"use strict";
|
|
58774
59095
|
var LZString = (function() {
|
|
58775
59096
|
var f = String.fromCharCode;
|