@diagrammo/dgmo 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/dgmo.md +17 -32
- package/.github/copilot-instructions.md +1 -1
- package/README.md +7 -1
- package/SKILL.md +1 -1
- package/dist/advanced.cjs +791 -614
- package/dist/advanced.d.cts +8 -51
- package/dist/advanced.d.ts +8 -51
- package/dist/advanced.js +791 -615
- package/dist/auto.cjs +793 -292
- package/dist/auto.js +95 -95
- package/dist/auto.mjs +793 -292
- package/dist/cli.cjs +145 -328
- package/dist/index.cjs +792 -291
- package/dist/index.js +792 -291
- package/docs/ai-integration.md +46 -26
- package/docs/language-reference.md +2 -2
- package/package.json +3 -3
- package/src/advanced.ts +0 -12
- package/src/boxes-and-lines/label-placement.ts +339 -0
- package/src/boxes-and-lines/layout-search.ts +16 -2
- package/src/boxes-and-lines/layout.ts +32 -7
- package/src/boxes-and-lines/renderer.ts +67 -139
- package/src/c4/parser.ts +15 -6
- package/src/chart.ts +1 -1
- package/src/class/parser.ts +6 -2
- package/src/cli-banner.ts +2 -3
- package/src/cli.ts +539 -1223
- package/src/cycle/parser.ts +4 -1
- package/src/dgmo-router.ts +16 -2
- package/src/diagnostics.ts +0 -0
- package/src/echarts.ts +5 -1
- package/src/er/parser.ts +5 -1
- package/src/gantt/parser.ts +13 -4
- package/src/graph/flowchart-parser.ts +5 -1
- package/src/graph/state-parser.ts +5 -1
- package/src/infra/parser.ts +10 -6
- package/src/journey-map/parser.ts +5 -2
- package/src/kanban/parser.ts +6 -2
- package/src/map/context-labels.ts +29 -19
- package/src/map/layout.ts +403 -108
- package/src/map/legend-band.ts +28 -0
- package/src/map/parser.ts +13 -4
- package/src/map/renderer.ts +74 -23
- package/src/map/resolver.ts +2 -2
- package/src/org/parser.ts +8 -2
- package/src/pyramid/parser.ts +4 -1
- package/src/raci/parser.ts +4 -1
- package/src/ring/parser.ts +4 -1
- package/src/sequence/parser.ts +6 -3
- package/src/sitemap/parser.ts +12 -3
- package/src/tech-radar/parser.ts +6 -3
- package/src/utils/parsing.ts +2 -2
- package/src/utils/tag-groups.ts +3 -3
- package/src/visualizations/parse.ts +9 -3
- package/src/migrate/data-comma.ts +0 -117
- package/src/migrate/embedded.ts +0 -238
- package/src/migrate/index.ts +0 -295
- package/src/migrate/line-classifier.ts +0 -128
- package/src/migrate/line-transformer.ts +0 -218
package/dist/index.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);
|
|
@@ -29044,63 +29151,6 @@ function ensureArrowMarkers(defs, colors) {
|
|
|
29044
29151
|
).attr("fill", color);
|
|
29045
29152
|
}
|
|
29046
29153
|
}
|
|
29047
|
-
function edgePolylineMidpoint(points) {
|
|
29048
|
-
if (points.length === 0) return { x: 0, y: 0 };
|
|
29049
|
-
if (points.length === 1) return { x: points[0].x, y: points[0].y };
|
|
29050
|
-
let total = 0;
|
|
29051
|
-
const segLen = [];
|
|
29052
|
-
for (let k = 1; k < points.length; k++) {
|
|
29053
|
-
const len = Math.hypot(
|
|
29054
|
-
points[k].x - points[k - 1].x,
|
|
29055
|
-
points[k].y - points[k - 1].y
|
|
29056
|
-
);
|
|
29057
|
-
segLen.push(len);
|
|
29058
|
-
total += len;
|
|
29059
|
-
}
|
|
29060
|
-
let half = total / 2;
|
|
29061
|
-
for (let k = 1; k < points.length; k++) {
|
|
29062
|
-
const len = segLen[k - 1];
|
|
29063
|
-
if (half <= len || k === points.length - 1) {
|
|
29064
|
-
const t = len === 0 ? 0 : Math.min(1, half / len);
|
|
29065
|
-
return {
|
|
29066
|
-
x: points[k - 1].x + (points[k].x - points[k - 1].x) * t,
|
|
29067
|
-
y: points[k - 1].y + (points[k].y - points[k - 1].y) * t
|
|
29068
|
-
};
|
|
29069
|
-
}
|
|
29070
|
-
half -= len;
|
|
29071
|
-
}
|
|
29072
|
-
const last = points[points.length - 1];
|
|
29073
|
-
return { x: last.x, y: last.y };
|
|
29074
|
-
}
|
|
29075
|
-
function resolveEdgeLabelOverlaps(labels) {
|
|
29076
|
-
const MAX_PASSES = 8;
|
|
29077
|
-
const PAD = 4;
|
|
29078
|
-
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
29079
|
-
let moved = false;
|
|
29080
|
-
for (let i = 0; i < labels.length; i++) {
|
|
29081
|
-
for (let j = i + 1; j < labels.length; j++) {
|
|
29082
|
-
const a = labels[i];
|
|
29083
|
-
const b = labels[j];
|
|
29084
|
-
const dx = Math.abs(a.x - b.x);
|
|
29085
|
-
const dy = Math.abs(a.y - b.y);
|
|
29086
|
-
const overlapX = (a.width + b.width) / 2 + PAD - dx;
|
|
29087
|
-
const overlapY = (a.height + b.height) / 2 + PAD - dy;
|
|
29088
|
-
if (overlapX > 0 && overlapY > 0) {
|
|
29089
|
-
const shift = overlapY / 2 + 1;
|
|
29090
|
-
if (a.y < b.y) {
|
|
29091
|
-
a.y -= shift;
|
|
29092
|
-
b.y += shift;
|
|
29093
|
-
} else {
|
|
29094
|
-
a.y += shift;
|
|
29095
|
-
b.y -= shift;
|
|
29096
|
-
}
|
|
29097
|
-
moved = true;
|
|
29098
|
-
}
|
|
29099
|
-
}
|
|
29100
|
-
}
|
|
29101
|
-
if (!moved) break;
|
|
29102
|
-
}
|
|
29103
|
-
}
|
|
29104
29154
|
function renderBoxesAndLines(container, parsed, layout, palette, isDark, options) {
|
|
29105
29155
|
const {
|
|
29106
29156
|
onClickItem,
|
|
@@ -29307,8 +29357,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29307
29357
|
groupG.append("text").attr("class", "bl-group-label").attr("x", gx + group.width / 2).attr("y", gy + 18).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sGroupLabelFontSize).attr("font-weight", "600").attr("fill", palette.text).text(group.label);
|
|
29308
29358
|
}
|
|
29309
29359
|
}
|
|
29310
|
-
const labelPositions = [];
|
|
29311
|
-
const edgeGroups = /* @__PURE__ */ new Map();
|
|
29312
29360
|
for (let i = 0; i < layout.edges.length; i++) {
|
|
29313
29361
|
const le = layout.edges[i];
|
|
29314
29362
|
const color = edgeColorMap.get(i) ?? palette.textMuted;
|
|
@@ -29327,7 +29375,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29327
29375
|
const nodeLayout = layoutNodeMap.get(le.source);
|
|
29328
29376
|
if (nodeLayout) {
|
|
29329
29377
|
const edgeG2 = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
|
|
29330
|
-
edgeGroups.set(i, edgeG2);
|
|
29331
29378
|
const markerId2 = `bl-arrow-${color.replace("#", "")}`;
|
|
29332
29379
|
const cx = nodeLayout.x;
|
|
29333
29380
|
const cy = nodeLayout.y;
|
|
@@ -29376,7 +29423,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29376
29423
|
}
|
|
29377
29424
|
if (points.length < 2) continue;
|
|
29378
29425
|
const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
|
|
29379
|
-
edgeGroups.set(i, edgeG);
|
|
29380
29426
|
const markerId = `bl-arrow-${color.replace("#", "")}`;
|
|
29381
29427
|
const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
|
|
29382
29428
|
const path = edgeG.append("path").attr("class", "bl-edge").attr("d", gen(points) ?? "").attr("fill", "none").attr("stroke", color).attr("stroke-width", sEdgeStrokeWidth).attr("marker-end", `url(#${markerId})`);
|
|
@@ -29384,31 +29430,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29384
29430
|
const revId = `bl-arrow-rev-${color.replace("#", "")}`;
|
|
29385
29431
|
path.attr("marker-start", `url(#${revId})`);
|
|
29386
29432
|
}
|
|
29387
|
-
if (le.label && le.points.length > 0) {
|
|
29388
|
-
const lw = measureText(le.label, sEdgeLabelFontSize);
|
|
29389
|
-
const labelH = sEdgeLabelFontSize + 6;
|
|
29390
|
-
const mid = edgePolylineMidpoint(le.points);
|
|
29391
|
-
let ly = mid.y;
|
|
29392
|
-
if (le.parallelCount > 1 && le.yOffset !== 0) {
|
|
29393
|
-
ly += (le.yOffset < 0 ? -labelH : labelH) * 0.5;
|
|
29394
|
-
}
|
|
29395
|
-
labelPositions.push({
|
|
29396
|
-
x: mid.x,
|
|
29397
|
-
y: ly,
|
|
29398
|
-
width: lw + 8,
|
|
29399
|
-
height: labelH,
|
|
29400
|
-
idx: i
|
|
29401
|
-
});
|
|
29402
|
-
}
|
|
29403
|
-
}
|
|
29404
|
-
resolveEdgeLabelOverlaps(labelPositions);
|
|
29405
|
-
for (const lp of labelPositions) {
|
|
29406
|
-
const le = layout.edges[lp.idx];
|
|
29407
|
-
if (!le.label) continue;
|
|
29408
|
-
const edgeG = edgeGroups.get(lp.idx);
|
|
29409
|
-
const target = edgeG ?? diagramG;
|
|
29410
|
-
target.append("rect").attr("x", lp.x - lp.width / 2).attr("y", lp.y - lp.height / 2).attr("width", lp.width).attr("height", lp.height).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
|
|
29411
|
-
target.append("text").attr("x", lp.x).attr("y", lp.y + sEdgeLabelFontSize / 3).attr("text-anchor", "middle").attr("font-size", sEdgeLabelFontSize).attr("fill", palette.textMuted).text(le.label);
|
|
29412
29433
|
}
|
|
29413
29434
|
for (const ln of layout.nodes) {
|
|
29414
29435
|
const node = nodeMap.get(ln.label);
|
|
@@ -29459,8 +29480,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29459
29480
|
nodeG.append("rect").attr("x", x).attr("y", y).attr("width", ln.width).attr("height", ln.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", colors.fill).attr("stroke", colors.stroke).attr("stroke-width", sNodeStrokeWidth);
|
|
29460
29481
|
const desc = node.description;
|
|
29461
29482
|
if (desc && desc.length > 0 && !hideDescriptions) {
|
|
29462
|
-
const
|
|
29463
|
-
const fitted = fitLabelToHeader(node.label, ln.width,
|
|
29483
|
+
const MAX_LABEL_LINES4 = 3;
|
|
29484
|
+
const fitted = fitLabelToHeader(node.label, ln.width, MAX_LABEL_LINES4);
|
|
29464
29485
|
const labelLines = fitted.lines;
|
|
29465
29486
|
const labelLineH = fitted.fontSize * 1.3;
|
|
29466
29487
|
const labelTotalH = labelLines.length * labelLineH;
|
|
@@ -29594,6 +29615,35 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
29594
29615
|
}
|
|
29595
29616
|
}
|
|
29596
29617
|
}
|
|
29618
|
+
const labelLayer = diagramG.append("g").attr("class", "bl-edge-labels");
|
|
29619
|
+
const labelLineHeight = sEdgeLabelFontSize * 1.3;
|
|
29620
|
+
for (const le of layout.edges) {
|
|
29621
|
+
if (!le.labelLines || le.labelLines.length === 0) continue;
|
|
29622
|
+
if (le.labelX === void 0 || le.labelY === void 0) continue;
|
|
29623
|
+
const lx = le.labelX;
|
|
29624
|
+
const ly = le.labelY;
|
|
29625
|
+
if (hidden.size > 0) {
|
|
29626
|
+
let isHidden = false;
|
|
29627
|
+
for (const [groupKey, hiddenVals] of hidden) {
|
|
29628
|
+
const val = le.metadata[groupKey];
|
|
29629
|
+
if (val && hiddenVals.has(val.toLowerCase())) {
|
|
29630
|
+
isHidden = true;
|
|
29631
|
+
break;
|
|
29632
|
+
}
|
|
29633
|
+
}
|
|
29634
|
+
if (isHidden) continue;
|
|
29635
|
+
}
|
|
29636
|
+
const lw = le.labelWidth ?? 0;
|
|
29637
|
+
const lh = le.labelHeight ?? sEdgeLabelFontSize + 6;
|
|
29638
|
+
const lines = le.labelLines;
|
|
29639
|
+
const labelG = labelLayer.append("g").attr("class", "bl-edge-label").attr("data-line-number", String(le.lineNumber));
|
|
29640
|
+
labelG.append("rect").attr("x", lx - lw / 2).attr("y", ly - lh / 2).attr("width", lw).attr("height", lh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.72);
|
|
29641
|
+
const text = labelG.append("text").attr("x", lx).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sEdgeLabelFontSize).attr("fill", palette.textMuted);
|
|
29642
|
+
const firstY = ly - (lines.length - 1) / 2 * labelLineHeight + sEdgeLabelFontSize / 3;
|
|
29643
|
+
lines.forEach((line11, k) => {
|
|
29644
|
+
text.append("tspan").attr("x", lx).attr("y", firstY + k * labelLineHeight).text(line11);
|
|
29645
|
+
});
|
|
29646
|
+
}
|
|
29597
29647
|
const hasDescriptions = parsed.nodes.some(
|
|
29598
29648
|
(n) => n.description && n.description.length > 0
|
|
29599
29649
|
);
|
|
@@ -29710,6 +29760,222 @@ var init_renderer6 = __esm({
|
|
|
29710
29760
|
}
|
|
29711
29761
|
});
|
|
29712
29762
|
|
|
29763
|
+
// src/boxes-and-lines/label-placement.ts
|
|
29764
|
+
function measureEdgeLabel(label, fontSize = EDGE_LABEL_FONT_SIZE6, maxWidth = LABEL_MAX_WIDTH) {
|
|
29765
|
+
let lines = wrapTextToWidth(label, fontSize, maxWidth, { hardBreak: true });
|
|
29766
|
+
if (lines.length > MAX_LABEL_LINES) {
|
|
29767
|
+
const kept = lines.slice(0, MAX_LABEL_LINES - 1);
|
|
29768
|
+
const rest = lines.slice(MAX_LABEL_LINES - 1).join(" ");
|
|
29769
|
+
kept.push(truncateText(rest, fontSize, maxWidth));
|
|
29770
|
+
lines = kept;
|
|
29771
|
+
}
|
|
29772
|
+
let textW = 0;
|
|
29773
|
+
for (const ln of lines) {
|
|
29774
|
+
const w = measureText(ln, fontSize);
|
|
29775
|
+
if (w > textW) textW = w;
|
|
29776
|
+
}
|
|
29777
|
+
return {
|
|
29778
|
+
lines,
|
|
29779
|
+
width: textW + 2 * H_PAD,
|
|
29780
|
+
height: lines.length * fontSize * LABEL_LINE_HEIGHT + 2 * V_PAD
|
|
29781
|
+
};
|
|
29782
|
+
}
|
|
29783
|
+
function edgePolylineMidpoint(points) {
|
|
29784
|
+
return pointAtArcFraction(points, 0.5).p;
|
|
29785
|
+
}
|
|
29786
|
+
function pointAtArcFraction(points, t) {
|
|
29787
|
+
if (points.length === 0) return { p: { x: 0, y: 0 }, nx: 0, ny: 1 };
|
|
29788
|
+
if (points.length === 1)
|
|
29789
|
+
return { p: { x: points[0].x, y: points[0].y }, nx: 0, ny: 1 };
|
|
29790
|
+
const segLen = [];
|
|
29791
|
+
let total = 0;
|
|
29792
|
+
for (let k = 1; k < points.length; k++) {
|
|
29793
|
+
const len = Math.hypot(
|
|
29794
|
+
points[k].x - points[k - 1].x,
|
|
29795
|
+
points[k].y - points[k - 1].y
|
|
29796
|
+
);
|
|
29797
|
+
segLen.push(len);
|
|
29798
|
+
total += len;
|
|
29799
|
+
}
|
|
29800
|
+
let want = total * Math.max(0, Math.min(1, t));
|
|
29801
|
+
for (let k = 1; k < points.length; k++) {
|
|
29802
|
+
const len = segLen[k - 1];
|
|
29803
|
+
if (want <= len || k === points.length - 1) {
|
|
29804
|
+
const f = len === 0 ? 0 : Math.min(1, want / len);
|
|
29805
|
+
const ax = points[k - 1].x;
|
|
29806
|
+
const ay = points[k - 1].y;
|
|
29807
|
+
const bx = points[k].x;
|
|
29808
|
+
const by = points[k].y;
|
|
29809
|
+
const dx = bx - ax;
|
|
29810
|
+
const dy = by - ay;
|
|
29811
|
+
const dlen = Math.hypot(dx, dy) || 1;
|
|
29812
|
+
return {
|
|
29813
|
+
p: { x: ax + dx * f, y: ay + dy * f },
|
|
29814
|
+
nx: -dy / dlen,
|
|
29815
|
+
ny: dx / dlen
|
|
29816
|
+
};
|
|
29817
|
+
}
|
|
29818
|
+
want -= len;
|
|
29819
|
+
}
|
|
29820
|
+
const last = points[points.length - 1];
|
|
29821
|
+
return { p: { x: last.x, y: last.y }, nx: 0, ny: 1 };
|
|
29822
|
+
}
|
|
29823
|
+
function rectFromCenter(cx, cy, w, h) {
|
|
29824
|
+
return {
|
|
29825
|
+
minX: cx - w / 2,
|
|
29826
|
+
minY: cy - h / 2,
|
|
29827
|
+
maxX: cx + w / 2,
|
|
29828
|
+
maxY: cy + h / 2
|
|
29829
|
+
};
|
|
29830
|
+
}
|
|
29831
|
+
function overlapsAny(cx, cy, w, h, obstacles, pad2) {
|
|
29832
|
+
const minX = cx - w / 2 - pad2;
|
|
29833
|
+
const minY = cy - h / 2 - pad2;
|
|
29834
|
+
const maxX = cx + w / 2 + pad2;
|
|
29835
|
+
const maxY = cy + h / 2 + pad2;
|
|
29836
|
+
for (const o of obstacles) {
|
|
29837
|
+
if (minX < o.maxX && maxX > o.minX && minY < o.maxY && maxY > o.minY)
|
|
29838
|
+
return true;
|
|
29839
|
+
}
|
|
29840
|
+
return false;
|
|
29841
|
+
}
|
|
29842
|
+
function slideFractions() {
|
|
29843
|
+
const out = [0.5];
|
|
29844
|
+
for (let k = 1; k <= SLIDE_SAMPLES; k++) {
|
|
29845
|
+
const off = k * 0.08;
|
|
29846
|
+
if (0.5 - off >= 0.12) out.push(0.5 - off);
|
|
29847
|
+
if (0.5 + off <= 0.88) out.push(0.5 + off);
|
|
29848
|
+
}
|
|
29849
|
+
return out;
|
|
29850
|
+
}
|
|
29851
|
+
function findClearPosition(w, h, points, obstacles) {
|
|
29852
|
+
const ts = slideFractions();
|
|
29853
|
+
for (const t of ts) {
|
|
29854
|
+
const { p } = pointAtArcFraction(points, t);
|
|
29855
|
+
if (!overlapsAny(p.x, p.y, w, h, obstacles, BOX_CLEAR_PAD)) return p;
|
|
29856
|
+
}
|
|
29857
|
+
for (let mag = PERP_STEP; mag <= PERP_MAX; mag += PERP_STEP) {
|
|
29858
|
+
for (const t of ts) {
|
|
29859
|
+
const { p, nx, ny } = pointAtArcFraction(points, t);
|
|
29860
|
+
for (const sign of [-1, 1]) {
|
|
29861
|
+
const x = p.x + nx * mag * sign;
|
|
29862
|
+
const y = p.y + ny * mag * sign;
|
|
29863
|
+
if (!overlapsAny(x, y, w, h, obstacles, BOX_CLEAR_PAD)) return { x, y };
|
|
29864
|
+
}
|
|
29865
|
+
}
|
|
29866
|
+
}
|
|
29867
|
+
return null;
|
|
29868
|
+
}
|
|
29869
|
+
function resolveLabelOverlaps(boxes) {
|
|
29870
|
+
const MAX_PASSES = 8;
|
|
29871
|
+
const PAD = 4;
|
|
29872
|
+
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
29873
|
+
let moved = false;
|
|
29874
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
29875
|
+
for (let j = i + 1; j < boxes.length; j++) {
|
|
29876
|
+
const a = boxes[i];
|
|
29877
|
+
const b = boxes[j];
|
|
29878
|
+
const dx = Math.abs(a.cx - b.cx);
|
|
29879
|
+
const dy = Math.abs(a.cy - b.cy);
|
|
29880
|
+
const overlapX = (a.w + b.w) / 2 + PAD - dx;
|
|
29881
|
+
const overlapY = (a.h + b.h) / 2 + PAD - dy;
|
|
29882
|
+
if (overlapX > 0 && overlapY > 0) {
|
|
29883
|
+
const shift = overlapY / 2 + 1;
|
|
29884
|
+
if (a.cy < b.cy) {
|
|
29885
|
+
a.cy -= shift;
|
|
29886
|
+
b.cy += shift;
|
|
29887
|
+
} else {
|
|
29888
|
+
a.cy += shift;
|
|
29889
|
+
b.cy -= shift;
|
|
29890
|
+
}
|
|
29891
|
+
moved = true;
|
|
29892
|
+
}
|
|
29893
|
+
}
|
|
29894
|
+
}
|
|
29895
|
+
if (!moved) break;
|
|
29896
|
+
}
|
|
29897
|
+
}
|
|
29898
|
+
function placeEdgeLabels(layout, opts) {
|
|
29899
|
+
const fontSize = opts?.fontSize ?? EDGE_LABEL_FONT_SIZE6;
|
|
29900
|
+
const maxWidth = opts?.maxWidth ?? LABEL_MAX_WIDTH;
|
|
29901
|
+
const obstacles = [];
|
|
29902
|
+
for (const n of layout.nodes)
|
|
29903
|
+
obstacles.push(rectFromCenter(n.x, n.y, n.width, n.height));
|
|
29904
|
+
for (const g of layout.groups)
|
|
29905
|
+
if (g.collapsed)
|
|
29906
|
+
obstacles.push(rectFromCenter(g.x, g.y, g.width, g.height));
|
|
29907
|
+
const boxes = [];
|
|
29908
|
+
layout.edges.forEach((e, idx) => {
|
|
29909
|
+
if (!e.label || e.points.length < 2) return;
|
|
29910
|
+
const m = measureEdgeLabel(e.label, fontSize, maxWidth);
|
|
29911
|
+
const mid = edgePolylineMidpoint(e.points);
|
|
29912
|
+
let cy = mid.y;
|
|
29913
|
+
if (e.parallelCount > 1 && e.yOffset !== 0)
|
|
29914
|
+
cy += (e.yOffset < 0 ? -1 : 1) * (m.height / 2);
|
|
29915
|
+
boxes.push({
|
|
29916
|
+
edgeIdx: idx,
|
|
29917
|
+
cx: mid.x,
|
|
29918
|
+
cy,
|
|
29919
|
+
w: m.width,
|
|
29920
|
+
h: m.height,
|
|
29921
|
+
lines: m.lines,
|
|
29922
|
+
resolved: true
|
|
29923
|
+
});
|
|
29924
|
+
});
|
|
29925
|
+
for (const box of boxes) {
|
|
29926
|
+
if (!overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
|
|
29927
|
+
continue;
|
|
29928
|
+
const e = layout.edges[box.edgeIdx];
|
|
29929
|
+
const clear = findClearPosition(box.w, box.h, e.points, obstacles);
|
|
29930
|
+
if (clear) {
|
|
29931
|
+
box.cx = clear.x;
|
|
29932
|
+
box.cy = clear.y;
|
|
29933
|
+
} else {
|
|
29934
|
+
box.resolved = false;
|
|
29935
|
+
}
|
|
29936
|
+
}
|
|
29937
|
+
resolveLabelOverlaps(boxes);
|
|
29938
|
+
for (const box of boxes) {
|
|
29939
|
+
if (overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
|
|
29940
|
+
box.resolved = false;
|
|
29941
|
+
}
|
|
29942
|
+
const boxByIdx = /* @__PURE__ */ new Map();
|
|
29943
|
+
for (const b of boxes) boxByIdx.set(b.edgeIdx, b);
|
|
29944
|
+
const edges = layout.edges.map((e, idx) => {
|
|
29945
|
+
const b = boxByIdx.get(idx);
|
|
29946
|
+
if (!b) return e;
|
|
29947
|
+
return {
|
|
29948
|
+
...e,
|
|
29949
|
+
labelX: b.cx,
|
|
29950
|
+
labelY: b.cy,
|
|
29951
|
+
labelWidth: b.w,
|
|
29952
|
+
labelHeight: b.h,
|
|
29953
|
+
labelLines: b.lines
|
|
29954
|
+
};
|
|
29955
|
+
});
|
|
29956
|
+
return {
|
|
29957
|
+
layout: { ...layout, edges },
|
|
29958
|
+
unresolved: boxes.filter((b) => !b.resolved).map((b) => b.edgeIdx)
|
|
29959
|
+
};
|
|
29960
|
+
}
|
|
29961
|
+
var EDGE_LABEL_FONT_SIZE6, LABEL_MAX_WIDTH, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, H_PAD, V_PAD, BOX_CLEAR_PAD, PERP_STEP, PERP_MAX, SLIDE_SAMPLES;
|
|
29962
|
+
var init_label_placement = __esm({
|
|
29963
|
+
"src/boxes-and-lines/label-placement.ts"() {
|
|
29964
|
+
"use strict";
|
|
29965
|
+
init_text_measure();
|
|
29966
|
+
EDGE_LABEL_FONT_SIZE6 = 11;
|
|
29967
|
+
LABEL_MAX_WIDTH = 160;
|
|
29968
|
+
MAX_LABEL_LINES = 3;
|
|
29969
|
+
LABEL_LINE_HEIGHT = 1.3;
|
|
29970
|
+
H_PAD = 6;
|
|
29971
|
+
V_PAD = 3;
|
|
29972
|
+
BOX_CLEAR_PAD = 4;
|
|
29973
|
+
PERP_STEP = 8;
|
|
29974
|
+
PERP_MAX = 40;
|
|
29975
|
+
SLIDE_SAMPLES = 9;
|
|
29976
|
+
}
|
|
29977
|
+
});
|
|
29978
|
+
|
|
29713
29979
|
// src/boxes-and-lines/layout-layered.ts
|
|
29714
29980
|
function rng(s) {
|
|
29715
29981
|
return () => {
|
|
@@ -31497,6 +31763,7 @@ function edgeLength(layout) {
|
|
|
31497
31763
|
}
|
|
31498
31764
|
async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
31499
31765
|
const hideDescriptions = opts?.hideDescriptions ?? false;
|
|
31766
|
+
const reserveEdgeLabels = opts?.reserveEdgeLabels ?? false;
|
|
31500
31767
|
const onProgress = opts?.onProgress;
|
|
31501
31768
|
const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
|
|
31502
31769
|
const collapsedGroupLabels = /* @__PURE__ */ new Set();
|
|
@@ -31789,8 +32056,14 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
|
31789
32056
|
for (const e of ord(parsed.edges)) {
|
|
31790
32057
|
const s = srcRep(e.source);
|
|
31791
32058
|
const t = tgtRep(e.target);
|
|
31792
|
-
if (s !== t && g.hasNode(s) && g.hasNode(t))
|
|
31793
|
-
|
|
32059
|
+
if (s !== t && g.hasNode(s) && g.hasNode(t)) {
|
|
32060
|
+
let edgeLabel = {};
|
|
32061
|
+
if (reserveEdgeLabels && e.label) {
|
|
32062
|
+
const m = measureEdgeLabel(e.label, EDGE_LABEL_FONT_SIZE6);
|
|
32063
|
+
edgeLabel = { width: m.width, height: m.height, labelpos: "c" };
|
|
32064
|
+
}
|
|
32065
|
+
g.setEdge(s, t, edgeLabel, edgeKey(e));
|
|
32066
|
+
}
|
|
31794
32067
|
}
|
|
31795
32068
|
import_dagre4.default.layout(g);
|
|
31796
32069
|
const nodes = parsed.nodes.map((n2) => {
|
|
@@ -32021,6 +32294,7 @@ var init_layout_search = __esm({
|
|
|
32021
32294
|
init_layout5();
|
|
32022
32295
|
init_layout_layered();
|
|
32023
32296
|
init_layout_grouped();
|
|
32297
|
+
init_label_placement();
|
|
32024
32298
|
DEFAULT_LAMBDA = 4;
|
|
32025
32299
|
ESCALATE_THRESHOLD = 4;
|
|
32026
32300
|
ESCALATE_MAX_N = 45;
|
|
@@ -32079,9 +32353,9 @@ function estimateLabelLines(label, nodeWidth = NODE_WIDTH) {
|
|
|
32079
32353
|
current = word;
|
|
32080
32354
|
}
|
|
32081
32355
|
}
|
|
32082
|
-
if (lines <=
|
|
32356
|
+
if (lines <= MAX_LABEL_LINES2) return Math.min(lines, MAX_LABEL_LINES2);
|
|
32083
32357
|
}
|
|
32084
|
-
return
|
|
32358
|
+
return MAX_LABEL_LINES2;
|
|
32085
32359
|
}
|
|
32086
32360
|
function computeNodeSize(node, reserveValueRow) {
|
|
32087
32361
|
if (!node.description || node.description.length === 0) {
|
|
@@ -32089,7 +32363,7 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
32089
32363
|
}
|
|
32090
32364
|
const w = DESC_NODE_WIDTH;
|
|
32091
32365
|
const labelLines = estimateLabelLines(node.label, w);
|
|
32092
|
-
const labelHeight = labelLines * 13 *
|
|
32366
|
+
const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT2 + LABEL_PAD;
|
|
32093
32367
|
const maxTextWidth = w - 24;
|
|
32094
32368
|
let totalRenderedLines = 0;
|
|
32095
32369
|
for (const line11 of node.description) {
|
|
@@ -32113,7 +32387,7 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
32113
32387
|
}
|
|
32114
32388
|
async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
32115
32389
|
const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
|
|
32116
|
-
const
|
|
32390
|
+
const searchOpts = {
|
|
32117
32391
|
...layoutOptions?.hideDescriptions !== void 0 && {
|
|
32118
32392
|
hideDescriptions: layoutOptions.hideDescriptions
|
|
32119
32393
|
},
|
|
@@ -32123,12 +32397,23 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
|
32123
32397
|
...layoutOptions?.onProgress !== void 0 && {
|
|
32124
32398
|
onProgress: layoutOptions.onProgress
|
|
32125
32399
|
}
|
|
32126
|
-
}
|
|
32127
|
-
|
|
32128
|
-
applyParallelEdgeOffsets(searched),
|
|
32400
|
+
};
|
|
32401
|
+
const searched = await layoutBoxesAndLinesSearch2(
|
|
32129
32402
|
parsed,
|
|
32130
|
-
|
|
32131
|
-
|
|
32403
|
+
collapseInfo,
|
|
32404
|
+
searchOpts
|
|
32405
|
+
);
|
|
32406
|
+
let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
|
|
32407
|
+
if (placed.unresolved.length > 0) {
|
|
32408
|
+
const relaid = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
|
|
32409
|
+
...searchOpts,
|
|
32410
|
+
reserveEdgeLabels: true
|
|
32411
|
+
});
|
|
32412
|
+
const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
|
|
32413
|
+
if (relaidPlaced.unresolved.length < placed.unresolved.length)
|
|
32414
|
+
placed = relaidPlaced;
|
|
32415
|
+
}
|
|
32416
|
+
return attachNotes(placed.layout, parsed, layoutOptions?.collapsedNotes);
|
|
32132
32417
|
}
|
|
32133
32418
|
function attachNotes(layout, parsed, collapsedNotes) {
|
|
32134
32419
|
const notesSuppressed = parsed.options?.["no-notes"] === "on";
|
|
@@ -32196,6 +32481,7 @@ function attachNotes(layout, parsed, collapsedNotes) {
|
|
|
32196
32481
|
points: e.points.map((pt) => ({ x: pt.x + shiftX, y: pt.y + shiftY })),
|
|
32197
32482
|
...e.labelX !== void 0 && { labelX: e.labelX + shiftX },
|
|
32198
32483
|
...e.labelY !== void 0 && { labelY: e.labelY + shiftY }
|
|
32484
|
+
// labelWidth/labelHeight/labelLines are shift-invariant — carried via spread.
|
|
32199
32485
|
})) : layout.edges;
|
|
32200
32486
|
const finalGroups = shifted ? layout.groups.map((grp) => ({
|
|
32201
32487
|
...grp,
|
|
@@ -32240,11 +32526,12 @@ function applyParallelEdgeOffsets(layout) {
|
|
|
32240
32526
|
}))
|
|
32241
32527
|
};
|
|
32242
32528
|
}
|
|
32243
|
-
var MARGIN5, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2,
|
|
32529
|
+
var MARGIN5, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES2, LABEL_LINE_HEIGHT2, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
|
|
32244
32530
|
var init_layout5 = __esm({
|
|
32245
32531
|
"src/boxes-and-lines/layout.ts"() {
|
|
32246
32532
|
"use strict";
|
|
32247
32533
|
init_text_measure();
|
|
32534
|
+
init_label_placement();
|
|
32248
32535
|
init_notes();
|
|
32249
32536
|
MARGIN5 = 40;
|
|
32250
32537
|
MAX_PARALLEL_EDGES = 5;
|
|
@@ -32258,8 +32545,8 @@ var init_layout5 = __esm({
|
|
|
32258
32545
|
DESC_PADDING = 8;
|
|
32259
32546
|
SEPARATOR_GAP4 = 4;
|
|
32260
32547
|
MAX_DESC_LINES2 = 6;
|
|
32261
|
-
|
|
32262
|
-
|
|
32548
|
+
MAX_LABEL_LINES2 = 3;
|
|
32549
|
+
LABEL_LINE_HEIGHT2 = 1.3;
|
|
32263
32550
|
LABEL_PAD = 12;
|
|
32264
32551
|
VALUE_ROW_FONT = 11;
|
|
32265
32552
|
VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
|
|
@@ -32278,7 +32565,7 @@ function tokenize(text) {
|
|
|
32278
32565
|
return tokens;
|
|
32279
32566
|
}
|
|
32280
32567
|
function tryWrap(tokens, maxWidth, fontSize, maxLines) {
|
|
32281
|
-
const availWidth = maxWidth -
|
|
32568
|
+
const availWidth = maxWidth - H_PAD2;
|
|
32282
32569
|
const lines = [];
|
|
32283
32570
|
let currentLine = "";
|
|
32284
32571
|
for (const token of tokens) {
|
|
@@ -32303,7 +32590,7 @@ function tryWrap(tokens, maxWidth, fontSize, maxLines) {
|
|
|
32303
32590
|
return lines;
|
|
32304
32591
|
}
|
|
32305
32592
|
function truncateLastLine(lines, maxWidth, fontSize) {
|
|
32306
|
-
const availWidth = maxWidth -
|
|
32593
|
+
const availWidth = maxWidth - H_PAD2;
|
|
32307
32594
|
const result = [...lines];
|
|
32308
32595
|
const last = result[result.length - 1];
|
|
32309
32596
|
if (measureText(last, fontSize) > availWidth) {
|
|
@@ -32311,7 +32598,7 @@ function truncateLastLine(lines, maxWidth, fontSize) {
|
|
|
32311
32598
|
}
|
|
32312
32599
|
return result;
|
|
32313
32600
|
}
|
|
32314
|
-
function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines =
|
|
32601
|
+
function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES3) {
|
|
32315
32602
|
if (!text) return { lines: [""], fontSize: baseFontSize };
|
|
32316
32603
|
const tokens = tokenize(text);
|
|
32317
32604
|
for (let fs = baseFontSize; fs >= minFontSize; fs--) {
|
|
@@ -32326,7 +32613,7 @@ function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABE
|
|
|
32326
32613
|
if (allLines.length > maxLines) {
|
|
32327
32614
|
const last = truncated[truncated.length - 1];
|
|
32328
32615
|
if (!last.endsWith("\u2026")) {
|
|
32329
|
-
const availWidth = maxWidth -
|
|
32616
|
+
const availWidth = maxWidth - H_PAD2;
|
|
32330
32617
|
if (measureText(last + "\u2026", minFontSize) > availWidth) {
|
|
32331
32618
|
truncated[truncated.length - 1] = truncateText(
|
|
32332
32619
|
last,
|
|
@@ -32353,7 +32640,7 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
|
|
|
32353
32640
|
nodeWidth,
|
|
32354
32641
|
baseFontSize,
|
|
32355
32642
|
MIN_FONT_SIZE,
|
|
32356
|
-
|
|
32643
|
+
MAX_LABEL_LINES3
|
|
32357
32644
|
);
|
|
32358
32645
|
const descLines = [];
|
|
32359
32646
|
let descFontSize = DESC_FONT_SIZE3;
|
|
@@ -32381,14 +32668,14 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
|
|
|
32381
32668
|
descFontSize
|
|
32382
32669
|
};
|
|
32383
32670
|
}
|
|
32384
|
-
var
|
|
32671
|
+
var H_PAD2, MAX_LABEL_LINES3, MAX_DESC_LINES3, ROOT_FONT_SIZE, MIN_FONT_SIZE, FONT_STEP, DESC_FONT_SIZE3;
|
|
32385
32672
|
var init_text_wrap = __esm({
|
|
32386
32673
|
"src/mindmap/text-wrap.ts"() {
|
|
32387
32674
|
"use strict";
|
|
32388
32675
|
init_description_helpers();
|
|
32389
32676
|
init_text_measure();
|
|
32390
|
-
|
|
32391
|
-
|
|
32677
|
+
H_PAD2 = 16;
|
|
32678
|
+
MAX_LABEL_LINES3 = 3;
|
|
32392
32679
|
MAX_DESC_LINES3 = 2;
|
|
32393
32680
|
ROOT_FONT_SIZE = 17;
|
|
32394
32681
|
MIN_FONT_SIZE = 9;
|
|
@@ -32775,7 +33062,7 @@ function nodeHeightS(node, hideDescriptions, sc) {
|
|
|
32775
33062
|
hideDescriptions
|
|
32776
33063
|
);
|
|
32777
33064
|
const labelLineCount = text.labelLines.length;
|
|
32778
|
-
const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT :
|
|
33065
|
+
const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT : LABEL_LINE_HEIGHT3 * labelLineCount;
|
|
32779
33066
|
let h = labelH + NODE_V_PAD;
|
|
32780
33067
|
if (text.descLines.length > 0) {
|
|
32781
33068
|
h += DESC_LINE_HEIGHT3 * text.descLines.length + 4;
|
|
@@ -32795,7 +33082,7 @@ function populateDepthCache(roots) {
|
|
|
32795
33082
|
};
|
|
32796
33083
|
walk(roots, 0);
|
|
32797
33084
|
}
|
|
32798
|
-
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT,
|
|
33085
|
+
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN6, MULTI_ROOT_GAP, nodeDepthCache;
|
|
32799
33086
|
var init_layout6 = __esm({
|
|
32800
33087
|
"src/mindmap/layout.ts"() {
|
|
32801
33088
|
"use strict";
|
|
@@ -32806,7 +33093,7 @@ var init_layout6 = __esm({
|
|
|
32806
33093
|
DEPTH1_WIDTH = 150;
|
|
32807
33094
|
LEAF_WIDTH = 120;
|
|
32808
33095
|
SINGLE_LABEL_HEIGHT = 28;
|
|
32809
|
-
|
|
33096
|
+
LABEL_LINE_HEIGHT3 = 18;
|
|
32810
33097
|
DESC_LINE_HEIGHT3 = 14;
|
|
32811
33098
|
NODE_V_PAD = 10;
|
|
32812
33099
|
H_GAP2 = 40;
|
|
@@ -33097,18 +33384,18 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
33097
33384
|
descFontSize
|
|
33098
33385
|
} = textLayout;
|
|
33099
33386
|
const labelLineCount = labelLines.length;
|
|
33100
|
-
const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 :
|
|
33387
|
+
const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 : LABEL_LINE_HEIGHT4 * labelLineCount;
|
|
33101
33388
|
const labelZoneHeight = showDesc ? labelZoneH : node.height;
|
|
33102
33389
|
const centerX = node.x + node.width / 2;
|
|
33103
33390
|
if (labelLineCount <= 1) {
|
|
33104
33391
|
const labelY = node.y + labelZoneHeight / 2 + fontSize * 0.35;
|
|
33105
33392
|
nodeG.append("text").attr("x", centerX).attr("y", labelY).attr("text-anchor", "middle").attr("font-size", fontSize).attr("font-weight", isRoot ? "bold" : "normal").attr("fill", onFillText).text(labelLines[0]);
|
|
33106
33393
|
} else {
|
|
33107
|
-
const blockH =
|
|
33394
|
+
const blockH = LABEL_LINE_HEIGHT4 * (labelLineCount - 1);
|
|
33108
33395
|
const firstBaselineY = node.y + labelZoneHeight / 2 - blockH / 2 + fontSize * 0.35;
|
|
33109
33396
|
const textEl = nodeG.append("text").attr("x", centerX).attr("text-anchor", "middle").attr("font-size", fontSize).attr("font-weight", isRoot ? "bold" : "normal").attr("fill", onFillText);
|
|
33110
33397
|
for (let i = 0; i < labelLines.length; i++) {
|
|
33111
|
-
textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i *
|
|
33398
|
+
textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i * LABEL_LINE_HEIGHT4).text(labelLines[i]);
|
|
33112
33399
|
}
|
|
33113
33400
|
}
|
|
33114
33401
|
if (labelLines.length > 1 || labelLines[0] !== node.label) {
|
|
@@ -33183,7 +33470,7 @@ function renderMindmapForExport(content, theme, palette) {
|
|
|
33183
33470
|
return extractExportSvg(container, theme);
|
|
33184
33471
|
});
|
|
33185
33472
|
}
|
|
33186
|
-
var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2,
|
|
33473
|
+
var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT4, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
|
|
33187
33474
|
var init_renderer7 = __esm({
|
|
33188
33475
|
"src/mindmap/renderer.ts"() {
|
|
33189
33476
|
"use strict";
|
|
@@ -33206,7 +33493,7 @@ var init_renderer7 = __esm({
|
|
|
33206
33493
|
DIAGRAM_PADDING7 = 20;
|
|
33207
33494
|
TITLE_HEIGHT4 = 30;
|
|
33208
33495
|
SINGLE_LABEL_HEIGHT2 = 28;
|
|
33209
|
-
|
|
33496
|
+
LABEL_LINE_HEIGHT4 = 18;
|
|
33210
33497
|
DESC_LINE_HEIGHT4 = 14;
|
|
33211
33498
|
NODE_RADIUS = 6;
|
|
33212
33499
|
ROOT_STROKE_WIDTH = 2.5;
|
|
@@ -35873,7 +36160,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
35873
36160
|
const labelText = edge.label ?? "";
|
|
35874
36161
|
const techText = edge.technology ? `[${edge.technology}]` : "";
|
|
35875
36162
|
const textW = Math.max(
|
|
35876
|
-
measureText(labelText,
|
|
36163
|
+
measureText(labelText, EDGE_LABEL_FONT_SIZE7),
|
|
35877
36164
|
measureText(techText, TECH_FONT_SIZE)
|
|
35878
36165
|
);
|
|
35879
36166
|
const bgW = textW + 12;
|
|
@@ -35884,7 +36171,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
35884
36171
|
textY = midPt.y - 4;
|
|
35885
36172
|
}
|
|
35886
36173
|
if (labelText) {
|
|
35887
|
-
edgeG.append("text").attr("x", midPt.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size",
|
|
36174
|
+
edgeG.append("text").attr("x", midPt.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("class", "c4-edge-label").text(labelText);
|
|
35888
36175
|
}
|
|
35889
36176
|
if (techText) {
|
|
35890
36177
|
edgeG.append("text").attr("x", midPt.x).attr("y", labelText ? textY + 18 : textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", TECH_FONT_SIZE).attr("font-style", "italic").attr("class", "c4-edge-tech").text(techText);
|
|
@@ -36068,7 +36355,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
|
|
|
36068
36355
|
const labelText = edge.label ?? "";
|
|
36069
36356
|
const techText = edge.technology ? `[${edge.technology}]` : "";
|
|
36070
36357
|
const textW = Math.max(
|
|
36071
|
-
measureText(labelText,
|
|
36358
|
+
measureText(labelText, EDGE_LABEL_FONT_SIZE7),
|
|
36072
36359
|
measureText(techText, TECH_FONT_SIZE)
|
|
36073
36360
|
);
|
|
36074
36361
|
const bgW = textW + 12;
|
|
@@ -36086,7 +36373,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
|
|
|
36086
36373
|
});
|
|
36087
36374
|
}
|
|
36088
36375
|
}
|
|
36089
|
-
|
|
36376
|
+
placeEdgeLabels2(pendingLabels, edges, obstacleRects);
|
|
36090
36377
|
for (const lbl of pendingLabels) {
|
|
36091
36378
|
lbl.edgeG.append("rect").attr("x", lbl.x - lbl.bgW / 2).attr("y", lbl.y - lbl.bgH / 2).attr("width", lbl.bgW).attr("height", lbl.bgH).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.9).attr("class", "c4-edge-label-bg");
|
|
36092
36379
|
let textY = lbl.y;
|
|
@@ -36094,7 +36381,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
|
|
|
36094
36381
|
textY = lbl.y - 4;
|
|
36095
36382
|
}
|
|
36096
36383
|
if (lbl.labelText) {
|
|
36097
|
-
lbl.edgeG.append("text").attr("x", lbl.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size",
|
|
36384
|
+
lbl.edgeG.append("text").attr("x", lbl.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("class", "c4-edge-label").text(lbl.labelText);
|
|
36098
36385
|
}
|
|
36099
36386
|
if (lbl.techText) {
|
|
36100
36387
|
lbl.edgeG.append("text").attr("x", lbl.x).attr("y", lbl.labelText ? textY + 18 : textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", TECH_FONT_SIZE).attr("font-style", "italic").attr("class", "c4-edge-tech").text(lbl.techText);
|
|
@@ -36175,7 +36462,7 @@ function tangentAt(points, t) {
|
|
|
36175
36462
|
}
|
|
36176
36463
|
return { x: 0, y: 1 };
|
|
36177
36464
|
}
|
|
36178
|
-
function
|
|
36465
|
+
function placeEdgeLabels2(labels, edges, obstacleRects) {
|
|
36179
36466
|
if (labels.length === 0) return;
|
|
36180
36467
|
const allPaths = edges.map((e) => e.points);
|
|
36181
36468
|
const placedRects = [];
|
|
@@ -36713,7 +37000,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
|
|
|
36713
37000
|
document.body.removeChild(el);
|
|
36714
37001
|
}
|
|
36715
37002
|
}
|
|
36716
|
-
var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6,
|
|
37003
|
+
var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE7, TECH_FONT_SIZE, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
|
|
36717
37004
|
var init_renderer9 = __esm({
|
|
36718
37005
|
"src/c4/renderer.ts"() {
|
|
36719
37006
|
"use strict";
|
|
@@ -36738,7 +37025,7 @@ var init_renderer9 = __esm({
|
|
|
36738
37025
|
NAME_FONT_SIZE2 = 14;
|
|
36739
37026
|
DESC_FONT_SIZE5 = 11;
|
|
36740
37027
|
DESC_LINE_HEIGHT6 = 16;
|
|
36741
|
-
|
|
37028
|
+
EDGE_LABEL_FONT_SIZE7 = 11;
|
|
36742
37029
|
TECH_FONT_SIZE = 10;
|
|
36743
37030
|
CARD_H_PAD4 = 20;
|
|
36744
37031
|
CARD_V_PAD4 = 14;
|
|
@@ -37362,7 +37649,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
|
|
|
37362
37649
|
const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
|
|
37363
37650
|
const sTitleY = ctx.structural(TITLE_Y);
|
|
37364
37651
|
const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
|
|
37365
|
-
const sEdgeLabelFontSize = ctx.text(
|
|
37652
|
+
const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE8);
|
|
37366
37653
|
const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
|
|
37367
37654
|
const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
|
|
37368
37655
|
const sArrowheadW = ctx.structural(ARROWHEAD_W3);
|
|
@@ -37588,7 +37875,7 @@ function renderFlowchartForExport(content, theme, palette) {
|
|
|
37588
37875
|
document.body.removeChild(container);
|
|
37589
37876
|
}
|
|
37590
37877
|
}
|
|
37591
|
-
var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2,
|
|
37878
|
+
var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE8, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
|
|
37592
37879
|
var init_flowchart_renderer = __esm({
|
|
37593
37880
|
"src/graph/flowchart-renderer.ts"() {
|
|
37594
37881
|
"use strict";
|
|
@@ -37608,7 +37895,7 @@ var init_flowchart_renderer = __esm({
|
|
|
37608
37895
|
DIAGRAM_PADDING9 = 20;
|
|
37609
37896
|
MAX_SCALE6 = 3;
|
|
37610
37897
|
NODE_FONT_SIZE2 = 13;
|
|
37611
|
-
|
|
37898
|
+
EDGE_LABEL_FONT_SIZE8 = 11;
|
|
37612
37899
|
ARROWHEAD_W3 = 10;
|
|
37613
37900
|
ARROWHEAD_H3 = 7;
|
|
37614
37901
|
IO_SKEW = 15;
|
|
@@ -39042,7 +39329,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
39042
39329
|
const midIdx = Math.floor(edge.points.length / 2);
|
|
39043
39330
|
const midPt = edge.points[midIdx];
|
|
39044
39331
|
if (midPt) {
|
|
39045
|
-
const halfWidth = (measureText(edge.label,
|
|
39332
|
+
const halfWidth = (measureText(edge.label, EDGE_LABEL_FONT_SIZE9) + 8) / 2;
|
|
39046
39333
|
if (midPt.x - halfWidth < minX) minX = midPt.x - halfWidth;
|
|
39047
39334
|
if (midPt.x + halfWidth > maxX) maxX = midPt.x + halfWidth;
|
|
39048
39335
|
}
|
|
@@ -39076,7 +39363,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
39076
39363
|
height: totalHeight
|
|
39077
39364
|
};
|
|
39078
39365
|
}
|
|
39079
|
-
var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5,
|
|
39366
|
+
var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP2;
|
|
39080
39367
|
var init_layout10 = __esm({
|
|
39081
39368
|
"src/infra/layout.ts"() {
|
|
39082
39369
|
"use strict";
|
|
@@ -39091,7 +39378,7 @@ var init_layout10 = __esm({
|
|
|
39091
39378
|
COLLAPSE_BAR_HEIGHT3 = 6;
|
|
39092
39379
|
NODE_FONT_SIZE3 = 13;
|
|
39093
39380
|
META_FONT_SIZE5 = 10;
|
|
39094
|
-
|
|
39381
|
+
EDGE_LABEL_FONT_SIZE9 = 11;
|
|
39095
39382
|
PADDING_X3 = 24;
|
|
39096
39383
|
GROUP_PADDING2 = 20;
|
|
39097
39384
|
GROUP_HEADER_HEIGHT = 24;
|
|
@@ -39196,7 +39483,7 @@ function buildScaledConstants(ctx) {
|
|
|
39196
39483
|
sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
|
|
39197
39484
|
sMetaFontSize: ctx.text(META_FONT_SIZE6),
|
|
39198
39485
|
sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
|
|
39199
|
-
sEdgeLabelFontSize: ctx.text(
|
|
39486
|
+
sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE10),
|
|
39200
39487
|
sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
|
|
39201
39488
|
sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
|
|
39202
39489
|
sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
|
|
@@ -39925,7 +40212,7 @@ function renderEdgeLabels(svg, edges, nodes, groups, palette, _isDark, animate,
|
|
|
39925
40212
|
if (!midPt) continue;
|
|
39926
40213
|
const labelText = edge.label;
|
|
39927
40214
|
const g = svg.append("g").attr("class", animate ? "infra-edge-label" : "");
|
|
39928
|
-
const textWidth = measureText(labelText,
|
|
40215
|
+
const textWidth = measureText(labelText, EDGE_LABEL_FONT_SIZE10) + 8;
|
|
39929
40216
|
g.append("rect").attr("x", midPt.x - textWidth / 2).attr("y", midPt.y - 8).attr("width", textWidth).attr("height", 16).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.9);
|
|
39930
40217
|
g.append("text").attr("x", midPt.x).attr("y", midPt.y + 4).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sEdgeLabelFontSize).attr("fill", palette.textMuted).text(labelText);
|
|
39931
40218
|
if (animate) {
|
|
@@ -40535,7 +40822,7 @@ function parseAndLayoutInfra(content) {
|
|
|
40535
40822
|
const layout = layoutInfra(computed);
|
|
40536
40823
|
return { parsed, computed, layout };
|
|
40537
40824
|
}
|
|
40538
|
-
var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6,
|
|
40825
|
+
var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
|
|
40539
40826
|
var init_renderer10 = __esm({
|
|
40540
40827
|
"src/infra/renderer.ts"() {
|
|
40541
40828
|
"use strict";
|
|
@@ -40561,7 +40848,7 @@ var init_renderer10 = __esm({
|
|
|
40561
40848
|
NODE_FONT_SIZE4 = 13;
|
|
40562
40849
|
META_FONT_SIZE6 = 10;
|
|
40563
40850
|
META_LINE_HEIGHT6 = 14;
|
|
40564
|
-
|
|
40851
|
+
EDGE_LABEL_FONT_SIZE10 = 11;
|
|
40565
40852
|
GROUP_LABEL_FONT_SIZE2 = 14;
|
|
40566
40853
|
NODE_BORDER_RADIUS = 8;
|
|
40567
40854
|
OVERLOAD_STROKE_WIDTH = 3;
|
|
@@ -46949,7 +47236,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
|
|
|
46949
47236
|
const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
|
|
46950
47237
|
const sTitleY = ctx.structural(TITLE_Y);
|
|
46951
47238
|
const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
|
|
46952
|
-
const sEdgeLabelFontSize = ctx.text(
|
|
47239
|
+
const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE11);
|
|
46953
47240
|
const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
|
|
46954
47241
|
const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
|
|
46955
47242
|
const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
|
|
@@ -47229,7 +47516,7 @@ function renderStateForExport(content, theme, palette) {
|
|
|
47229
47516
|
document.body.removeChild(container);
|
|
47230
47517
|
}
|
|
47231
47518
|
}
|
|
47232
|
-
var d3Selection19, DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6,
|
|
47519
|
+
var d3Selection19, DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, GROUP_LABEL_FONT_SIZE3, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
|
|
47233
47520
|
var init_state_renderer = __esm({
|
|
47234
47521
|
"src/graph/state-renderer.ts"() {
|
|
47235
47522
|
"use strict";
|
|
@@ -47249,7 +47536,7 @@ var init_state_renderer = __esm({
|
|
|
47249
47536
|
DIAGRAM_PADDING12 = 20;
|
|
47250
47537
|
MAX_SCALE7 = 3;
|
|
47251
47538
|
NODE_FONT_SIZE6 = 13;
|
|
47252
|
-
|
|
47539
|
+
EDGE_LABEL_FONT_SIZE11 = 11;
|
|
47253
47540
|
GROUP_LABEL_FONT_SIZE3 = 11;
|
|
47254
47541
|
ARROWHEAD_W5 = 10;
|
|
47255
47542
|
ARROWHEAD_H5 = 7;
|
|
@@ -50071,10 +50358,10 @@ function renderedDescNodeHeight(numLines, scale) {
|
|
|
50071
50358
|
return headerH + textBlockH + bottomPad;
|
|
50072
50359
|
}
|
|
50073
50360
|
function wrapEdgeLabelText(label, description, maxWidth = EDGE_LABEL_MAX_WIDTH) {
|
|
50074
|
-
const labelLines = label ? wrapTextToWidth(label,
|
|
50361
|
+
const labelLines = label ? wrapTextToWidth(label, EDGE_LABEL_FONT_SIZE12, maxWidth) : [];
|
|
50075
50362
|
const descLines = [];
|
|
50076
50363
|
for (const d of description) {
|
|
50077
|
-
descLines.push(...wrapTextToWidth(d,
|
|
50364
|
+
descLines.push(...wrapTextToWidth(d, EDGE_LABEL_FONT_SIZE12, maxWidth));
|
|
50078
50365
|
}
|
|
50079
50366
|
return { labelLines, descLines };
|
|
50080
50367
|
}
|
|
@@ -50212,9 +50499,9 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
|
|
|
50212
50499
|
const lineCount = labelLines.length + descLines.length;
|
|
50213
50500
|
let labelPxW = 0;
|
|
50214
50501
|
for (const l of labelLines)
|
|
50215
|
-
labelPxW = Math.max(labelPxW, measureText(l,
|
|
50502
|
+
labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50216
50503
|
for (const l of descLines)
|
|
50217
|
-
labelPxW = Math.max(labelPxW, measureText(l,
|
|
50504
|
+
labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50218
50505
|
const { labelX, labelY, labelAngle } = computeEdgeLabelPosition(
|
|
50219
50506
|
midAngle,
|
|
50220
50507
|
radius,
|
|
@@ -50316,9 +50603,9 @@ function fitToCanvas(nodes, edges, parsed, _cx, _cy, radius, width, height, _isC
|
|
|
50316
50603
|
);
|
|
50317
50604
|
let textWidth = 0;
|
|
50318
50605
|
for (const l of labelLines)
|
|
50319
|
-
textWidth = Math.max(textWidth, measureText(l,
|
|
50606
|
+
textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50320
50607
|
for (const l of descLines)
|
|
50321
|
-
textWidth = Math.max(textWidth, measureText(l,
|
|
50608
|
+
textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
|
|
50322
50609
|
if (textWidth === 0) continue;
|
|
50323
50610
|
const normAngle = (le.labelAngle % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
|
50324
50611
|
const isRight = normAngle < Math.PI * 0.4 || normAngle > Math.PI * 1.6;
|
|
@@ -50392,7 +50679,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
|
|
|
50392
50679
|
const midAngle = srcExitAngle + dir * visibleSweep / 2;
|
|
50393
50680
|
return { path, midAngle };
|
|
50394
50681
|
}
|
|
50395
|
-
var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6,
|
|
50682
|
+
var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE12, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT4, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
|
|
50396
50683
|
var init_layout14 = __esm({
|
|
50397
50684
|
"src/cycle/layout.ts"() {
|
|
50398
50685
|
"use strict";
|
|
@@ -50403,7 +50690,7 @@ var init_layout14 = __esm({
|
|
|
50403
50690
|
LABEL_FONT_SIZE4 = 13;
|
|
50404
50691
|
CIRCLE_LABEL_FONT_SIZE = 16;
|
|
50405
50692
|
DESC_FONT_SIZE6 = 11;
|
|
50406
|
-
|
|
50693
|
+
EDGE_LABEL_FONT_SIZE12 = 11;
|
|
50407
50694
|
MIN_NODE_WIDTH4 = 70;
|
|
50408
50695
|
MAX_NODE_WIDTH3 = 260;
|
|
50409
50696
|
DESC_MIN_WIDTH = 140;
|
|
@@ -50548,7 +50835,7 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
50548
50835
|
const scaledNodeFont = Math.max(6, Math.round(NODE_FONT_SIZE7 * layout.scale));
|
|
50549
50836
|
const scaledEdgeLabelFont = Math.max(
|
|
50550
50837
|
6,
|
|
50551
|
-
Math.round(
|
|
50838
|
+
Math.round(EDGE_LABEL_FONT_SIZE13 * layout.scale)
|
|
50552
50839
|
);
|
|
50553
50840
|
const CIRCLE_LABEL_FONT_SIZE2 = 16;
|
|
50554
50841
|
const scaledCircleLabelFont = Math.max(
|
|
@@ -50711,7 +50998,7 @@ function ensureArrowMarker(defs, color, strokeWidth) {
|
|
|
50711
50998
|
const mh = Math.max(1.5, mw * 0.5);
|
|
50712
50999
|
defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${mw} ${mh}`).attr("refX", mw * 0.1).attr("refY", mh / 2).attr("markerWidth", mw).attr("markerHeight", mh).attr("orient", "auto").append("polygon").attr("points", `0,0 ${mw},${mh / 2} 0,${mh}`).attr("fill", color);
|
|
50713
51000
|
}
|
|
50714
|
-
var d3Selection22, NODE_FONT_SIZE7, DESC_FONT_SIZE7,
|
|
51001
|
+
var d3Selection22, NODE_FONT_SIZE7, DESC_FONT_SIZE7, EDGE_LABEL_FONT_SIZE13, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
|
|
50715
51002
|
var init_renderer15 = __esm({
|
|
50716
51003
|
"src/cycle/renderer.ts"() {
|
|
50717
51004
|
"use strict";
|
|
@@ -50729,7 +51016,7 @@ var init_renderer15 = __esm({
|
|
|
50729
51016
|
init_text_measure();
|
|
50730
51017
|
NODE_FONT_SIZE7 = 13;
|
|
50731
51018
|
DESC_FONT_SIZE7 = 11;
|
|
50732
|
-
|
|
51019
|
+
EDGE_LABEL_FONT_SIZE13 = 11;
|
|
50733
51020
|
DESC_LINE_HEIGHT8 = 15;
|
|
50734
51021
|
TITLE_AREA_HEIGHT = 50;
|
|
50735
51022
|
}
|
|
@@ -51127,7 +51414,7 @@ function resolveMap(parsed, data) {
|
|
|
51127
51414
|
const hint = suggest(r.name, allNames);
|
|
51128
51415
|
err(
|
|
51129
51416
|
r.lineNumber,
|
|
51130
|
-
`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.`,
|
|
51131
51418
|
"E_MAP_UNKNOWN_SUBDIVISION"
|
|
51132
51419
|
);
|
|
51133
51420
|
continue;
|
|
@@ -51207,7 +51494,7 @@ function resolveMap(parsed, data) {
|
|
|
51207
51494
|
const hint = suggest(name, cityNames);
|
|
51208
51495
|
err(
|
|
51209
51496
|
line11,
|
|
51210
|
-
`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>\`.`,
|
|
51211
51498
|
"E_MAP_UNKNOWN_PLACE"
|
|
51212
51499
|
);
|
|
51213
51500
|
return { kind: "miss" };
|
|
@@ -51749,6 +52036,21 @@ function mapLegendBand(legend, opts) {
|
|
|
51749
52036
|
if (height <= 0) return 0;
|
|
51750
52037
|
return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
|
|
51751
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
|
+
}
|
|
51752
52054
|
var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
|
|
51753
52055
|
var init_legend_band = __esm({
|
|
51754
52056
|
"src/map/legend-band.ts"() {
|
|
@@ -51873,6 +52175,8 @@ function placeContextLabels(args) {
|
|
|
51873
52175
|
waterBodies,
|
|
51874
52176
|
countries,
|
|
51875
52177
|
palette,
|
|
52178
|
+
countryTone,
|
|
52179
|
+
waterTone,
|
|
51876
52180
|
project,
|
|
51877
52181
|
collides,
|
|
51878
52182
|
contentPoints,
|
|
@@ -51882,8 +52186,8 @@ function placeContextLabels(args) {
|
|
|
51882
52186
|
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
51883
52187
|
const budget = labelBudget(width, height, band);
|
|
51884
52188
|
if (budget <= 0) return [];
|
|
51885
|
-
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
51886
|
-
const countryColor = palette.textMuted;
|
|
52189
|
+
const waterColor = waterTone ?? mix(palette.colors.blue, palette.textMuted, 50);
|
|
52190
|
+
const countryColor = countryTone ?? palette.textMuted;
|
|
51887
52191
|
const haloColor = palette.bg;
|
|
51888
52192
|
const candidates = [];
|
|
51889
52193
|
const center = [width / 2, height / 2];
|
|
@@ -51972,8 +52276,7 @@ function placeContextLabels(args) {
|
|
|
51972
52276
|
)
|
|
51973
52277
|
);
|
|
51974
52278
|
const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
|
|
51975
|
-
const
|
|
51976
|
-
const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
|
|
52279
|
+
const color = countryColor;
|
|
51977
52280
|
const text = c.name;
|
|
51978
52281
|
const tw = labelWidth(text, 0, fontSize);
|
|
51979
52282
|
if (tw > w || fontSize + 2 * PADY > h) continue;
|
|
@@ -52067,7 +52370,7 @@ function placeContextLabels(args) {
|
|
|
52067
52370
|
}
|
|
52068
52371
|
return placed;
|
|
52069
52372
|
}
|
|
52070
|
-
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;
|
|
52071
52374
|
var init_context_labels = __esm({
|
|
52072
52375
|
"src/map/context-labels.ts"() {
|
|
52073
52376
|
"use strict";
|
|
@@ -52084,7 +52387,6 @@ var init_context_labels = __esm({
|
|
|
52084
52387
|
COUNTRY_FONT_MAX = 22;
|
|
52085
52388
|
COUNTRY_SIZE_FRAC_MIN = 0.06;
|
|
52086
52389
|
COUNTRY_SIZE_FRAC_MAX = 0.32;
|
|
52087
|
-
COUNTRY_FADE_MAX = 45;
|
|
52088
52390
|
MAX_COUNTRY_POSITIONS = 4;
|
|
52089
52391
|
COUNTRY_POS_GRID = 5;
|
|
52090
52392
|
COUNTRY_POS_TOPN_MARGIN = 3;
|
|
@@ -52211,36 +52513,23 @@ function countryLabelPositions(args) {
|
|
|
52211
52513
|
return n;
|
|
52212
52514
|
};
|
|
52213
52515
|
const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
|
|
52214
|
-
const
|
|
52215
|
-
|
|
52216
|
-
|
|
52217
|
-
|
|
52218
|
-
|
|
52219
|
-
|
|
52220
|
-
|
|
52221
|
-
let bestIdx = 0;
|
|
52222
|
-
let bestMin = -1;
|
|
52223
|
-
for (let k = 0; k < pool.length; k++) {
|
|
52224
|
-
const c = pool[k];
|
|
52225
|
-
let minD = Infinity;
|
|
52226
|
-
for (const o of ordered) {
|
|
52227
|
-
const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
|
|
52228
|
-
if (d < minD) minD = d;
|
|
52229
|
-
}
|
|
52230
|
-
if (minD > bestMin) {
|
|
52231
|
-
bestMin = minD;
|
|
52232
|
-
bestIdx = k;
|
|
52233
|
-
}
|
|
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]]
|
|
52234
52523
|
}
|
|
52235
|
-
|
|
52236
|
-
|
|
52237
|
-
|
|
52524
|
+
] : [];
|
|
52525
|
+
const grid = [...kept].sort(
|
|
52526
|
+
(a, b) => interiorness(b) - interiorness(a) || dist2ToCentre(a) - dist2ToCentre(b)
|
|
52527
|
+
).map((c) => ({
|
|
52238
52528
|
lonLat: [c.lon, c.lat],
|
|
52239
52529
|
screen: [c.sx, c.sy]
|
|
52240
52530
|
}));
|
|
52241
52531
|
const curatedPos = curated ? mkCurated(curated, project) : [];
|
|
52242
|
-
|
|
52243
|
-
return out;
|
|
52532
|
+
return [...curatedPos, ...lead, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
|
|
52244
52533
|
}
|
|
52245
52534
|
function mkCurated(curated, project) {
|
|
52246
52535
|
if (!curated) return [];
|
|
@@ -52506,6 +52795,25 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52506
52795
|
palette.bg,
|
|
52507
52796
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
52508
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
|
+
);
|
|
52509
52817
|
const hasDirectColor = resolved.regions.some((r) => r.color !== void 0);
|
|
52510
52818
|
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && !hasDirectColor && fillGroupNames.size === 0;
|
|
52511
52819
|
const colorByIso = /* @__PURE__ */ new Map();
|
|
@@ -52620,6 +52928,33 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
52620
52928
|
hasSubtitle: Boolean(resolved.subtitle)
|
|
52621
52929
|
});
|
|
52622
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
|
+
});
|
|
52623
52958
|
const reserve = opts._calloutReserve;
|
|
52624
52959
|
const fitLeft = FIT_PAD + (reserve?.left ?? 0);
|
|
52625
52960
|
const fitRight = width - FIT_PAD - (reserve?.right ?? 0);
|
|
@@ -53349,6 +53684,19 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53349
53684
|
const by = b.cy - (b.cy - py) / tb * trimB;
|
|
53350
53685
|
return `M${ax},${ay}Q${px},${py} ${bx},${by}`;
|
|
53351
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
|
+
};
|
|
53352
53700
|
const routeLegVals = resolved.routes.flatMap((rt) => rt.legs).map((l) => Number(l.value)).filter((n) => Number.isFinite(n) && n > 0);
|
|
53353
53701
|
const rlMin = routeLegVals.length ? Math.min(...routeLegVals) : 0;
|
|
53354
53702
|
const rlMax = routeLegVals.length ? Math.max(...routeLegVals) : 0;
|
|
@@ -53362,13 +53710,12 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53362
53710
|
const a = poiScreen.get(leg.fromId);
|
|
53363
53711
|
const b = poiScreen.get(leg.toId);
|
|
53364
53712
|
if (!a || !b) continue;
|
|
53365
|
-
const
|
|
53366
|
-
const my = (a.cy + b.cy) / 2;
|
|
53713
|
+
const lp = legLabelPoint(a, b, leg.style === "arc", 0);
|
|
53367
53714
|
const bow = {
|
|
53368
53715
|
curved: leg.style === "arc",
|
|
53369
53716
|
offset: 0,
|
|
53370
|
-
labelX:
|
|
53371
|
-
labelY:
|
|
53717
|
+
labelX: lp.x,
|
|
53718
|
+
labelY: lp.y
|
|
53372
53719
|
};
|
|
53373
53720
|
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53374
53721
|
legs.push({
|
|
@@ -53415,13 +53762,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53415
53762
|
const b = poiScreen.get(e.toId);
|
|
53416
53763
|
if (!a || !b) return;
|
|
53417
53764
|
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
53418
|
-
const
|
|
53419
|
-
const
|
|
53765
|
+
const curved = e.style === "arc" || n > 1;
|
|
53766
|
+
const lp = legLabelPoint(a, b, curved, fanOffset);
|
|
53420
53767
|
const bow = {
|
|
53421
|
-
curved
|
|
53768
|
+
curved,
|
|
53422
53769
|
offset: fanOffset,
|
|
53423
|
-
labelX:
|
|
53424
|
-
labelY:
|
|
53770
|
+
labelX: lp.x,
|
|
53771
|
+
labelY: lp.y
|
|
53425
53772
|
};
|
|
53426
53773
|
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53427
53774
|
legs.push({
|
|
@@ -53445,7 +53792,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53445
53792
|
});
|
|
53446
53793
|
}
|
|
53447
53794
|
const labels = [];
|
|
53448
|
-
const obstacles = [];
|
|
53795
|
+
const obstacles = [...topReserved];
|
|
53449
53796
|
const regionLabelGuards = [];
|
|
53450
53797
|
const markers = pois.map((p) => ({
|
|
53451
53798
|
cx: p.cx,
|
|
@@ -53483,6 +53830,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53483
53830
|
}
|
|
53484
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));
|
|
53485
53832
|
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
53833
|
+
const labeledRegionIds = /* @__PURE__ */ new Set();
|
|
53486
53834
|
const showRegionValues = resolved.directives.noRegionValue !== true && activeIsScore;
|
|
53487
53835
|
const regionValueStr = (value) => showRegionValues && value !== void 0 ? compactNumber(value) : void 0;
|
|
53488
53836
|
const isCompact = width < COMPACT_WIDTH_PX;
|
|
@@ -53503,7 +53851,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53503
53851
|
const REGION_FONT_MAX_ORIENT = 22;
|
|
53504
53852
|
const REGION_SIZE_FRAC_MIN = 0.06;
|
|
53505
53853
|
const REGION_SIZE_FRAC_MAX = 0.32;
|
|
53506
|
-
const
|
|
53854
|
+
const SUBJECT_FONT_MAX = 18;
|
|
53855
|
+
const SUBJECT_MIN_PROMINENCE = 13;
|
|
53856
|
+
const SUBJECT_LEADER_FONT = 15;
|
|
53507
53857
|
const canvasLinear = Math.sqrt(Math.max(1, width * height));
|
|
53508
53858
|
const sizeT = (boxW, boxH) => {
|
|
53509
53859
|
const frac = Math.sqrt(Math.max(0, boxW * boxH)) / canvasLinear;
|
|
@@ -53515,9 +53865,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53515
53865
|
)
|
|
53516
53866
|
);
|
|
53517
53867
|
};
|
|
53518
|
-
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2,
|
|
53868
|
+
const pushRegionLabel = (x, y, text, fill2, lineNumber, valueLine, fontSize = FONT2, colorOverride) => {
|
|
53519
53869
|
const { color: baseColor, haloColor } = labelOnFill(fill2);
|
|
53520
|
-
const color =
|
|
53870
|
+
const color = colorOverride ?? baseColor;
|
|
53521
53871
|
const vf = Math.round(fontSize * 0.82);
|
|
53522
53872
|
const halfW = Math.max(
|
|
53523
53873
|
measureLegendText(text, fontSize),
|
|
@@ -53532,7 +53882,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53532
53882
|
text,
|
|
53533
53883
|
anchor: "middle",
|
|
53534
53884
|
color,
|
|
53535
|
-
|
|
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,
|
|
53536
53890
|
haloColor,
|
|
53537
53891
|
...fontSize !== FONT2 && { fontSize },
|
|
53538
53892
|
...valueLine !== void 0 && { valueLine },
|
|
@@ -53568,7 +53922,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53568
53922
|
if (!c || !Number.isFinite(c[0])) return null;
|
|
53569
53923
|
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
53570
53924
|
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
53571
|
-
const placedRegionRects = [];
|
|
53925
|
+
const placedRegionRects = [...topReserved];
|
|
53572
53926
|
const placedLeaders = [];
|
|
53573
53927
|
const isEmptyFill = (f) => f === water || f === neutralFill || f === foreignFill;
|
|
53574
53928
|
const POI_LABEL_PAD = 14;
|
|
@@ -53581,9 +53935,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53581
53935
|
const SHORT_HOP_MAX = Math.max(46, Math.min(width, height) * 0.11);
|
|
53582
53936
|
const STEP = 4;
|
|
53583
53937
|
const HOP_GAP = 9;
|
|
53584
|
-
const tryShortHopCallout = (cx, cy, fill2, name, value) => {
|
|
53585
|
-
const chipW = stackW(name, value);
|
|
53586
|
-
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);
|
|
53587
53941
|
const dirs = [
|
|
53588
53942
|
[0, -1],
|
|
53589
53943
|
// north (over Canada)
|
|
@@ -53599,7 +53953,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53599
53953
|
let ex = cx;
|
|
53600
53954
|
let ey = cy;
|
|
53601
53955
|
let steps = 0;
|
|
53602
|
-
const maxSteps = Math.ceil(
|
|
53956
|
+
const maxSteps = Math.ceil(maxLen / STEP) + 1;
|
|
53603
53957
|
while (steps < maxSteps && fillAt(ex, ey) === fill2) {
|
|
53604
53958
|
ex += dx * STEP;
|
|
53605
53959
|
ey += dy * STEP;
|
|
@@ -53607,8 +53961,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53607
53961
|
}
|
|
53608
53962
|
if (fillAt(ex, ey) === fill2) continue;
|
|
53609
53963
|
const halfAlong = (Math.abs(dx) * chipW + Math.abs(dy) * chipH) / 2;
|
|
53610
|
-
const ccx = ex + dx * (
|
|
53611
|
-
const ccy = ey + dy * (
|
|
53964
|
+
const ccx = ex + dx * (gap + halfAlong);
|
|
53965
|
+
const ccy = ey + dy * (gap + halfAlong);
|
|
53612
53966
|
const rect = {
|
|
53613
53967
|
x: ccx - chipW / 2,
|
|
53614
53968
|
y: ccy - chipH / 2,
|
|
@@ -53633,7 +53987,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53633
53987
|
const innerX = ccx - dx * (chipW / 2);
|
|
53634
53988
|
const innerY = ccy - dy * (chipH / 2);
|
|
53635
53989
|
const len = Math.hypot(innerX - cx, innerY - cy);
|
|
53636
|
-
if (len >
|
|
53990
|
+
if (len > maxLen) continue;
|
|
53637
53991
|
if (placedLeaders.some(
|
|
53638
53992
|
(l) => segmentsCross(cx, cy, innerX, innerY, l[0], l[1], l[2], l[3])
|
|
53639
53993
|
))
|
|
@@ -53657,6 +54011,78 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53657
54011
|
const valStr = regionValueStr(r.value);
|
|
53658
54012
|
const fitsRegions = (rect) => !placedRegionRects.some((p) => rectsOverlap(rect, p));
|
|
53659
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
|
+
}
|
|
53660
54086
|
const seekAnchors = [
|
|
53661
54087
|
{ x: c[0], y: c[1], guard: false }
|
|
53662
54088
|
];
|
|
@@ -53679,13 +54105,13 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53679
54105
|
const nameRect = regionLabelRect(a.x, a.y, t);
|
|
53680
54106
|
if (valStr && stackW(t, valStr) <= boxW && stackH(true) <= boxH) {
|
|
53681
54107
|
const stackRect = regionLabelRect(a.x, a.y, t, valStr);
|
|
53682
|
-
if (fitsRegions(stackRect) && fitsPois(nameRect)) {
|
|
54108
|
+
if (fitsRegions(stackRect) && fitsPois(nameRect) && extremesOnFill(a.x, a.y, t)) {
|
|
53683
54109
|
chosen = { text: t, valueLine: valStr, ax: a.x, ay: a.y };
|
|
53684
54110
|
break;
|
|
53685
54111
|
}
|
|
53686
54112
|
}
|
|
53687
54113
|
if (labelW(t) <= boxW && labelH <= boxH) {
|
|
53688
|
-
if (fitsRegions(nameRect) && fitsPois(nameRect)) {
|
|
54114
|
+
if (fitsRegions(nameRect) && fitsPois(nameRect) && (valStr === void 0 || extremesOnFill(a.x, a.y, t))) {
|
|
53689
54115
|
chosen = { text: t, ax: a.x, ay: a.y };
|
|
53690
54116
|
break;
|
|
53691
54117
|
}
|
|
@@ -53693,7 +54119,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53693
54119
|
}
|
|
53694
54120
|
if (chosen) break;
|
|
53695
54121
|
}
|
|
53696
|
-
if (chosen === void 0 &&
|
|
54122
|
+
if (chosen === void 0 && r.label !== void 0 && isSubject) {
|
|
53697
54123
|
const hop = tryShortHopCallout(c[0], c[1], r.fill, r.label, valStr);
|
|
53698
54124
|
if (hop) {
|
|
53699
54125
|
placedRegionRects.push(hop.rect);
|
|
@@ -53705,9 +54131,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53705
54131
|
text: r.label,
|
|
53706
54132
|
anchor: "middle",
|
|
53707
54133
|
color: palette.text,
|
|
53708
|
-
halo:
|
|
54134
|
+
halo: false,
|
|
53709
54135
|
haloColor: palette.bg,
|
|
53710
|
-
valueLine: valStr,
|
|
54136
|
+
...valStr !== void 0 && { valueLine: valStr },
|
|
53711
54137
|
leader: {
|
|
53712
54138
|
x1: hop.leader[0],
|
|
53713
54139
|
y1: hop.leader[1],
|
|
@@ -53718,20 +54144,28 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53718
54144
|
calloutDot: { x: c[0], y: c[1], color: dark },
|
|
53719
54145
|
lineNumber: r.lineNumber
|
|
53720
54146
|
});
|
|
54147
|
+
labeledRegionIds.add(r.id);
|
|
53721
54148
|
}
|
|
53722
54149
|
continue;
|
|
53723
54150
|
}
|
|
53724
54151
|
if (chosen === void 0) continue;
|
|
53725
54152
|
const isOrient = r.value === void 0 && r.layer === "base";
|
|
53726
54153
|
let font = FONT2;
|
|
53727
|
-
let fade = 0;
|
|
53728
54154
|
if (isOrient) {
|
|
53729
54155
|
const growT = sizeT(boxW, boxH);
|
|
53730
54156
|
const desiredFont = Math.round(
|
|
53731
54157
|
FONT2 + growT * (REGION_FONT_MAX_ORIENT - FONT2)
|
|
53732
54158
|
);
|
|
53733
54159
|
const hasVal = chosen.valueLine !== void 0;
|
|
53734
|
-
|
|
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--) {
|
|
53735
54169
|
if (stackW(chosen.text, chosen.valueLine, f) > boxW || stackH(hasVal, f) > boxH)
|
|
53736
54170
|
continue;
|
|
53737
54171
|
const gRect = regionLabelRect(
|
|
@@ -53748,14 +54182,18 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53748
54182
|
void 0,
|
|
53749
54183
|
f
|
|
53750
54184
|
);
|
|
53751
|
-
if (!fitsRegions(gRect) || !fitsPois(gName))
|
|
53752
|
-
const halfW = measureLegendText(chosen.text, f) / 2;
|
|
53753
|
-
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))
|
|
53754
54186
|
continue;
|
|
53755
|
-
|
|
54187
|
+
if (!extremesClean(f)) continue;
|
|
54188
|
+
fit = f;
|
|
53756
54189
|
break;
|
|
53757
54190
|
}
|
|
53758
|
-
|
|
54191
|
+
if (fit < 0) {
|
|
54192
|
+
if (!frameContainers.has(r.id)) continue;
|
|
54193
|
+
font = FONT2;
|
|
54194
|
+
} else {
|
|
54195
|
+
font = fit;
|
|
54196
|
+
}
|
|
53759
54197
|
}
|
|
53760
54198
|
const rRect = regionLabelRect(
|
|
53761
54199
|
chosen.ax,
|
|
@@ -53773,8 +54211,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53773
54211
|
r.lineNumber,
|
|
53774
54212
|
chosen.valueLine,
|
|
53775
54213
|
font,
|
|
53776
|
-
|
|
54214
|
+
isOrient ? backdropLandTone : void 0
|
|
53777
54215
|
);
|
|
54216
|
+
labeledRegionIds.add(r.id);
|
|
53778
54217
|
regionLabelGuards.push({
|
|
53779
54218
|
label: labels[labels.length - 1],
|
|
53780
54219
|
rect: rRect
|
|
@@ -53792,6 +54231,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53792
54231
|
seed.lineNumber,
|
|
53793
54232
|
valStr
|
|
53794
54233
|
);
|
|
54234
|
+
labeledRegionIds.add(seed.iso);
|
|
53795
54235
|
regionLabelGuards.push({
|
|
53796
54236
|
label: labels[labels.length - 1],
|
|
53797
54237
|
rect: regionLabelRect(seed.x, seed.y, text, valStr)
|
|
@@ -54223,13 +54663,30 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54223
54663
|
const nx = -dy / len;
|
|
54224
54664
|
const ny = dx / len;
|
|
54225
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
|
+
};
|
|
54226
54684
|
const candidates = [];
|
|
54227
54685
|
for (const off of [0, perp, -perp, 2 * perp, -2 * perp])
|
|
54228
|
-
for (const t of T_LIST)
|
|
54229
|
-
|
|
54230
|
-
|
|
54231
|
-
|
|
54232
|
-
]);
|
|
54686
|
+
for (const t of T_LIST) {
|
|
54687
|
+
const [bx, by] = pointAt(t);
|
|
54688
|
+
candidates.push([bx + nx * off, by + ny * off - 4]);
|
|
54689
|
+
}
|
|
54233
54690
|
const clean = ([cx2, cy2]) => {
|
|
54234
54691
|
const rect2 = { x: cx2 - w / 2, y: cy2 - labelH / 2, w, h: labelH };
|
|
54235
54692
|
if (rect2.x < 0 || rect2.x + w > width || rect2.y < 0 || rect2.y + rect2.h > height)
|
|
@@ -54239,10 +54696,8 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54239
54696
|
if (placedEdge.some((o) => rectsOverlap(o, rect2))) return false;
|
|
54240
54697
|
return true;
|
|
54241
54698
|
};
|
|
54242
|
-
const
|
|
54243
|
-
|
|
54244
|
-
a.cy + dy * 0.5 - 4
|
|
54245
|
-
];
|
|
54699
|
+
const mid = pointAt(0.5);
|
|
54700
|
+
const [cx, cy] = candidates.find(clean) ?? [mid[0], mid[1] - 4];
|
|
54246
54701
|
const style = labelOnFill(fillAt(cx, cy));
|
|
54247
54702
|
legs[i] = {
|
|
54248
54703
|
...lg,
|
|
@@ -54270,7 +54725,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54270
54725
|
const rawCountries = [];
|
|
54271
54726
|
for (const f of worldLayer.values()) {
|
|
54272
54727
|
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
54273
|
-
if (!iso || regionById.has(iso)) continue;
|
|
54728
|
+
if (!iso || regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54274
54729
|
let hasReferencedSub = false;
|
|
54275
54730
|
for (const k of regionById.keys())
|
|
54276
54731
|
if (k.startsWith(iso + "-")) {
|
|
@@ -54344,7 +54799,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54344
54799
|
);
|
|
54345
54800
|
if (usLayer && framedStateContainers) {
|
|
54346
54801
|
for (const [iso, f] of usLayer) {
|
|
54347
|
-
if (regionById.has(iso)) continue;
|
|
54802
|
+
if (regionById.has(iso) || labeledRegionIds.has(iso)) continue;
|
|
54348
54803
|
const viewF = cullFeatureToView(f);
|
|
54349
54804
|
if (!viewF) continue;
|
|
54350
54805
|
const b = path.bounds(viewF);
|
|
@@ -54368,6 +54823,11 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54368
54823
|
waterBodies: data.waterBodies,
|
|
54369
54824
|
countries: countryCandidates,
|
|
54370
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,
|
|
54371
54831
|
project,
|
|
54372
54832
|
collides,
|
|
54373
54833
|
contentPoints,
|
|
@@ -54922,19 +55382,60 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
54922
55382
|
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
54923
55383
|
};
|
|
54924
55384
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
54925
|
-
|
|
54926
|
-
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);
|
|
54927
55431
|
if (leg.tags) {
|
|
54928
55432
|
for (const [group, value] of Object.entries(leg.tags)) {
|
|
54929
55433
|
p.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
54930
55434
|
}
|
|
54931
55435
|
}
|
|
54932
55436
|
wireSync(p, leg.lineNumber);
|
|
54933
|
-
if (
|
|
54934
|
-
|
|
54935
|
-
const s = arrowSize(leg.width);
|
|
54936
|
-
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);
|
|
54937
|
-
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);
|
|
54938
55439
|
}
|
|
54939
55440
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
54940
55441
|
const lt = emitText(
|
|
@@ -58563,9 +59064,9 @@ var init_renderer20 = __esm({
|
|
|
58563
59064
|
}
|
|
58564
59065
|
});
|
|
58565
59066
|
|
|
58566
|
-
// 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
|
|
58567
59068
|
var require_lz_string = __commonJS({
|
|
58568
|
-
"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) {
|
|
58569
59070
|
"use strict";
|
|
58570
59071
|
var LZString = (function() {
|
|
58571
59072
|
var f = String.fromCharCode;
|