@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.cjs
CHANGED
|
@@ -37,6 +37,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
37
37
|
function makeDgmoError(line11, message, severity = "error", code) {
|
|
38
38
|
return code !== void 0 ? { line: line11, message, severity, code } : { line: line11, message, severity };
|
|
39
39
|
}
|
|
40
|
+
function dedupeDiagnostics(diagnostics) {
|
|
41
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const d of diagnostics) {
|
|
44
|
+
const key = `${d.line}\0${d.column ?? ""}\0${d.severity}\0${d.code ?? ""}\0${d.message}`;
|
|
45
|
+
if (seen.has(key)) continue;
|
|
46
|
+
seen.add(key);
|
|
47
|
+
out.push(d);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
40
51
|
function formatDgmoError(err) {
|
|
41
52
|
return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
|
|
42
53
|
}
|
|
@@ -94,7 +105,7 @@ function vennAliasKeywordRemovedMessage(args) {
|
|
|
94
105
|
return `Venn 'alias' keyword was removed. Use 'as' instead \u2014 '${args.name} as ${args.alias}'.`;
|
|
95
106
|
}
|
|
96
107
|
function pipeOperatorRemovedMessage() {
|
|
97
|
-
return `'|' removed \u2014 'Node | c: red' \u2192 'Node c: red'
|
|
108
|
+
return `'|' removed \u2014 'Node | c: red' \u2192 'Node c: red'`;
|
|
98
109
|
}
|
|
99
110
|
function ganttBarePercentRemovedMessage(percent) {
|
|
100
111
|
return `Bare-percent progress shorthand '| ${percent}' was removed. Use 'progress: ${percent.replace(/%$/, "")}' (integer 0\u2013100, no '%').`;
|
|
@@ -1150,7 +1161,7 @@ var init_parsing = __esm({
|
|
|
1150
1161
|
"no-title",
|
|
1151
1162
|
"no-notes"
|
|
1152
1163
|
]);
|
|
1153
|
-
MULTIPLE_PIPE_ERROR = '
|
|
1164
|
+
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.";
|
|
1154
1165
|
RESERVED_KEY_RE = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
1155
1166
|
AS_ALIAS_RE = /\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11})$/;
|
|
1156
1167
|
POTENTIAL_META_RE = /\b([\w-]+)\s*:/;
|
|
@@ -1331,12 +1342,10 @@ function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
|
|
|
1331
1342
|
);
|
|
1332
1343
|
if (!isPrefix) {
|
|
1333
1344
|
const defined = group.entries.map((e) => e.value);
|
|
1334
|
-
let msg = `Unknown value '${value}' for tag group '${group.name}'`;
|
|
1345
|
+
let msg = `Unknown value '${value}' for tag group '${group.name}' \u2014 use one of: ${defined.join(", ")}`;
|
|
1335
1346
|
const hint = suggestFn?.(value, defined);
|
|
1336
1347
|
if (hint) {
|
|
1337
1348
|
msg += `. ${hint}`;
|
|
1338
|
-
} else {
|
|
1339
|
-
msg += ` \u2014 defined values: ${defined.join(", ")}`;
|
|
1340
1349
|
}
|
|
1341
1350
|
pushWarning(entity.lineNumber, msg);
|
|
1342
1351
|
}
|
|
@@ -2112,7 +2121,10 @@ function parseVisualizationFull(content, palette) {
|
|
|
2112
2121
|
lineNumber
|
|
2113
2122
|
});
|
|
2114
2123
|
} else {
|
|
2115
|
-
warn(
|
|
2124
|
+
warn(
|
|
2125
|
+
lineNumber,
|
|
2126
|
+
`Unrecognized era entry: "${line11}" \u2014 use '<start> -> <end> Label [color]' (e.g. '1716 -> 1717 Reign').`
|
|
2127
|
+
);
|
|
2116
2128
|
}
|
|
2117
2129
|
continue;
|
|
2118
2130
|
}
|
|
@@ -2140,7 +2152,10 @@ function parseVisualizationFull(content, palette) {
|
|
|
2140
2152
|
lineNumber
|
|
2141
2153
|
});
|
|
2142
2154
|
} else {
|
|
2143
|
-
warn(
|
|
2155
|
+
warn(
|
|
2156
|
+
lineNumber,
|
|
2157
|
+
`Unrecognized marker entry: "${line11}" \u2014 use '<date> Label [color]' (e.g. '1776 Independence').`
|
|
2158
|
+
);
|
|
2144
2159
|
}
|
|
2145
2160
|
continue;
|
|
2146
2161
|
}
|
|
@@ -2818,7 +2833,7 @@ function parseVisualizationFull(content, palette) {
|
|
|
2818
2833
|
result.diagnostics.push(
|
|
2819
2834
|
makeDgmoError(
|
|
2820
2835
|
ov.lineNumber,
|
|
2821
|
-
`Intersection references unknown set or alias "${ref}"`
|
|
2836
|
+
`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)`
|
|
2822
2837
|
)
|
|
2823
2838
|
);
|
|
2824
2839
|
if (!result.error)
|
|
@@ -6262,10 +6277,10 @@ function parseSequenceDgmo(content, palette) {
|
|
|
6262
6277
|
const rawInside = fallbackMatch[1];
|
|
6263
6278
|
const pipeIdx = rawInside.indexOf("|");
|
|
6264
6279
|
const cleanName = rawInside.substring(0, pipeIdx).trim().replace(/\([^)]*\)$/, "").trim();
|
|
6265
|
-
const metaPart = rawInside.substring(pipeIdx).trim();
|
|
6280
|
+
const metaPart = rawInside.substring(pipeIdx).replace(/^\|\s*/, "").trim();
|
|
6266
6281
|
pushError(
|
|
6267
6282
|
lineNumber,
|
|
6268
|
-
`
|
|
6283
|
+
`Metadata goes after the bracket with no pipe \u2014 use '[${cleanName}] ${metaPart}' (e.g. '[Backend] t: Product'), not '[${rawInside.trim()}]'`
|
|
6269
6284
|
);
|
|
6270
6285
|
continue;
|
|
6271
6286
|
}
|
|
@@ -6875,7 +6890,7 @@ function parseSequenceDgmo(content, palette) {
|
|
|
6875
6890
|
}
|
|
6876
6891
|
pushWarning(
|
|
6877
6892
|
lineNumber,
|
|
6878
|
-
`Unexpected line: '${trimmed}'. Expected a message (A -> B), participant, section (== Name ==), or block
|
|
6893
|
+
`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>').`
|
|
6879
6894
|
);
|
|
6880
6895
|
}
|
|
6881
6896
|
if (!hasExplicitChart && result.messages.length === 0) {
|
|
@@ -8037,7 +8052,11 @@ function parseFlowchart(content, palette) {
|
|
|
8037
8052
|
const value = optMatch[2].trim();
|
|
8038
8053
|
if (key === "no-color") {
|
|
8039
8054
|
result.diagnostics.push(
|
|
8040
|
-
makeDgmoError(
|
|
8055
|
+
makeDgmoError(
|
|
8056
|
+
lineNumber,
|
|
8057
|
+
'"no-color" was removed \u2014 node colors are assigned automatically by shape; delete this line.',
|
|
8058
|
+
"warning"
|
|
8059
|
+
)
|
|
8041
8060
|
);
|
|
8042
8061
|
continue;
|
|
8043
8062
|
}
|
|
@@ -8366,7 +8385,11 @@ function parseState(content, palette) {
|
|
|
8366
8385
|
const value = optMatch[2].trim();
|
|
8367
8386
|
if (key === "no-color") {
|
|
8368
8387
|
result.diagnostics.push(
|
|
8369
|
-
makeDgmoError(
|
|
8388
|
+
makeDgmoError(
|
|
8389
|
+
lineNumber,
|
|
8390
|
+
'"no-color" was removed \u2014 state colors are assigned automatically; delete this line.',
|
|
8391
|
+
"warning"
|
|
8392
|
+
)
|
|
8370
8393
|
);
|
|
8371
8394
|
continue;
|
|
8372
8395
|
}
|
|
@@ -8681,7 +8704,7 @@ function parseClassDiagram(content, palette) {
|
|
|
8681
8704
|
result.diagnostics.push(
|
|
8682
8705
|
makeDgmoError(
|
|
8683
8706
|
lineNumber,
|
|
8684
|
-
'"no-auto-color"
|
|
8707
|
+
'"no-auto-color" was removed \u2014 colors are always auto-assigned from the palette; delete this line.',
|
|
8685
8708
|
"warning"
|
|
8686
8709
|
)
|
|
8687
8710
|
);
|
|
@@ -8796,7 +8819,11 @@ function parseClassDiagram(content, palette) {
|
|
|
8796
8819
|
continue;
|
|
8797
8820
|
}
|
|
8798
8821
|
result.diagnostics.push(
|
|
8799
|
-
makeDgmoError(
|
|
8822
|
+
makeDgmoError(
|
|
8823
|
+
lineNumber,
|
|
8824
|
+
`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)`,
|
|
8825
|
+
"warning"
|
|
8826
|
+
)
|
|
8800
8827
|
);
|
|
8801
8828
|
}
|
|
8802
8829
|
if (notes.length > 0) {
|
|
@@ -9274,7 +9301,11 @@ function parseERDiagram(content, palette) {
|
|
|
9274
9301
|
continue;
|
|
9275
9302
|
}
|
|
9276
9303
|
result.diagnostics.push(
|
|
9277
|
-
makeDgmoError(
|
|
9304
|
+
makeDgmoError(
|
|
9305
|
+
lineNumber,
|
|
9306
|
+
`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)`,
|
|
9307
|
+
"warning"
|
|
9308
|
+
)
|
|
9278
9309
|
);
|
|
9279
9310
|
}
|
|
9280
9311
|
if (result.tables.length === 0 && !result.error) {
|
|
@@ -9618,7 +9649,10 @@ function parseOrg(content, palette) {
|
|
|
9618
9649
|
const value = metadataMatch[2].trim();
|
|
9619
9650
|
const parent = findMetadataParent(indent, indentStack);
|
|
9620
9651
|
if (!parent) {
|
|
9621
|
-
pushError(
|
|
9652
|
+
pushError(
|
|
9653
|
+
lineNumber,
|
|
9654
|
+
"Metadata 'key: value' must be indented under a node \u2014 no node precedes it."
|
|
9655
|
+
);
|
|
9622
9656
|
} else {
|
|
9623
9657
|
parent.metadata = { ...parent.metadata, [key]: value };
|
|
9624
9658
|
}
|
|
@@ -9636,7 +9670,10 @@ function parseOrg(content, palette) {
|
|
|
9636
9670
|
);
|
|
9637
9671
|
attachNode(node, indent, indentStack, result);
|
|
9638
9672
|
} else {
|
|
9639
|
-
pushError(
|
|
9673
|
+
pushError(
|
|
9674
|
+
lineNumber,
|
|
9675
|
+
"Metadata 'key: value' must be indented under a node \u2014 no node precedes it."
|
|
9676
|
+
);
|
|
9640
9677
|
}
|
|
9641
9678
|
} else {
|
|
9642
9679
|
const node = parseNodeLabel(
|
|
@@ -9985,7 +10022,10 @@ function parseSitemap(content, palette) {
|
|
|
9985
10022
|
if (arrowInfo) {
|
|
9986
10023
|
const source = findParentNode(indent, indentStack);
|
|
9987
10024
|
if (!source) {
|
|
9988
|
-
pushError(
|
|
10025
|
+
pushError(
|
|
10026
|
+
lineNumber,
|
|
10027
|
+
"Arrow '-> target' must be indented under its source page \u2014 no page precedes it."
|
|
10028
|
+
);
|
|
9989
10029
|
} else {
|
|
9990
10030
|
deferredArrows.push({
|
|
9991
10031
|
sourceNode: source,
|
|
@@ -10055,7 +10095,10 @@ function parseSitemap(content, palette) {
|
|
|
10055
10095
|
const value = metadataMatch[2].trim();
|
|
10056
10096
|
const parent = findParentNode(indent, indentStack);
|
|
10057
10097
|
if (!parent) {
|
|
10058
|
-
pushError(
|
|
10098
|
+
pushError(
|
|
10099
|
+
lineNumber,
|
|
10100
|
+
"Metadata 'key: value' must be indented under a page \u2014 no page precedes it."
|
|
10101
|
+
);
|
|
10059
10102
|
} else {
|
|
10060
10103
|
parent.metadata = { ...parent.metadata, [key]: value };
|
|
10061
10104
|
}
|
|
@@ -10075,7 +10118,10 @@ function parseSitemap(content, palette) {
|
|
|
10075
10118
|
const key = normalizeName(node.label);
|
|
10076
10119
|
labelToNode.set(key, node);
|
|
10077
10120
|
} else {
|
|
10078
|
-
pushError(
|
|
10121
|
+
pushError(
|
|
10122
|
+
lineNumber,
|
|
10123
|
+
"Metadata 'key: value' must be indented under a page \u2014 no page precedes it."
|
|
10124
|
+
);
|
|
10079
10125
|
}
|
|
10080
10126
|
} else {
|
|
10081
10127
|
const descResult = tryStripDescriptionKeyword(trimmed);
|
|
@@ -11893,7 +11939,7 @@ function parseChart(content, palette) {
|
|
|
11893
11939
|
result.diagnostics.push(
|
|
11894
11940
|
makeDgmoError(
|
|
11895
11941
|
lineNumber,
|
|
11896
|
-
`'${trimmed}' \u2014 ##
|
|
11942
|
+
`'${trimmed}' removed \u2014 use a bracket container '[${trimmed.replace(/^#+\s*/, "")}]' instead (## headers are no longer supported).`
|
|
11897
11943
|
)
|
|
11898
11944
|
);
|
|
11899
11945
|
continue;
|
|
@@ -12850,7 +12896,11 @@ function parseExtendedChartFull(content, palette) {
|
|
|
12850
12896
|
continue;
|
|
12851
12897
|
}
|
|
12852
12898
|
result.diagnostics.push(
|
|
12853
|
-
makeDgmoError(
|
|
12899
|
+
makeDgmoError(
|
|
12900
|
+
lineNumber,
|
|
12901
|
+
`Unexpected line: '${trimmed}'. Expected a data row ('Label value', space-separated) or a known option.`,
|
|
12902
|
+
"warning"
|
|
12903
|
+
)
|
|
12854
12904
|
);
|
|
12855
12905
|
}
|
|
12856
12906
|
const warn = (line11, message) => {
|
|
@@ -15212,7 +15262,10 @@ function parseKanban(content, palette) {
|
|
|
15212
15262
|
continue;
|
|
15213
15263
|
}
|
|
15214
15264
|
if (!currentColumn) {
|
|
15215
|
-
warn(
|
|
15265
|
+
warn(
|
|
15266
|
+
lineNumber,
|
|
15267
|
+
"Card line found before any column. Declare a column first with '[Column Name]', then indent cards beneath it. (\xA711)"
|
|
15268
|
+
);
|
|
15216
15269
|
continue;
|
|
15217
15270
|
}
|
|
15218
15271
|
if (currentCard && indent > cardBaseIndent) {
|
|
@@ -15351,7 +15404,7 @@ var init_parser13 = __esm({
|
|
|
15351
15404
|
KNOWN_OPTIONS4 = /* @__PURE__ */ new Set(["hide", "active-tag"]);
|
|
15352
15405
|
KNOWN_BOOLEANS3 = /* @__PURE__ */ new Set(["solid-fill", "no-title"]);
|
|
15353
15406
|
REMOVED_BOOLEANS = {
|
|
15354
|
-
"no-auto-color": '"no-auto-color"
|
|
15407
|
+
"no-auto-color": '"no-auto-color" was removed \u2014 colors are always auto-assigned from the palette; delete this line.'
|
|
15355
15408
|
};
|
|
15356
15409
|
}
|
|
15357
15410
|
});
|
|
@@ -15588,7 +15641,7 @@ function parseC4(content, palette) {
|
|
|
15588
15641
|
} else {
|
|
15589
15642
|
pushError(
|
|
15590
15643
|
lineNumber,
|
|
15591
|
-
`"container ${refName}" must be
|
|
15644
|
+
`"container ${refName}" must be indented under a deployment node (e.g. under 'Prod is a deployment-node')`
|
|
15592
15645
|
);
|
|
15593
15646
|
}
|
|
15594
15647
|
continue;
|
|
@@ -15643,7 +15696,10 @@ function parseC4(content, palette) {
|
|
|
15643
15696
|
indent
|
|
15644
15697
|
});
|
|
15645
15698
|
} else {
|
|
15646
|
-
pushError(
|
|
15699
|
+
pushError(
|
|
15700
|
+
lineNumber,
|
|
15701
|
+
`"${sectionType}" must be indented under an element (e.g. under 'Web App is a container')`
|
|
15702
|
+
);
|
|
15647
15703
|
}
|
|
15648
15704
|
continue;
|
|
15649
15705
|
}
|
|
@@ -15670,7 +15726,10 @@ function parseC4(content, palette) {
|
|
|
15670
15726
|
indent
|
|
15671
15727
|
});
|
|
15672
15728
|
} else {
|
|
15673
|
-
pushError(
|
|
15729
|
+
pushError(
|
|
15730
|
+
lineNumber,
|
|
15731
|
+
`Group [${groupName}] must be indented under an element (e.g. under 'Web App is a container')`
|
|
15732
|
+
);
|
|
15674
15733
|
}
|
|
15675
15734
|
continue;
|
|
15676
15735
|
}
|
|
@@ -16000,7 +16059,10 @@ function parseC4(content, palette) {
|
|
|
16000
16059
|
}
|
|
16001
16060
|
desc.push(descResult.text);
|
|
16002
16061
|
} else {
|
|
16003
|
-
pushError(
|
|
16062
|
+
pushError(
|
|
16063
|
+
lineNumber,
|
|
16064
|
+
`Unexpected content: "${trimmed}". Expected an element ('Web App is a container'), a relationship ('-Uses-> Database'), indented metadata ('tech: React'), or a group ('[Backend]'). (\xA78)`
|
|
16065
|
+
);
|
|
16004
16066
|
}
|
|
16005
16067
|
}
|
|
16006
16068
|
finalizeAutoTagColors(result.tagGroups, palette);
|
|
@@ -16062,7 +16124,7 @@ function validateRelationshipTargets(result, knownNames, pushWarning) {
|
|
|
16062
16124
|
for (const el of elements) {
|
|
16063
16125
|
for (const rel of el.relationships) {
|
|
16064
16126
|
if (!knownNames.has(normalizeName(rel.target))) {
|
|
16065
|
-
let msg = `Relationship target "${rel.target}" not found`;
|
|
16127
|
+
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`;
|
|
16066
16128
|
const hint = suggest(normalizeName(rel.target), allNames);
|
|
16067
16129
|
if (hint) msg += `. ${hint}`;
|
|
16068
16130
|
pushWarning(rel.lineNumber, msg, "warning");
|
|
@@ -16090,7 +16152,7 @@ function validateDeploymentRefs(result, knownNames, pushWarning) {
|
|
|
16090
16152
|
for (const node of nodes) {
|
|
16091
16153
|
for (const ref of node.containerRefs) {
|
|
16092
16154
|
if (!knownNames.has(normalizeName(ref))) {
|
|
16093
|
-
let msg = `Deployment reference "container ${ref}" not found`;
|
|
16155
|
+
let msg = `Deployment reference "container ${ref}" not found \u2014 declare the container element first, then reference it under the deployment node`;
|
|
16094
16156
|
const hint = suggest(normalizeName(ref), allNames);
|
|
16095
16157
|
if (hint) msg += `. ${hint}`;
|
|
16096
16158
|
pushWarning(node.lineNumber, msg, "warning");
|
|
@@ -16809,7 +16871,7 @@ function parseInfra(content) {
|
|
|
16809
16871
|
}
|
|
16810
16872
|
warn(
|
|
16811
16873
|
lineNumber,
|
|
16812
|
-
`Invalid tag value '${trimmed}' in tag group '${currentTagGroup.name}'.`
|
|
16874
|
+
`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 '|'.`
|
|
16813
16875
|
);
|
|
16814
16876
|
continue;
|
|
16815
16877
|
}
|
|
@@ -16882,7 +16944,7 @@ function parseInfra(content) {
|
|
|
16882
16944
|
const n = deprecatedFanout[1];
|
|
16883
16945
|
setError(
|
|
16884
16946
|
lineNumber,
|
|
16885
|
-
`'x${n}' fanout syntax is no longer supported \u2014 use '
|
|
16947
|
+
`'x${n}' fanout syntax is no longer supported \u2014 use 'fanout: ${n}' instead`
|
|
16886
16948
|
);
|
|
16887
16949
|
continue;
|
|
16888
16950
|
}
|
|
@@ -17056,8 +17118,10 @@ function parseInfra(content) {
|
|
|
17056
17118
|
const hint = suggest(key, allKeys);
|
|
17057
17119
|
const valueLooksNumeric = /^[\d.]+%?$/.test(rawVal);
|
|
17058
17120
|
if (hint || valueLooksNumeric) {
|
|
17059
|
-
let msg = `Unknown property '${key}'.`;
|
|
17121
|
+
let msg = `Unknown infra property '${key}'.`;
|
|
17060
17122
|
if (hint) msg += ` ${hint}`;
|
|
17123
|
+
else
|
|
17124
|
+
msg += ` Properties are colon-keyed 'key: value' from the known schema (e.g. latency-ms, max-rps, cache-hit, instances, concurrency).`;
|
|
17061
17125
|
warn(lineNumber, msg);
|
|
17062
17126
|
} else if (!currentNode.isEdge) {
|
|
17063
17127
|
pushDescription(currentNode, trimmed);
|
|
@@ -17068,7 +17132,7 @@ function parseInfra(content) {
|
|
|
17068
17132
|
if (EDGE_ONLY_KEYS.has(key) && !currentNode.isEdge) {
|
|
17069
17133
|
warn(
|
|
17070
17134
|
lineNumber,
|
|
17071
|
-
`Property '${key}'
|
|
17135
|
+
`Property '${key}' belongs on the entry point \u2014 declare it indented under an 'internet' or 'edge' node, not on '${currentNode.label}'.`
|
|
17072
17136
|
);
|
|
17073
17137
|
}
|
|
17074
17138
|
const value = parsePropertyValue(rawVal);
|
|
@@ -17175,7 +17239,7 @@ function parseInfra(content) {
|
|
|
17175
17239
|
}
|
|
17176
17240
|
warn(
|
|
17177
17241
|
lineNumber,
|
|
17178
|
-
currentGroup ? `Unexpected line: '${trimmed}'.
|
|
17242
|
+
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'.`
|
|
17179
17243
|
);
|
|
17180
17244
|
}
|
|
17181
17245
|
finishCurrentNode();
|
|
@@ -17551,7 +17615,10 @@ function parseGantt(content, palette) {
|
|
|
17551
17615
|
continue;
|
|
17552
17616
|
}
|
|
17553
17617
|
if (COMMENT_RE.test(line11)) continue;
|
|
17554
|
-
warn(
|
|
17618
|
+
warn(
|
|
17619
|
+
lineNumber,
|
|
17620
|
+
`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)`
|
|
17621
|
+
);
|
|
17555
17622
|
continue;
|
|
17556
17623
|
}
|
|
17557
17624
|
}
|
|
@@ -17577,7 +17644,10 @@ function parseGantt(content, palette) {
|
|
|
17577
17644
|
lineNumber
|
|
17578
17645
|
});
|
|
17579
17646
|
} else {
|
|
17580
|
-
warn(
|
|
17647
|
+
warn(
|
|
17648
|
+
lineNumber,
|
|
17649
|
+
`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)`
|
|
17650
|
+
);
|
|
17581
17651
|
}
|
|
17582
17652
|
continue;
|
|
17583
17653
|
}
|
|
@@ -17603,7 +17673,10 @@ function parseGantt(content, palette) {
|
|
|
17603
17673
|
lineNumber
|
|
17604
17674
|
});
|
|
17605
17675
|
} else {
|
|
17606
|
-
warn(
|
|
17676
|
+
warn(
|
|
17677
|
+
lineNumber,
|
|
17678
|
+
`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)`
|
|
17679
|
+
);
|
|
17607
17680
|
}
|
|
17608
17681
|
continue;
|
|
17609
17682
|
}
|
|
@@ -17769,7 +17842,7 @@ function parseGantt(content, palette) {
|
|
|
17769
17842
|
} else {
|
|
17770
17843
|
softError(
|
|
17771
17844
|
lineNumber,
|
|
17772
|
-
`Arrow
|
|
17845
|
+
`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)`
|
|
17773
17846
|
);
|
|
17774
17847
|
}
|
|
17775
17848
|
}
|
|
@@ -18989,7 +19062,10 @@ function parseMap(content, palette) {
|
|
|
18989
19062
|
function handleTag(trimmed, line11) {
|
|
18990
19063
|
const m = matchTagBlockHeading(trimmed);
|
|
18991
19064
|
if (!m) {
|
|
18992
|
-
pushError(
|
|
19065
|
+
pushError(
|
|
19066
|
+
line11,
|
|
19067
|
+
`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.`
|
|
19068
|
+
);
|
|
18993
19069
|
return;
|
|
18994
19070
|
}
|
|
18995
19071
|
emitTagLegacyDiagnostic(m, line11, diagnostics);
|
|
@@ -19101,7 +19177,7 @@ function parseMap(content, palette) {
|
|
|
19101
19177
|
if (!pos || pos.kind === "name" && !pos.name) {
|
|
19102
19178
|
pushError(
|
|
19103
19179
|
line11,
|
|
19104
|
-
"route
|
|
19180
|
+
"route needs an origin place \u2014 `route <origin>` then indented legs (`-> dest`); origin may be a city name, IATA code, or `<lat> <lon>`. (\xA724B)"
|
|
19105
19181
|
);
|
|
19106
19182
|
return;
|
|
19107
19183
|
}
|
|
@@ -19164,7 +19240,10 @@ function parseMap(content, palette) {
|
|
|
19164
19240
|
function handleEdges(trimmed, line11) {
|
|
19165
19241
|
const parts = trimmed.split(ARROW_SPLIT);
|
|
19166
19242
|
if (parts.length < 3) {
|
|
19167
|
-
pushError(
|
|
19243
|
+
pushError(
|
|
19244
|
+
line11,
|
|
19245
|
+
`Malformed edge: "${trimmed}" \u2014 an edge is 'A -> B' (or 'A -label-> B', '~>' for arc, '--' undirected), both endpoints non-empty. (\xA724B)`
|
|
19246
|
+
);
|
|
19168
19247
|
return;
|
|
19169
19248
|
}
|
|
19170
19249
|
const endpoints = [];
|
|
@@ -19187,7 +19266,10 @@ function parseMap(content, palette) {
|
|
|
19187
19266
|
const from = endpoints[k];
|
|
19188
19267
|
const to = endpoints[k + 1];
|
|
19189
19268
|
if (!from || !to) {
|
|
19190
|
-
pushError(
|
|
19269
|
+
pushError(
|
|
19270
|
+
line11,
|
|
19271
|
+
`Edge has an empty endpoint: "${trimmed}" \u2014 both sides of the arrow need a place, e.g. 'Paris -> Berlin'. (\xA724B)`
|
|
19272
|
+
);
|
|
19191
19273
|
continue;
|
|
19192
19274
|
}
|
|
19193
19275
|
const isLast = k === links.length - 1;
|
|
@@ -21306,7 +21388,10 @@ function parseTechRadar(content) {
|
|
|
21306
21388
|
}
|
|
21307
21389
|
if (indent === 0 && trimmed.toLowerCase() === "rings") {
|
|
21308
21390
|
if (result.rings.length > 0) {
|
|
21309
|
-
warn(
|
|
21391
|
+
warn(
|
|
21392
|
+
lineNumber,
|
|
21393
|
+
'Duplicate "rings" block \u2014 only the last is kept; merge all ring names into one "rings" block.'
|
|
21394
|
+
);
|
|
21310
21395
|
result.rings = [];
|
|
21311
21396
|
}
|
|
21312
21397
|
inRingsBlock = true;
|
|
@@ -21525,7 +21610,7 @@ function parseTechRadar(content) {
|
|
|
21525
21610
|
result.diagnostics.push(
|
|
21526
21611
|
makeDgmoError(
|
|
21527
21612
|
lineNumber,
|
|
21528
|
-
`Blip "${trimmed}" has no ring
|
|
21613
|
+
`Blip "${trimmed}" has no ring. Put it under a ring section header, or add a colon-keyed ring \u2014 '${trimmed} ring: RingName' (per \xA720).`
|
|
21529
21614
|
)
|
|
21530
21615
|
);
|
|
21531
21616
|
continue;
|
|
@@ -21534,7 +21619,7 @@ function parseTechRadar(content) {
|
|
|
21534
21619
|
if (!trimmed.includes("|") && headerParsed && result.rings.length > 0) {
|
|
21535
21620
|
warn(
|
|
21536
21621
|
lineNumber,
|
|
21537
|
-
`Unrecognized line "${trimmed}".
|
|
21622
|
+
`Unrecognized line "${trimmed}". A quadrant header is 'Name quadrant: <top-left|top-right|bottom-left|bottom-right>' (per \xA720).`
|
|
21538
21623
|
);
|
|
21539
21624
|
}
|
|
21540
21625
|
}
|
|
@@ -21827,7 +21912,10 @@ function parseCycle(content) {
|
|
|
21827
21912
|
currentNode.description.push(descLine);
|
|
21828
21913
|
continue;
|
|
21829
21914
|
}
|
|
21830
|
-
warn(
|
|
21915
|
+
warn(
|
|
21916
|
+
lineNum,
|
|
21917
|
+
`Indented line "${trimmed}" has no step above it \u2014 add a step on a non-indented line first, then indent its details. (\xA721)`
|
|
21918
|
+
);
|
|
21831
21919
|
continue;
|
|
21832
21920
|
}
|
|
21833
21921
|
}
|
|
@@ -22130,7 +22218,7 @@ function parseJourneyMap(content, palette) {
|
|
|
22130
22218
|
if (trimmed.includes("|") || !PHASE_RE.test(trimmed)) {
|
|
22131
22219
|
warn(
|
|
22132
22220
|
lineNumber,
|
|
22133
|
-
"
|
|
22221
|
+
`Step "${trimmed}" sits outside any phase and is ignored \u2014 indent it under a '[Phase]' header (per \xA722).`
|
|
22134
22222
|
);
|
|
22135
22223
|
continue;
|
|
22136
22224
|
}
|
|
@@ -22187,7 +22275,10 @@ function parseJourneyMap(content, palette) {
|
|
|
22187
22275
|
continue;
|
|
22188
22276
|
}
|
|
22189
22277
|
if (indent > 0 && !currentPhase && hasPhases) {
|
|
22190
|
-
warn(
|
|
22278
|
+
warn(
|
|
22279
|
+
lineNumber,
|
|
22280
|
+
`Indented line outside any phase \u2014 add a '[Phase]' header above it, or unindent it to a top-level step. (\xA722)`
|
|
22281
|
+
);
|
|
22191
22282
|
}
|
|
22192
22283
|
}
|
|
22193
22284
|
if (currentStep) {
|
|
@@ -22533,7 +22624,10 @@ function parsePyramid(content) {
|
|
|
22533
22624
|
continue;
|
|
22534
22625
|
}
|
|
22535
22626
|
if (!currentLayer) {
|
|
22536
|
-
warn(
|
|
22627
|
+
warn(
|
|
22628
|
+
lineNum,
|
|
22629
|
+
`Indented description "${trimmed}" has no layer above it \u2014 add a layer name on a non-indented line first. (\xA723)`
|
|
22630
|
+
);
|
|
22537
22631
|
continue;
|
|
22538
22632
|
}
|
|
22539
22633
|
const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
|
|
@@ -22707,7 +22801,10 @@ function parseRing(content) {
|
|
|
22707
22801
|
continue;
|
|
22708
22802
|
}
|
|
22709
22803
|
if (!currentLayer) {
|
|
22710
|
-
warn(
|
|
22804
|
+
warn(
|
|
22805
|
+
lineNum,
|
|
22806
|
+
`Indented description "${trimmed}" has no layer above it \u2014 add a layer name on a non-indented line first. (\xA724)`
|
|
22807
|
+
);
|
|
22711
22808
|
continue;
|
|
22712
22809
|
}
|
|
22713
22810
|
const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
|
|
@@ -23433,7 +23530,10 @@ function parseRaci(content, palette) {
|
|
|
23433
23530
|
continue;
|
|
23434
23531
|
}
|
|
23435
23532
|
if (bodyStarted) {
|
|
23436
|
-
errorAt(
|
|
23533
|
+
errorAt(
|
|
23534
|
+
lineNumber,
|
|
23535
|
+
`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)`
|
|
23536
|
+
);
|
|
23437
23537
|
} else {
|
|
23438
23538
|
warn(lineNumber, `Unrecognized directive: '${trimmed}'.`);
|
|
23439
23539
|
}
|
|
@@ -23980,6 +24080,13 @@ function getAllChartTypes() {
|
|
|
23980
24080
|
return chartTypes.map((c) => c.id);
|
|
23981
24081
|
}
|
|
23982
24082
|
function parseDgmo(content) {
|
|
24083
|
+
const result = parseDgmoUndeduped(content);
|
|
24084
|
+
return {
|
|
24085
|
+
diagnostics: dedupeDiagnostics(result.diagnostics),
|
|
24086
|
+
chartType: result.chartType
|
|
24087
|
+
};
|
|
24088
|
+
}
|
|
24089
|
+
function parseDgmoUndeduped(content) {
|
|
23983
24090
|
const chartType = parseDgmoChartType(content);
|
|
23984
24091
|
if (!chartType) {
|
|
23985
24092
|
const colonDiag = detectColonChartType(content);
|
|
@@ -51307,7 +51414,7 @@ function resolveMap(parsed, data) {
|
|
|
51307
51414
|
const hint = suggest(r.name, allNames);
|
|
51308
51415
|
err(
|
|
51309
51416
|
r.lineNumber,
|
|
51310
|
-
`Unknown
|
|
51417
|
+
`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.`,
|
|
51311
51418
|
"E_MAP_UNKNOWN_SUBDIVISION"
|
|
51312
51419
|
);
|
|
51313
51420
|
continue;
|
|
@@ -51387,7 +51494,7 @@ function resolveMap(parsed, data) {
|
|
|
51387
51494
|
const hint = suggest(name, cityNames);
|
|
51388
51495
|
err(
|
|
51389
51496
|
line11,
|
|
51390
|
-
`Unknown place "${name}"
|
|
51497
|
+
`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>\`.`,
|
|
51391
51498
|
"E_MAP_UNKNOWN_PLACE"
|
|
51392
51499
|
);
|
|
51393
51500
|
return { kind: "miss" };
|
|
@@ -51929,6 +52036,21 @@ function mapLegendBand(legend, opts) {
|
|
|
51929
52036
|
if (height <= 0) return 0;
|
|
51930
52037
|
return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
|
|
51931
52038
|
}
|
|
52039
|
+
function mapLegendBox(legend, opts) {
|
|
52040
|
+
if (!legend) return null;
|
|
52041
|
+
const groups = mapLegendGroups(legend);
|
|
52042
|
+
if (groups.length === 0) return null;
|
|
52043
|
+
const config = mapLegendConfig(groups, opts.mode);
|
|
52044
|
+
const state = { activeGroup: legend.activeGroup };
|
|
52045
|
+
const { width, height } = computeLegendLayout(config, state, opts.width);
|
|
52046
|
+
if (height <= 0 || width <= 0) return null;
|
|
52047
|
+
return {
|
|
52048
|
+
x: (opts.width - width) / 2,
|
|
52049
|
+
y: mapLegendTop(opts.hasTitle, opts.hasSubtitle),
|
|
52050
|
+
width,
|
|
52051
|
+
height
|
|
52052
|
+
};
|
|
52053
|
+
}
|
|
51932
52054
|
var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
|
|
51933
52055
|
var init_legend_band = __esm({
|
|
51934
52056
|
"src/map/legend-band.ts"() {
|
|
@@ -52053,6 +52175,8 @@ function placeContextLabels(args) {
|
|
|
52053
52175
|
waterBodies,
|
|
52054
52176
|
countries,
|
|
52055
52177
|
palette,
|
|
52178
|
+
countryTone,
|
|
52179
|
+
waterTone,
|
|
52056
52180
|
project,
|
|
52057
52181
|
collides,
|
|
52058
52182
|
contentPoints,
|
|
@@ -52062,8 +52186,8 @@ function placeContextLabels(args) {
|
|
|
52062
52186
|
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
52063
52187
|
const budget = labelBudget(width, height, band);
|
|
52064
52188
|
if (budget <= 0) return [];
|
|
52065
|
-
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
52066
|
-
const countryColor = palette.textMuted;
|
|
52189
|
+
const waterColor = waterTone ?? mix(palette.colors.blue, palette.textMuted, 50);
|
|
52190
|
+
const countryColor = countryTone ?? palette.textMuted;
|
|
52067
52191
|
const haloColor = palette.bg;
|
|
52068
52192
|
const candidates = [];
|
|
52069
52193
|
const center = [width / 2, height / 2];
|
|
@@ -52152,8 +52276,7 @@ function placeContextLabels(args) {
|
|
|
52152
52276
|
)
|
|
52153
52277
|
);
|
|
52154
52278
|
const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
|
|
52155
|
-
const
|
|
52156
|
-
const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
|
|
52279
|
+
const color = countryColor;
|
|
52157
52280
|
const text = c.name;
|
|
52158
52281
|
const tw = labelWidth(text, 0, fontSize);
|
|
52159
52282
|
if (tw > w || fontSize + 2 * PADY > h) continue;
|
|
@@ -52247,7 +52370,7 @@ function placeContextLabels(args) {
|
|
|
52247
52370
|
}
|
|
52248
52371
|
return placed;
|
|
52249
52372
|
}
|
|
52250
|
-
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,
|
|
52373
|
+
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;
|
|
52251
52374
|
var init_context_labels = __esm({
|
|
52252
52375
|
"src/map/context-labels.ts"() {
|
|
52253
52376
|
"use strict";
|
|
@@ -52264,7 +52387,6 @@ var init_context_labels = __esm({
|
|
|
52264
52387
|
COUNTRY_FONT_MAX = 22;
|
|
52265
52388
|
COUNTRY_SIZE_FRAC_MIN = 0.06;
|
|
52266
52389
|
COUNTRY_SIZE_FRAC_MAX = 0.32;
|
|
52267
|
-
COUNTRY_FADE_MAX = 45;
|
|
52268
52390
|
MAX_COUNTRY_POSITIONS = 4;
|
|
52269
52391
|
COUNTRY_POS_GRID = 5;
|
|
52270
52392
|
COUNTRY_POS_TOPN_MARGIN = 3;
|
|
@@ -52391,36 +52513,23 @@ function countryLabelPositions(args) {
|
|
|
52391
52513
|
return n;
|
|
52392
52514
|
};
|
|
52393
52515
|
const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
|
|
52394
|
-
const
|
|
52395
|
-
|
|
52396
|
-
|
|
52397
|
-
|
|
52398
|
-
|
|
52399
|
-
|
|
52400
|
-
|
|
52401
|
-
let bestIdx = 0;
|
|
52402
|
-
let bestMin = -1;
|
|
52403
|
-
for (let k = 0; k < pool.length; k++) {
|
|
52404
|
-
const c = pool[k];
|
|
52405
|
-
let minD = Infinity;
|
|
52406
|
-
for (const o of ordered) {
|
|
52407
|
-
const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
|
|
52408
|
-
if (d < minD) minD = d;
|
|
52409
|
-
}
|
|
52410
|
-
if (minD > bestMin) {
|
|
52411
|
-
bestMin = minD;
|
|
52412
|
-
bestIdx = k;
|
|
52413
|
-
}
|
|
52516
|
+
const centreLon = kept.reduce((s, c) => s + c.lon, 0) / kept.length;
|
|
52517
|
+
const centreLat = kept.reduce((s, c) => s + c.lat, 0) / kept.length;
|
|
52518
|
+
const centreScreen = project(centreLon, centreLat);
|
|
52519
|
+
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 ? [
|
|
52520
|
+
{
|
|
52521
|
+
lonLat: [centreLon, centreLat],
|
|
52522
|
+
screen: [centreScreen[0], centreScreen[1]]
|
|
52414
52523
|
}
|
|
52415
|
-
|
|
52416
|
-
|
|
52417
|
-
|
|
52524
|
+
] : [];
|
|
52525
|
+
const grid = [...kept].sort(
|
|
52526
|
+
(a, b) => interiorness(b) - interiorness(a) || dist2ToCentre(a) - dist2ToCentre(b)
|
|
52527
|
+
).map((c) => ({
|
|
52418
52528
|
lonLat: [c.lon, c.lat],
|
|
52419
52529
|
screen: [c.sx, c.sy]
|
|
52420
52530
|
}));
|
|
52421
52531
|
const curatedPos = curated ? mkCurated(curated, project) : [];
|
|
52422
|
-
|
|
52423
|
-
return out;
|
|
52532
|
+
return [...curatedPos, ...lead, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
|
|
52424
52533
|
}
|
|
52425
52534
|
function mkCurated(curated, project) {
|
|
52426
52535
|
if (!curated) return [];
|
|
@@ -52686,6 +52795,25 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52686
52795
|
palette.bg,
|
|
52687
52796
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
52688
52797
|
);
|
|
52798
|
+
const contrastFloorTone = (tone, ...substrates) => {
|
|
52799
|
+
const clears = (t) => substrates.every((s) => contrastRatio(t, s) >= REGION_LABEL_HALO_RATIO);
|
|
52800
|
+
if (clears(tone)) return tone;
|
|
52801
|
+
for (let s = 10; s <= 100; s += 10) {
|
|
52802
|
+
const t = mix(palette.text, tone, s);
|
|
52803
|
+
if (clears(t)) return t;
|
|
52804
|
+
}
|
|
52805
|
+
return palette.text;
|
|
52806
|
+
};
|
|
52807
|
+
const backdropLandTone = contrastFloorTone(
|
|
52808
|
+
palette.textMuted,
|
|
52809
|
+
neutralFill,
|
|
52810
|
+
water
|
|
52811
|
+
);
|
|
52812
|
+
const backdropWaterTone = contrastFloorTone(
|
|
52813
|
+
mix(palette.colors.blue, palette.textMuted, 50),
|
|
52814
|
+
water,
|
|
52815
|
+
neutralFill
|
|
52816
|
+
);
|
|
52689
52817
|
const hasDirectColor = resolved.regions.some((r) => r.color !== void 0);
|
|
52690
52818
|
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && !hasDirectColor && fillGroupNames.size === 0;
|
|
52691
52819
|
const colorByIso = /* @__PURE__ */ new Map();
|
|
@@ -52800,6 +52928,33 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52800
52928
|
hasSubtitle: Boolean(resolved.subtitle)
|
|
52801
52929
|
});
|
|
52802
52930
|
if (legendBand > topPad) topPad = legendBand;
|
|
52931
|
+
const topReserved = [];
|
|
52932
|
+
if (shownTitle) {
|
|
52933
|
+
const lines = resolved.subtitle ? 2 : 1;
|
|
52934
|
+
const tw = Math.max(
|
|
52935
|
+
measureLegendText(shownTitle, TITLE_FONT_SIZE),
|
|
52936
|
+
resolved.subtitle ? measureLegendText(resolved.subtitle, TITLE_FONT_SIZE) : 0
|
|
52937
|
+
) + 2 * TITLE_FONT_SIZE;
|
|
52938
|
+
topReserved.push({
|
|
52939
|
+
x: width / 2 - tw / 2,
|
|
52940
|
+
y: TITLE_Y - TITLE_FONT_SIZE,
|
|
52941
|
+
w: tw,
|
|
52942
|
+
h: TITLE_FONT_SIZE * (lines + 0.5)
|
|
52943
|
+
});
|
|
52944
|
+
}
|
|
52945
|
+
const legendBox = mapLegendBox(legend, {
|
|
52946
|
+
width,
|
|
52947
|
+
mode: opts.legendMode ?? "preview",
|
|
52948
|
+
hasTitle: Boolean(shownTitle),
|
|
52949
|
+
hasSubtitle: Boolean(resolved.subtitle)
|
|
52950
|
+
});
|
|
52951
|
+
if (legendBox)
|
|
52952
|
+
topReserved.push({
|
|
52953
|
+
x: legendBox.x,
|
|
52954
|
+
y: legendBox.y,
|
|
52955
|
+
w: legendBox.width,
|
|
52956
|
+
h: legendBox.height
|
|
52957
|
+
});
|
|
52803
52958
|
const reserve = opts._calloutReserve;
|
|
52804
52959
|
const fitLeft = FIT_PAD + (reserve?.left ?? 0);
|
|
52805
52960
|
const fitRight = width - FIT_PAD - (reserve?.right ?? 0);
|
|
@@ -53529,6 +53684,19 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53529
53684
|
const by = b.cy - (b.cy - py) / tb * trimB;
|
|
53530
53685
|
return `M${ax},${ay}Q${px},${py} ${bx},${by}`;
|
|
53531
53686
|
};
|
|
53687
|
+
const legLabelPoint = (a, b, curved, offset) => {
|
|
53688
|
+
const mx = (a.cx + b.cx) / 2;
|
|
53689
|
+
const my = (a.cy + b.cy) / 2;
|
|
53690
|
+
if (!curved && offset === 0) return { x: mx, y: my - 4 };
|
|
53691
|
+
const dx = b.cx - a.cx;
|
|
53692
|
+
const dy = b.cy - a.cy;
|
|
53693
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
53694
|
+
const nx = -dy / len;
|
|
53695
|
+
const ny = dx / len;
|
|
53696
|
+
const bow = offset !== 0 ? offset : len * ARC_CURVE_FRAC;
|
|
53697
|
+
const off = bow * 0.5 + Math.sign(bow || 1) * 8;
|
|
53698
|
+
return { x: mx + nx * off, y: my + ny * off };
|
|
53699
|
+
};
|
|
53532
53700
|
const routeLegVals = resolved.routes.flatMap((rt) => rt.legs).map((l) => Number(l.value)).filter((n) => Number.isFinite(n) && n > 0);
|
|
53533
53701
|
const rlMin = routeLegVals.length ? Math.min(...routeLegVals) : 0;
|
|
53534
53702
|
const rlMax = routeLegVals.length ? Math.max(...routeLegVals) : 0;
|
|
@@ -53542,13 +53710,12 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53542
53710
|
const a = poiScreen.get(leg.fromId);
|
|
53543
53711
|
const b = poiScreen.get(leg.toId);
|
|
53544
53712
|
if (!a || !b) continue;
|
|
53545
|
-
const
|
|
53546
|
-
const my = (a.cy + b.cy) / 2;
|
|
53713
|
+
const lp = legLabelPoint(a, b, leg.style === "arc", 0);
|
|
53547
53714
|
const bow = {
|
|
53548
53715
|
curved: leg.style === "arc",
|
|
53549
53716
|
offset: 0,
|
|
53550
|
-
labelX:
|
|
53551
|
-
labelY:
|
|
53717
|
+
labelX: lp.x,
|
|
53718
|
+
labelY: lp.y
|
|
53552
53719
|
};
|
|
53553
53720
|
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53554
53721
|
legs.push({
|
|
@@ -53595,13 +53762,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53595
53762
|
const b = poiScreen.get(e.toId);
|
|
53596
53763
|
if (!a || !b) return;
|
|
53597
53764
|
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
53598
|
-
const
|
|
53599
|
-
const
|
|
53765
|
+
const curved = e.style === "arc" || n > 1;
|
|
53766
|
+
const lp = legLabelPoint(a, b, curved, fanOffset);
|
|
53600
53767
|
const bow = {
|
|
53601
|
-
curved
|
|
53768
|
+
curved,
|
|
53602
53769
|
offset: fanOffset,
|
|
53603
|
-
labelX:
|
|
53604
|
-
labelY:
|
|
53770
|
+
labelX: lp.x,
|
|
53771
|
+
labelY: lp.y
|
|
53605
53772
|
};
|
|
53606
53773
|
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53607
53774
|
legs.push({
|
|
@@ -53625,7 +53792,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53625
53792
|
});
|
|
53626
53793
|
}
|
|
53627
53794
|
const labels = [];
|
|
53628
|
-
const obstacles = [];
|
|
53795
|
+
const obstacles = [...topReserved];
|
|
53629
53796
|
const regionLabelGuards = [];
|
|
53630
53797
|
const markers = pois.map((p) => ({
|
|
53631
53798
|
cx: p.cx,
|
|
@@ -53663,6 +53830,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53663
53830
|
}
|
|
53664
53831
|
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));
|
|
53665
53832
|
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
53833
|
+
const labeledRegionIds = /* @__PURE__ */ new Set();
|
|
53666
53834
|
const showRegionValues = resolved.directives.noRegionValue !== true && activeIsScore;
|
|
53667
53835
|
const regionValueStr = (value) => showRegionValues && value !== void 0 ? compactNumber(value) : void 0;
|
|
53668
53836
|
const isCompact = width < COMPACT_WIDTH_PX;
|
|
@@ -53683,7 +53851,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53683
53851
|
const REGION_FONT_MAX_ORIENT = 22;
|
|
53684
53852
|
const REGION_SIZE_FRAC_MIN = 0.06;
|
|
53685
53853
|
const REGION_SIZE_FRAC_MAX = 0.32;
|
|
53686
|
-
const
|
|
53854
|
+
const SUBJECT_FONT_MAX = 18;
|
|
53855
|
+
const SUBJECT_MIN_PROMINENCE = 13;
|
|
53856
|
+
const SUBJECT_LEADER_FONT = 15;
|
|
53687
53857
|
const canvasLinear = Math.sqrt(Math.max(1, width * height));
|
|
53688
53858
|
const sizeT = (boxW, boxH) => {
|
|
53689
53859
|
const frac = Math.sqrt(Math.max(0, boxW * boxH)) / canvasLinear;
|
|
@@ -53695,9 +53865,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53695
53865
|
)
|
|
53696
53866
|
);
|
|
53697
53867
|
};
|
|
53698
|
-
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2,
|
|
53868
|
+
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2, colorOverride) => {
|
|
53699
53869
|
const { color: baseColor, haloColor } = labelOnFill(fill2);
|
|
53700
|
-
const color =
|
|
53870
|
+
const color = colorOverride ?? baseColor;
|
|
53701
53871
|
const vf = Math.round(fontSize * 0.82);
|
|
53702
53872
|
const halfW = Math.max(
|
|
53703
53873
|
measureLegendText(text, fontSize),
|
|
@@ -53712,7 +53882,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53712
53882
|
text,
|
|
53713
53883
|
anchor: "middle",
|
|
53714
53884
|
color,
|
|
53715
|
-
|
|
53885
|
+
// A backdrop label (colorOverride) NEVER haloes: the placement gate above
|
|
53886
|
+
// guarantees it sits on a floored-readable substrate (own fill, neutral
|
|
53887
|
+
// land, foreign land, or water), so a halo is pure noise. Data labels keep
|
|
53888
|
+
// the containment-gated halo until the role-tier pass (Phase 3).
|
|
53889
|
+
halo: colorOverride ? false : overflows,
|
|
53716
53890
|
haloColor,
|
|
53717
53891
|
...fontSize !== FONT2 && { fontSize },
|
|
53718
53892
|
...valueLine !== void 0 && { valueLine },
|
|
@@ -53748,7 +53922,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53748
53922
|
if (!c || !Number.isFinite(c[0])) return null;
|
|
53749
53923
|
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
53750
53924
|
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
53751
|
-
const placedRegionRects = [];
|
|
53925
|
+
const placedRegionRects = [...topReserved];
|
|
53752
53926
|
const placedLeaders = [];
|
|
53753
53927
|
const isEmptyFill = (f) => f === water || f === neutralFill || f === foreignFill;
|
|
53754
53928
|
const POI_LABEL_PAD = 14;
|
|
@@ -53761,9 +53935,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53761
53935
|
const SHORT_HOP_MAX = Math.max(46, Math.min(width, height) * 0.11);
|
|
53762
53936
|
const STEP = 4;
|
|
53763
53937
|
const HOP_GAP = 9;
|
|
53764
|
-
const tryShortHopCallout = (cx, cy, fill2, name, value) => {
|
|
53765
|
-
const chipW = stackW(name, value);
|
|
53766
|
-
const chipH = stackH(value !== void 0);
|
|
53938
|
+
const tryShortHopCallout = (cx, cy, fill2, name, value, font = FONT2, gap = HOP_GAP, maxLen = SHORT_HOP_MAX) => {
|
|
53939
|
+
const chipW = stackW(name, value, font);
|
|
53940
|
+
const chipH = stackH(value !== void 0, font);
|
|
53767
53941
|
const dirs = [
|
|
53768
53942
|
[0, -1],
|
|
53769
53943
|
// north (over Canada)
|
|
@@ -53779,7 +53953,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53779
53953
|
let ex = cx;
|
|
53780
53954
|
let ey = cy;
|
|
53781
53955
|
let steps = 0;
|
|
53782
|
-
const maxSteps = Math.ceil(
|
|
53956
|
+
const maxSteps = Math.ceil(maxLen / STEP) + 1;
|
|
53783
53957
|
while (steps < maxSteps && fillAt(ex, ey) === fill2) {
|
|
53784
53958
|
ex += dx * STEP;
|
|
53785
53959
|
ey += dy * STEP;
|
|
@@ -53787,8 +53961,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53787
53961
|
}
|
|
53788
53962
|
if (fillAt(ex, ey) === fill2) continue;
|
|
53789
53963
|
const halfAlong = (Math.abs(dx) * chipW + Math.abs(dy) * chipH) / 2;
|
|
53790
|
-
const ccx = ex + dx * (
|
|
53791
|
-
const ccy = ey + dy * (
|
|
53964
|
+
const ccx = ex + dx * (gap + halfAlong);
|
|
53965
|
+
const ccy = ey + dy * (gap + halfAlong);
|
|
53792
53966
|
const rect = {
|
|
53793
53967
|
x: ccx - chipW / 2,
|
|
53794
53968
|
y: ccy - chipH / 2,
|
|
@@ -53813,7 +53987,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53813
53987
|
const innerX = ccx - dx * (chipW / 2);
|
|
53814
53988
|
const innerY = ccy - dy * (chipH / 2);
|
|
53815
53989
|
const len = Math.hypot(innerX - cx, innerY - cy);
|
|
53816
|
-
if (len >
|
|
53990
|
+
if (len > maxLen) continue;
|
|
53817
53991
|
if (placedLeaders.some(
|
|
53818
53992
|
(l) => segmentsCross(cx, cy, innerX, innerY, l[0], l[1], l[2], l[3])
|
|
53819
53993
|
))
|
|
@@ -53837,6 +54011,78 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53837
54011
|
const valStr = regionValueStr(r.value);
|
|
53838
54012
|
const fitsRegions = (rect) => !placedRegionRects.some((p) => rectsOverlap(rect, p));
|
|
53839
54013
|
const fitsPois = (rect) => !poiObstacles.some((o) => rectsOverlap(rect, o));
|
|
54014
|
+
const isSubject = regionById.has(r.id);
|
|
54015
|
+
const extremesOnFill = (ax, ay, text) => {
|
|
54016
|
+
const halfW = measureLegendText(text, FONT2) / 2;
|
|
54017
|
+
return fillAt(ax - halfW, ay) === r.fill && fillAt(ax + halfW, ay) === r.fill;
|
|
54018
|
+
};
|
|
54019
|
+
if (isSubject && valStr === void 0 && r.label !== void 0) {
|
|
54020
|
+
const name = candidates[0];
|
|
54021
|
+
let best = -1;
|
|
54022
|
+
for (let f = SUBJECT_FONT_MAX; f >= FONT2; f--) {
|
|
54023
|
+
if (measureLegendText(name, f) > boxW || f + 2 * LABEL_PADY > boxH)
|
|
54024
|
+
continue;
|
|
54025
|
+
const rect = regionLabelRect(c[0], c[1], name, void 0, f);
|
|
54026
|
+
if (!fitsRegions(rect) || !fitsPois(rect) || collides(rect)) continue;
|
|
54027
|
+
const halfW = measureLegendText(name, f) / 2;
|
|
54028
|
+
if (fillAt(c[0] - halfW, c[1]) !== r.fill || fillAt(c[0] + halfW, c[1]) !== r.fill)
|
|
54029
|
+
continue;
|
|
54030
|
+
best = f;
|
|
54031
|
+
break;
|
|
54032
|
+
}
|
|
54033
|
+
if (best >= SUBJECT_MIN_PROMINENCE) {
|
|
54034
|
+
const rect = regionLabelRect(c[0], c[1], name, void 0, best);
|
|
54035
|
+
placedRegionRects.push(rect);
|
|
54036
|
+
pushRegionLabel(
|
|
54037
|
+
c[0],
|
|
54038
|
+
c[1],
|
|
54039
|
+
name,
|
|
54040
|
+
r.fill,
|
|
54041
|
+
r.lineNumber,
|
|
54042
|
+
void 0,
|
|
54043
|
+
best
|
|
54044
|
+
);
|
|
54045
|
+
labeledRegionIds.add(r.id);
|
|
54046
|
+
regionLabelGuards.push({ label: labels[labels.length - 1], rect });
|
|
54047
|
+
} else {
|
|
54048
|
+
const hop = tryShortHopCallout(
|
|
54049
|
+
c[0],
|
|
54050
|
+
c[1],
|
|
54051
|
+
r.fill,
|
|
54052
|
+
r.label,
|
|
54053
|
+
void 0,
|
|
54054
|
+
SUBJECT_LEADER_FONT,
|
|
54055
|
+
HOP_GAP + SUBJECT_LEADER_FONT,
|
|
54056
|
+
SHORT_HOP_MAX * 2.2
|
|
54057
|
+
);
|
|
54058
|
+
if (hop) {
|
|
54059
|
+
placedRegionRects.push(hop.rect);
|
|
54060
|
+
placedLeaders.push(hop.leader);
|
|
54061
|
+
const dark = mix(r.fill, palette.text, 60);
|
|
54062
|
+
labels.push({
|
|
54063
|
+
x: hop.x,
|
|
54064
|
+
y: hop.y,
|
|
54065
|
+
text: r.label,
|
|
54066
|
+
anchor: "middle",
|
|
54067
|
+
color: palette.text,
|
|
54068
|
+
halo: false,
|
|
54069
|
+
haloColor: palette.bg,
|
|
54070
|
+
fontSize: SUBJECT_LEADER_FONT,
|
|
54071
|
+
leader: {
|
|
54072
|
+
x1: hop.leader[0],
|
|
54073
|
+
y1: hop.leader[1],
|
|
54074
|
+
x2: hop.leader[2],
|
|
54075
|
+
y2: hop.leader[3]
|
|
54076
|
+
},
|
|
54077
|
+
leaderColor: dark,
|
|
54078
|
+
calloutDot: { x: c[0], y: c[1], color: dark },
|
|
54079
|
+
lineNumber: r.lineNumber
|
|
54080
|
+
});
|
|
54081
|
+
labeledRegionIds.add(r.id);
|
|
54082
|
+
}
|
|
54083
|
+
}
|
|
54084
|
+
continue;
|
|
54085
|
+
}
|
|
53840
54086
|
const seekAnchors = [
|
|
53841
54087
|
{ x: c[0], y: c[1], guard: false }
|
|
53842
54088
|
];
|
|
@@ -53859,13 +54105,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53859
54105
|
const nameRect = regionLabelRect(a.x, a.y, t);
|
|
53860
54106
|
if (valStr && stackW(t, valStr) <= boxW && stackH(true) <= boxH) {
|
|
53861
54107
|
const stackRect = regionLabelRect(a.x, a.y, t, valStr);
|
|
53862
|
-
if (fitsRegions(stackRect) && fitsPois(nameRect)) {
|
|
54108
|
+
if (fitsRegions(stackRect) && fitsPois(nameRect) && extremesOnFill(a.x, a.y, t)) {
|
|
53863
54109
|
chosen = { text: t, valueLine: valStr, ax: a.x, ay: a.y };
|
|
53864
54110
|
break;
|
|
53865
54111
|
}
|
|
53866
54112
|
}
|
|
53867
54113
|
if (labelW(t) <= boxW && labelH <= boxH) {
|
|
53868
|
-
if (fitsRegions(nameRect) && fitsPois(nameRect)) {
|
|
54114
|
+
if (fitsRegions(nameRect) && fitsPois(nameRect) && (valStr === void 0 || extremesOnFill(a.x, a.y, t))) {
|
|
53869
54115
|
chosen = { text: t, ax: a.x, ay: a.y };
|
|
53870
54116
|
break;
|
|
53871
54117
|
}
|
|
@@ -53873,7 +54119,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53873
54119
|
}
|
|
53874
54120
|
if (chosen) break;
|
|
53875
54121
|
}
|
|
53876
|
-
if (chosen === void 0 &&
|
|
54122
|
+
if (chosen === void 0 && r.label !== void 0 && isSubject) {
|
|
53877
54123
|
const hop = tryShortHopCallout(c[0], c[1], r.fill, r.label, valStr);
|
|
53878
54124
|
if (hop) {
|
|
53879
54125
|
placedRegionRects.push(hop.rect);
|
|
@@ -53885,9 +54131,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53885
54131
|
text: r.label,
|
|
53886
54132
|
anchor: "middle",
|
|
53887
54133
|
color: palette.text,
|
|
53888
|
-
halo:
|
|
54134
|
+
halo: false,
|
|
53889
54135
|
haloColor: palette.bg,
|
|
53890
|
-
valueLine: valStr,
|
|
54136
|
+
...valStr !== void 0 && { valueLine: valStr },
|
|
53891
54137
|
leader: {
|
|
53892
54138
|
x1: hop.leader[0],
|
|
53893
54139
|
y1: hop.leader[1],
|
|
@@ -53898,20 +54144,28 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53898
54144
|
calloutDot: { x: c[0], y: c[1], color: dark },
|
|
53899
54145
|
lineNumber: r.lineNumber
|
|
53900
54146
|
});
|
|
54147
|
+
labeledRegionIds.add(r.id);
|
|
53901
54148
|
}
|
|
53902
54149
|
continue;
|
|
53903
54150
|
}
|
|
53904
54151
|
if (chosen === void 0) continue;
|
|
53905
54152
|
const isOrient = r.value === void 0 && r.layer === "base";
|
|
53906
54153
|
let font = FONT2;
|
|
53907
|
-
let fade = 0;
|
|
53908
54154
|
if (isOrient) {
|
|
53909
54155
|
const growT = sizeT(boxW, boxH);
|
|
53910
54156
|
const desiredFont = Math.round(
|
|
53911
54157
|
FONT2 + growT * (REGION_FONT_MAX_ORIENT - FONT2)
|
|
53912
54158
|
);
|
|
53913
54159
|
const hasVal = chosen.valueLine !== void 0;
|
|
53914
|
-
|
|
54160
|
+
const extremesClean = (f) => {
|
|
54161
|
+
const halfW = measureLegendText(chosen.text, f) / 2;
|
|
54162
|
+
return [chosen.ax - halfW, chosen.ax + halfW].every((sx) => {
|
|
54163
|
+
const fAt = fillAt(sx, chosen.ay);
|
|
54164
|
+
return fAt === r.fill || isEmptyFill(fAt);
|
|
54165
|
+
});
|
|
54166
|
+
};
|
|
54167
|
+
let fit = -1;
|
|
54168
|
+
for (let f = desiredFont; f >= FONT2; f--) {
|
|
53915
54169
|
if (stackW(chosen.text, chosen.valueLine, f) > boxW || stackH(hasVal, f) > boxH)
|
|
53916
54170
|
continue;
|
|
53917
54171
|
const gRect = regionLabelRect(
|
|
@@ -53928,14 +54182,18 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53928
54182
|
void 0,
|
|
53929
54183
|
f
|
|
53930
54184
|
);
|
|
53931
|
-
if (!fitsRegions(gRect) || !fitsPois(gName))
|
|
53932
|
-
const halfW = measureLegendText(chosen.text, f) / 2;
|
|
53933
|
-
if (fillAt(chosen.ax - halfW, chosen.ay) !== r.fill || fillAt(chosen.ax + halfW, chosen.ay) !== r.fill)
|
|
54185
|
+
if (!fitsRegions(gRect) || !fitsPois(gName) || collides(gName))
|
|
53934
54186
|
continue;
|
|
53935
|
-
|
|
54187
|
+
if (!extremesClean(f)) continue;
|
|
54188
|
+
fit = f;
|
|
53936
54189
|
break;
|
|
53937
54190
|
}
|
|
53938
|
-
|
|
54191
|
+
if (fit < 0) {
|
|
54192
|
+
if (!frameContainers.has(r.id)) continue;
|
|
54193
|
+
font = FONT2;
|
|
54194
|
+
} else {
|
|
54195
|
+
font = fit;
|
|
54196
|
+
}
|
|
53939
54197
|
}
|
|
53940
54198
|
const rRect = regionLabelRect(
|
|
53941
54199
|
chosen.ax,
|
|
@@ -53953,8 +54211,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53953
54211
|
r.lineNumber,
|
|
53954
54212
|
chosen.valueLine,
|
|
53955
54213
|
font,
|
|
53956
|
-
|
|
54214
|
+
isOrient ? backdropLandTone : void 0
|
|
53957
54215
|
);
|
|
54216
|
+
labeledRegionIds.add(r.id);
|
|
53958
54217
|
regionLabelGuards.push({
|
|
53959
54218
|
label: labels[labels.length - 1],
|
|
53960
54219
|
rect: rRect
|
|
@@ -53972,6 +54231,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53972
54231
|
seed.lineNumber,
|
|
53973
54232
|
valStr
|
|
53974
54233
|
);
|
|
54234
|
+
labeledRegionIds.add(seed.iso);
|
|
53975
54235
|
regionLabelGuards.push({
|
|
53976
54236
|
label: labels[labels.length - 1],
|
|
53977
54237
|
rect: regionLabelRect(seed.x, seed.y, text, valStr)
|
|
@@ -54403,13 +54663,30 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54403
54663
|
const nx = -dy / len;
|
|
54404
54664
|
const ny = dx / len;
|
|
54405
54665
|
const perp = labelH + 2;
|
|
54666
|
+
const pm = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
54667
|
+
lg.d
|
|
54668
|
+
);
|
|
54669
|
+
const pointAt = (t) => {
|
|
54670
|
+
if (pm?.[5] !== void 0) {
|
|
54671
|
+
const u = 1 - t;
|
|
54672
|
+
return [
|
|
54673
|
+
u * u * +pm[1] + 2 * u * t * +pm[5] + t * t * +pm[7],
|
|
54674
|
+
u * u * +pm[2] + 2 * u * t * +pm[6] + t * t * +pm[8]
|
|
54675
|
+
];
|
|
54676
|
+
}
|
|
54677
|
+
if (pm?.[3] !== void 0)
|
|
54678
|
+
return [
|
|
54679
|
+
+pm[1] + (+pm[3] - +pm[1]) * t,
|
|
54680
|
+
+pm[2] + (+pm[4] - +pm[2]) * t
|
|
54681
|
+
];
|
|
54682
|
+
return [a.cx + dx * t, a.cy + dy * t];
|
|
54683
|
+
};
|
|
54406
54684
|
const candidates = [];
|
|
54407
54685
|
for (const off of [0, perp, -perp, 2 * perp, -2 * perp])
|
|
54408
|
-
for (const t of T_LIST)
|
|
54409
|
-
|
|
54410
|
-
|
|
54411
|
-
|
|
54412
|
-
]);
|
|
54686
|
+
for (const t of T_LIST) {
|
|
54687
|
+
const [bx, by] = pointAt(t);
|
|
54688
|
+
candidates.push([bx + nx * off, by + ny * off - 4]);
|
|
54689
|
+
}
|
|
54413
54690
|
const clean = ([cx2, cy2]) => {
|
|
54414
54691
|
const rect2 = { x: cx2 - w / 2, y: cy2 - labelH / 2, w, h: labelH };
|
|
54415
54692
|
if (rect2.x < 0 || rect2.x + w > width || rect2.y < 0 || rect2.y + rect2.h > height)
|
|
@@ -54419,10 +54696,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54419
54696
|
if (placedEdge.some((o) => rectsOverlap(o, rect2))) return false;
|
|
54420
54697
|
return true;
|
|
54421
54698
|
};
|
|
54422
|
-
const
|
|
54423
|
-
|
|
54424
|
-
a.cy + dy * 0.5 - 4
|
|
54425
|
-
];
|
|
54699
|
+
const mid = pointAt(0.5);
|
|
54700
|
+
const [cx, cy] = candidates.find(clean) ?? [mid[0], mid[1] - 4];
|
|
54426
54701
|
const style = labelOnFill(fillAt(cx, cy));
|
|
54427
54702
|
legs[i] = {
|
|
54428
54703
|
...lg,
|
|
@@ -54450,7 +54725,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54450
54725
|
const rawCountries = [];
|
|
54451
54726
|
for (const f of worldLayer.values()) {
|
|
54452
54727
|
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
54453
|
-
if (!iso || regionById.has(iso)) continue;
|
|
54728
|
+
if (!iso || regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54454
54729
|
let hasReferencedSub = false;
|
|
54455
54730
|
for (const k of regionById.keys())
|
|
54456
54731
|
if (k.startsWith(iso + "-")) {
|
|
@@ -54524,7 +54799,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54524
54799
|
);
|
|
54525
54800
|
if (usLayer && framedStateContainers) {
|
|
54526
54801
|
for (const [iso, f] of usLayer) {
|
|
54527
|
-
if (regionById.has(iso)) continue;
|
|
54802
|
+
if (regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54528
54803
|
const viewF = cullFeatureToView(f);
|
|
54529
54804
|
if (!viewF) continue;
|
|
54530
54805
|
const b = path.bounds(viewF);
|
|
@@ -54548,6 +54823,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54548
54823
|
waterBodies: data.waterBodies,
|
|
54549
54824
|
countries: countryCandidates,
|
|
54550
54825
|
palette,
|
|
54826
|
+
// One muted, contrast-floored tone shared with the orientation backdrop
|
|
54827
|
+
// (region pass) so a context country (Canada) and an orientation region
|
|
54828
|
+
// (Minnesota) read identically — never below the WCAG-AA floor, no fade.
|
|
54829
|
+
countryTone: backdropLandTone,
|
|
54830
|
+
waterTone: backdropWaterTone,
|
|
54551
54831
|
project,
|
|
54552
54832
|
collides,
|
|
54553
54833
|
contentPoints,
|
|
@@ -55102,19 +55382,60 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
55102
55382
|
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
55103
55383
|
};
|
|
55104
55384
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
55105
|
-
|
|
55106
|
-
const
|
|
55385
|
+
const legEnd = (d) => {
|
|
55386
|
+
const m = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
55387
|
+
d
|
|
55388
|
+
);
|
|
55389
|
+
if (!m) return null;
|
|
55390
|
+
const x0 = +m[1];
|
|
55391
|
+
const y0 = +m[2];
|
|
55392
|
+
let ex;
|
|
55393
|
+
let ey;
|
|
55394
|
+
let fromX;
|
|
55395
|
+
let fromY;
|
|
55396
|
+
if (m[3] !== void 0) {
|
|
55397
|
+
ex = +m[3];
|
|
55398
|
+
ey = +m[4];
|
|
55399
|
+
fromX = x0;
|
|
55400
|
+
fromY = y0;
|
|
55401
|
+
} else {
|
|
55402
|
+
ex = +m[7];
|
|
55403
|
+
ey = +m[8];
|
|
55404
|
+
fromX = +m[5];
|
|
55405
|
+
fromY = +m[6];
|
|
55406
|
+
}
|
|
55407
|
+
const dx = ex - fromX;
|
|
55408
|
+
const dy = ey - fromY;
|
|
55409
|
+
const len = Math.hypot(dx, dy);
|
|
55410
|
+
if (!(len > 0)) return null;
|
|
55411
|
+
return { ex, ey, tx: dx / len, ty: dy / len };
|
|
55412
|
+
};
|
|
55413
|
+
layout.legs.forEach((leg) => {
|
|
55414
|
+
const end = leg.arrow ? legEnd(leg.d) : null;
|
|
55415
|
+
let drawD = leg.d;
|
|
55416
|
+
let arrowTri = null;
|
|
55417
|
+
if (end) {
|
|
55418
|
+
const aLen = arrowSize(leg.width);
|
|
55419
|
+
const aHalf = Math.max(aLen * 0.5, leg.width * 0.6 + 1);
|
|
55420
|
+
const bx = end.ex - end.tx * aLen;
|
|
55421
|
+
const by = end.ey - end.ty * aLen;
|
|
55422
|
+
const px = -end.ty;
|
|
55423
|
+
const py = end.tx;
|
|
55424
|
+
arrowTri = `M${bx + px * aHalf},${by + py * aHalf}L${end.ex},${end.ey}L${bx - px * aHalf},${by - py * aHalf}z`;
|
|
55425
|
+
drawD = leg.d.replace(
|
|
55426
|
+
/(-?[\d.]+),(-?[\d.]+)$/,
|
|
55427
|
+
`${bx.toFixed(2)},${by.toFixed(2)}`
|
|
55428
|
+
);
|
|
55429
|
+
}
|
|
55430
|
+
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);
|
|
55107
55431
|
if (leg.tags) {
|
|
55108
55432
|
for (const [group, value] of Object.entries(leg.tags)) {
|
|
55109
55433
|
p.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
55110
55434
|
}
|
|
55111
55435
|
}
|
|
55112
55436
|
wireSync(p, leg.lineNumber);
|
|
55113
|
-
if (
|
|
55114
|
-
|
|
55115
|
-
const s = arrowSize(leg.width);
|
|
55116
|
-
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);
|
|
55117
|
-
p.attr("marker-end", `url(#${id})`);
|
|
55437
|
+
if (arrowTri) {
|
|
55438
|
+
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);
|
|
55118
55439
|
}
|
|
55119
55440
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
55120
55441
|
const lt = emitText(
|
|
@@ -58743,9 +59064,9 @@ var init_renderer20 = __esm({
|
|
|
58743
59064
|
}
|
|
58744
59065
|
});
|
|
58745
59066
|
|
|
58746
|
-
// node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js
|
|
59067
|
+
// ../../diagrammo/dgmo/node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js
|
|
58747
59068
|
var require_lz_string = __commonJS({
|
|
58748
|
-
"node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js"(exports2, module2) {
|
|
59069
|
+
"../../diagrammo/dgmo/node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js"(exports2, module2) {
|
|
58749
59070
|
"use strict";
|
|
58750
59071
|
var LZString = (function() {
|
|
58751
59072
|
var f = String.fromCharCode;
|