@diagrammo/dgmo 0.36.0 → 0.37.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/.cursorrules +2 -0
- package/.github/copilot-instructions.md +2 -0
- package/.windsurfrules +2 -0
- package/SKILL.md +2 -0
- package/dist/advanced.cjs +139 -54
- package/dist/advanced.d.cts +12 -7
- package/dist/advanced.d.ts +12 -7
- package/dist/advanced.js +139 -54
- package/dist/auto.cjs +140 -55
- package/dist/auto.js +96 -96
- package/dist/auto.mjs +140 -55
- package/dist/cli.cjs +107 -107
- package/dist/index.cjs +139 -54
- package/dist/index.js +139 -54
- package/docs/ai-integration.md +2 -0
- package/docs/language-reference.md +11 -7
- package/gallery/fixtures/map-coastline.dgmo +2 -2
- package/gallery/fixtures/map-route.dgmo +5 -5
- package/package.json +1 -1
- package/src/infra/parser.ts +95 -38
- package/src/map/layout.ts +91 -7
- package/src/map/parser.ts +51 -29
- package/src/map/renderer.ts +10 -0
- package/src/map/types.ts +9 -8
package/.cursorrules
CHANGED
|
@@ -112,6 +112,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
112
112
|
|
|
113
113
|
**Need more than the index gives you?** Fetch the per-type section: MCP `get_language_reference(type)` / `get_examples(type)`, or read that type's section below. The `suggest_chart_type` tool returns the chosen type's section automatically.
|
|
114
114
|
|
|
115
|
+
**When the type isn't obvious, ask — don't guess.** Call `suggest_chart_type` first. If it returns an `⚠️ ASK THE USER` directive (the request is ambiguous between candidates, or nothing matched), present those candidate options to the user and wait for their pick before generating — never silently choose. A confident result (high/medium) you can proceed with.
|
|
116
|
+
|
|
115
117
|
### Common examples (curated, parse-clean)
|
|
116
118
|
|
|
117
119
|
_The most common types, inline so you can generate them without a fetch. For the other 37, get the per-type section (see below)._
|
|
@@ -112,6 +112,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
112
112
|
|
|
113
113
|
**Need more than the index gives you?** Fetch the per-type section: MCP `get_language_reference(type)` / `get_examples(type)`, or read that type's section below. The `suggest_chart_type` tool returns the chosen type's section automatically.
|
|
114
114
|
|
|
115
|
+
**When the type isn't obvious, ask — don't guess.** Call `suggest_chart_type` first. If it returns an `⚠️ ASK THE USER` directive (the request is ambiguous between candidates, or nothing matched), present those candidate options to the user and wait for their pick before generating — never silently choose. A confident result (high/medium) you can proceed with.
|
|
116
|
+
|
|
115
117
|
### Common examples (curated, parse-clean)
|
|
116
118
|
|
|
117
119
|
_The most common types, inline so you can generate them without a fetch. For the other 37, get the per-type section (see below)._
|
package/.windsurfrules
CHANGED
|
@@ -112,6 +112,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
112
112
|
|
|
113
113
|
**Need more than the index gives you?** Fetch the per-type section: MCP `get_language_reference(type)` / `get_examples(type)`, or read that type's section below. The `suggest_chart_type` tool returns the chosen type's section automatically.
|
|
114
114
|
|
|
115
|
+
**When the type isn't obvious, ask — don't guess.** Call `suggest_chart_type` first. If it returns an `⚠️ ASK THE USER` directive (the request is ambiguous between candidates, or nothing matched), present those candidate options to the user and wait for their pick before generating — never silently choose. A confident result (high/medium) you can proceed with.
|
|
116
|
+
|
|
115
117
|
### Common examples (curated, parse-clean)
|
|
116
118
|
|
|
117
119
|
_The most common types, inline so you can generate them without a fetch. For the other 37, get the per-type section (see below)._
|
package/SKILL.md
CHANGED
|
@@ -117,6 +117,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
117
117
|
|
|
118
118
|
**Need more than the index gives you?** Fetch the per-type section: MCP `get_language_reference(type)` / `get_examples(type)`, or read that type's section below. The `suggest_chart_type` tool returns the chosen type's section automatically.
|
|
119
119
|
|
|
120
|
+
**When the type isn't obvious, ask — don't guess.** Call `suggest_chart_type` first. If it returns an `⚠️ ASK THE USER` directive (the request is ambiguous between candidates, or nothing matched), present those candidate options to the user and wait for their pick before generating — never silently choose. A confident result (high/medium) you can proceed with.
|
|
121
|
+
|
|
120
122
|
### Common examples (curated, parse-clean)
|
|
121
123
|
|
|
122
124
|
_The most common types, inline so you can generate them without a fetch. For the other 37, get the per-type section (see below)._
|
package/dist/advanced.cjs
CHANGED
|
@@ -16714,6 +16714,7 @@ function parseInfra(content) {
|
|
|
16714
16714
|
let currentGroup = null;
|
|
16715
16715
|
let currentTagGroup = null;
|
|
16716
16716
|
let baseIndent = 0;
|
|
16717
|
+
let firstLineConsumed = false;
|
|
16717
16718
|
function finishCurrentNode() {
|
|
16718
16719
|
if (!currentNode) return;
|
|
16719
16720
|
const key = currentNode.id;
|
|
@@ -16721,7 +16722,8 @@ function parseInfra(content) {
|
|
|
16721
16722
|
if (existing) {
|
|
16722
16723
|
const incomingDisplay = displayName(currentNode.label);
|
|
16723
16724
|
const existingDisplay = displayName(existing.label);
|
|
16724
|
-
|
|
16725
|
+
const isAliasRef = nameAliasMap.get(currentNode.label) === key;
|
|
16726
|
+
if (incomingDisplay !== existingDisplay && !isAliasRef) {
|
|
16725
16727
|
result.diagnostics.push(
|
|
16726
16728
|
makeDgmoError(
|
|
16727
16729
|
currentNode.lineNumber,
|
|
@@ -16787,19 +16789,22 @@ function parseInfra(content) {
|
|
|
16787
16789
|
if (indent === 0 && currentNode && !trimmed.startsWith("-")) {
|
|
16788
16790
|
finishCurrentNode();
|
|
16789
16791
|
}
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16792
|
+
if (!firstLineConsumed) {
|
|
16793
|
+
firstLineConsumed = true;
|
|
16794
|
+
const firstLineResult = parseFirstLine(trimmed);
|
|
16795
|
+
if (firstLineResult) {
|
|
16796
|
+
if (firstLineResult.chartType !== "infra") {
|
|
16797
|
+
setError(
|
|
16798
|
+
lineNumber,
|
|
16799
|
+
`Expected chart type 'infra', got '${firstLineResult.chartType}'`
|
|
16800
|
+
);
|
|
16801
|
+
}
|
|
16802
|
+
if (firstLineResult.title) {
|
|
16803
|
+
result.title = firstLineResult.title;
|
|
16804
|
+
result.titleLineNumber = lineNumber;
|
|
16805
|
+
}
|
|
16806
|
+
continue;
|
|
16801
16807
|
}
|
|
16802
|
-
continue;
|
|
16803
16808
|
}
|
|
16804
16809
|
if (/^direction-tb$/i.test(trimmed)) {
|
|
16805
16810
|
result.direction = "TB";
|
|
@@ -16865,18 +16870,20 @@ function parseInfra(content) {
|
|
|
16865
16870
|
if (compMatch) {
|
|
16866
16871
|
finishCurrentNode();
|
|
16867
16872
|
finishCurrentTagGroup();
|
|
16868
|
-
const
|
|
16873
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
16874
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
16869
16875
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
16870
16876
|
const name = peeled.label;
|
|
16871
|
-
const
|
|
16877
|
+
const alias = asAlias ?? peeled.alias;
|
|
16878
|
+
const rest = meta ?? "";
|
|
16872
16879
|
const { tags } = extractPipeMetadata(rest);
|
|
16873
16880
|
warnUnknownMetaKeys(
|
|
16874
16881
|
tags,
|
|
16875
16882
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
16876
16883
|
(msg) => warn(lineNumber, msg)
|
|
16877
16884
|
);
|
|
16878
|
-
const id = nodeId2(name);
|
|
16879
|
-
if (
|
|
16885
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
16886
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
16880
16887
|
const isEdge = EDGE_NODE_NAMES.has(id.toLowerCase());
|
|
16881
16888
|
currentNode = {
|
|
16882
16889
|
id,
|
|
@@ -16960,18 +16967,20 @@ function parseInfra(content) {
|
|
|
16960
16967
|
const compMatch = trimmed.match(COMPONENT_RE);
|
|
16961
16968
|
if (compMatch) {
|
|
16962
16969
|
finishCurrentTagGroup();
|
|
16963
|
-
const
|
|
16970
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
16971
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
16964
16972
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
16965
16973
|
const name = peeled.label;
|
|
16966
|
-
const
|
|
16974
|
+
const alias = asAlias ?? peeled.alias;
|
|
16975
|
+
const rest = meta ?? "";
|
|
16967
16976
|
const { tags: nodeTags } = extractPipeMetadata(rest);
|
|
16968
16977
|
warnUnknownMetaKeys(
|
|
16969
16978
|
nodeTags,
|
|
16970
16979
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
16971
16980
|
(msg) => warn(lineNumber, msg)
|
|
16972
16981
|
);
|
|
16973
|
-
const id = nodeId2(name);
|
|
16974
|
-
if (
|
|
16982
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
16983
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
16975
16984
|
const tags = currentGroup.metadata ? { ...currentGroup.metadata, ...nodeTags } : nodeTags;
|
|
16976
16985
|
currentNode = {
|
|
16977
16986
|
id,
|
|
@@ -17162,6 +17171,11 @@ function parseInfra(content) {
|
|
|
17162
17171
|
}
|
|
17163
17172
|
continue;
|
|
17164
17173
|
}
|
|
17174
|
+
if (currentNode && tagAliasSet.has(normalizeName(key))) {
|
|
17175
|
+
const mut = nodeMutableTags.get(currentNode.id);
|
|
17176
|
+
if (mut) mut[tagAttrKey(key)] = rawVal;
|
|
17177
|
+
continue;
|
|
17178
|
+
}
|
|
17165
17179
|
if (!INFRA_BEHAVIOR_KEYS.has(key) && !EDGE_ONLY_KEYS.has(key)) {
|
|
17166
17180
|
const allKeys = [...INFRA_BEHAVIOR_KEYS, ...EDGE_ONLY_KEYS];
|
|
17167
17181
|
const hint = suggest(key, allKeys);
|
|
@@ -17227,18 +17241,20 @@ function parseInfra(content) {
|
|
|
17227
17241
|
finishCurrentNode();
|
|
17228
17242
|
const compMatch = trimmed.match(COMPONENT_RE);
|
|
17229
17243
|
if (compMatch) {
|
|
17230
|
-
const
|
|
17244
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
17245
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
17231
17246
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
17232
17247
|
const name = peeled.label;
|
|
17233
|
-
const
|
|
17248
|
+
const alias = asAlias ?? peeled.alias;
|
|
17249
|
+
const rest = meta ?? "";
|
|
17234
17250
|
const { tags: nodeTags } = extractPipeMetadata(rest);
|
|
17235
17251
|
warnUnknownMetaKeys(
|
|
17236
17252
|
nodeTags,
|
|
17237
17253
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
17238
17254
|
(msg) => warn(lineNumber, msg)
|
|
17239
17255
|
);
|
|
17240
|
-
const id = nodeId2(name);
|
|
17241
|
-
if (
|
|
17256
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
17257
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
17242
17258
|
const tags = currentGroup.metadata ? { ...currentGroup.metadata, ...nodeTags } : nodeTags;
|
|
17243
17259
|
currentNode = {
|
|
17244
17260
|
id,
|
|
@@ -17260,18 +17276,20 @@ function parseInfra(content) {
|
|
|
17260
17276
|
finishCurrentNode();
|
|
17261
17277
|
finishCurrentTagGroup();
|
|
17262
17278
|
currentGroup = null;
|
|
17263
|
-
const
|
|
17279
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
17280
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
17264
17281
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
17265
17282
|
const name = peeled.label;
|
|
17266
|
-
const
|
|
17283
|
+
const alias = asAlias ?? peeled.alias;
|
|
17284
|
+
const rest = meta ?? "";
|
|
17267
17285
|
const { tags } = extractPipeMetadata(rest);
|
|
17268
17286
|
warnUnknownMetaKeys(
|
|
17269
17287
|
tags,
|
|
17270
17288
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
17271
17289
|
(msg) => warn(lineNumber, msg)
|
|
17272
17290
|
);
|
|
17273
|
-
const id = nodeId2(name);
|
|
17274
|
-
if (
|
|
17291
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
17292
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
17275
17293
|
currentNode = {
|
|
17276
17294
|
id,
|
|
17277
17295
|
label: name,
|
|
@@ -17471,7 +17489,7 @@ var init_parser15 = __esm({
|
|
|
17471
17489
|
DEPRECATED_FANOUT_RE = /\bx(\d+)\s*$/;
|
|
17472
17490
|
GROUP_RE = /^\[([^\]]+)\]\s*(?:as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*)?((?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
|
|
17473
17491
|
TAG_VALUE_RE = /^(\w[\w\s]+?)\s*$/;
|
|
17474
|
-
COMPONENT_RE = /^(?:"([^"]+)"|([a-zA-Z_][\w -]*?))\s*((?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
|
|
17492
|
+
COMPONENT_RE = /^(?:"(?<qname>[^"]+)"|(?<uname>[a-zA-Z_][\w -]*?))(?:\s+as\s+(?<alias>[A-Za-z][A-Za-z0-9_]{0,11}))?\s*(?<meta>(?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
|
|
17475
17493
|
PIPE_META_RE = /[|,]\s*(\w+)\s*:\s*([^|,]+)/g;
|
|
17476
17494
|
SAMELINE_META_RE = /(?:^|[\s,])(\w+)\s*:\s*([^,]+?)(?=\s*,|\s*$)/g;
|
|
17477
17495
|
PROPERTY_RE = /^([\w-]+):\s*(.+)$/;
|
|
@@ -18955,8 +18973,8 @@ function parseMap(content, palette) {
|
|
|
18955
18973
|
continue;
|
|
18956
18974
|
}
|
|
18957
18975
|
if (open.route && indent > open.route.indent) {
|
|
18958
|
-
const leg = parseLeg(trimmed, lineNumber
|
|
18959
|
-
open.route.route.legs.push(leg);
|
|
18976
|
+
const leg = parseLeg(trimmed, lineNumber);
|
|
18977
|
+
if (leg) open.route.route.legs.push(leg);
|
|
18960
18978
|
continue;
|
|
18961
18979
|
}
|
|
18962
18980
|
if (open.poi && indent > open.poi.indent) {
|
|
@@ -18985,6 +19003,14 @@ function parseMap(content, palette) {
|
|
|
18985
19003
|
Object.assign(open.poi.poi.meta, split.meta);
|
|
18986
19004
|
continue;
|
|
18987
19005
|
}
|
|
19006
|
+
if (split.name) {
|
|
19007
|
+
const src = open.poi.poi.alias ?? poiName(open.poi.poi.pos) ?? "the poi";
|
|
19008
|
+
pushError(
|
|
19009
|
+
lineNumber,
|
|
19010
|
+
`Malformed hub edge: "${trimmed}" \u2014 an indented line under a poi is an edge from "${src}" and needs an arrow glyph (\`-> dest\`, \`~> dest\`, or labeled \`-label-> dest\`). For a standalone place, declare it at the top level. (\xA724B.5)`
|
|
19011
|
+
);
|
|
19012
|
+
continue;
|
|
19013
|
+
}
|
|
18988
19014
|
}
|
|
18989
19015
|
closeBlocks();
|
|
18990
19016
|
const firstWord = trimmed.split(/\s+/)[0];
|
|
@@ -19233,31 +19259,44 @@ function parseMap(content, palette) {
|
|
|
19233
19259
|
const { tags, meta } = partitionMeta(split.meta, tagGroupNames());
|
|
19234
19260
|
const originLabel = meta["label"];
|
|
19235
19261
|
const originValue = meta["value"];
|
|
19236
|
-
|
|
19262
|
+
if (meta["style"] !== void 0) {
|
|
19263
|
+
pushError(
|
|
19264
|
+
line11,
|
|
19265
|
+
"route header no longer takes `style:` \u2014 set leg shape with the arrow glyph instead (`~\u2026~>` for an arc leg, `-\u2026->` for a straight one). (\xA724B.6)"
|
|
19266
|
+
);
|
|
19267
|
+
}
|
|
19237
19268
|
const route = {
|
|
19238
19269
|
origin: pos,
|
|
19239
19270
|
...split.alias !== void 0 && { originAlias: split.alias },
|
|
19240
19271
|
...originLabel !== void 0 && { originLabel },
|
|
19241
19272
|
...originValue !== void 0 && { originValue },
|
|
19242
19273
|
originTags: tags,
|
|
19243
|
-
style,
|
|
19244
19274
|
legs: [],
|
|
19245
19275
|
lineNumber: line11
|
|
19246
19276
|
};
|
|
19247
19277
|
routes.push(route);
|
|
19248
19278
|
open.route = { route, indent };
|
|
19249
19279
|
}
|
|
19250
|
-
function parseLeg(trimmed, line11
|
|
19251
|
-
let arrowStyle = "straight";
|
|
19252
|
-
let label;
|
|
19253
|
-
let rest = trimmed;
|
|
19280
|
+
function parseLeg(trimmed, line11) {
|
|
19254
19281
|
const m = trimmed.match(LEG_ARROW_RE);
|
|
19255
|
-
if (m) {
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19282
|
+
if (!m) {
|
|
19283
|
+
pushError(
|
|
19284
|
+
line11,
|
|
19285
|
+
`Malformed route leg: "${trimmed}" \u2014 a leg needs an arrow glyph (\`-> dest\`, \`~> dest\`, or labeled \`-label-> dest\` / \`~label~> dest\`). (\xA724B.6)`
|
|
19286
|
+
);
|
|
19287
|
+
return null;
|
|
19260
19288
|
}
|
|
19289
|
+
const arr = classifyArrow(m[1], line11);
|
|
19290
|
+
if (!arr.directed) {
|
|
19291
|
+
pushError(
|
|
19292
|
+
line11,
|
|
19293
|
+
`A route leg is directional \u2014 "${trimmed}" uses an undirected glyph; use \`-> \` (straight) or \`~> \` (arc). (\xA724B.6)`
|
|
19294
|
+
);
|
|
19295
|
+
return null;
|
|
19296
|
+
}
|
|
19297
|
+
const arrowStyle = arr.style;
|
|
19298
|
+
const label = arr.label;
|
|
19299
|
+
const rest = m[2];
|
|
19261
19300
|
const split = splitNameAndMeta(
|
|
19262
19301
|
rest,
|
|
19263
19302
|
registry(),
|
|
@@ -19273,10 +19312,9 @@ function parseMap(content, palette) {
|
|
|
19273
19312
|
const { tags, meta } = partitionMeta(split.meta, tagGroupNames());
|
|
19274
19313
|
const value = meta["value"];
|
|
19275
19314
|
const destLabel = meta["label"];
|
|
19276
|
-
const style = arrowStyle === "arc" || headerStyle === "arc" ? "arc" : "straight";
|
|
19277
19315
|
return {
|
|
19278
19316
|
...label !== void 0 && { label },
|
|
19279
|
-
style,
|
|
19317
|
+
style: arrowStyle,
|
|
19280
19318
|
...value !== void 0 && { value },
|
|
19281
19319
|
dest: pos,
|
|
19282
19320
|
...split.alias !== void 0 && { destAlias: split.alias },
|
|
@@ -54080,13 +54118,15 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54080
54118
|
labelY: lp.y
|
|
54081
54119
|
};
|
|
54082
54120
|
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
54121
|
+
const routeVal = Number(leg.value);
|
|
54083
54122
|
legs.push({
|
|
54084
54123
|
d: legPath(a, b, bow.curved, bow.offset),
|
|
54085
|
-
width: routeWidthFor(
|
|
54124
|
+
width: routeWidthFor(routeVal),
|
|
54086
54125
|
color: lineColor(leg.tags) ?? mix(palette.text, palette.bg, 72),
|
|
54087
54126
|
arrow: true,
|
|
54088
54127
|
fromId: leg.fromId,
|
|
54089
54128
|
toId: leg.toId,
|
|
54129
|
+
...Number.isFinite(routeVal) && routeVal > 0 && { value: routeVal },
|
|
54090
54130
|
...Object.keys(leg.tags).length > 0 && { tags: leg.tags },
|
|
54091
54131
|
lineNumber: leg.lineNumber,
|
|
54092
54132
|
...leg.label !== void 0 && {
|
|
@@ -54133,6 +54173,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54133
54173
|
labelY: lp.y
|
|
54134
54174
|
};
|
|
54135
54175
|
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
54176
|
+
const edgeVal = Number(e.meta["value"]);
|
|
54136
54177
|
legs.push({
|
|
54137
54178
|
d: legPath(a, b, bow.curved, bow.offset),
|
|
54138
54179
|
width: widthFor(e),
|
|
@@ -54140,6 +54181,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54140
54181
|
arrow: e.directed,
|
|
54141
54182
|
fromId: e.fromId,
|
|
54142
54183
|
toId: e.toId,
|
|
54184
|
+
...Number.isFinite(edgeVal) && edgeVal > 0 && { value: edgeVal },
|
|
54143
54185
|
...Object.keys(e.tags).length > 0 && { tags: e.tags },
|
|
54144
54186
|
lineNumber: e.lineNumber,
|
|
54145
54187
|
...e.label !== void 0 && {
|
|
@@ -54297,18 +54339,49 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54297
54339
|
const SHORT_HOP_MAX = Math.max(46, Math.min(width, height) * 0.11);
|
|
54298
54340
|
const STEP = 4;
|
|
54299
54341
|
const HOP_GAP = 9;
|
|
54342
|
+
const CLEAR_STEP = 6;
|
|
54343
|
+
const CLEAR_MAX = SHORT_HOP_MAX * 3;
|
|
54344
|
+
const CLEAR_TOL = CLEAR_STEP;
|
|
54345
|
+
const clearanceAt = (px, py, ox, oy) => {
|
|
54346
|
+
const baseAng = Math.atan2(oy, ox);
|
|
54347
|
+
let minClear = CLEAR_MAX;
|
|
54348
|
+
for (let k = -2; k <= 2; k++) {
|
|
54349
|
+
const a = baseAng + k * Math.PI / 4;
|
|
54350
|
+
const rx = Math.cos(a);
|
|
54351
|
+
const ry = Math.sin(a);
|
|
54352
|
+
let d = CLEAR_STEP;
|
|
54353
|
+
for (; d <= CLEAR_MAX; d += CLEAR_STEP) {
|
|
54354
|
+
const qx = px + rx * d;
|
|
54355
|
+
const qy = py + ry * d;
|
|
54356
|
+
if (qx < FIT_PAD || qy < topPad || qx > width - FIT_PAD || qy > height - FIT_PAD)
|
|
54357
|
+
break;
|
|
54358
|
+
if (fillAt(qx, qy) !== water) break;
|
|
54359
|
+
}
|
|
54360
|
+
if (d < minClear) minClear = d;
|
|
54361
|
+
}
|
|
54362
|
+
return minClear;
|
|
54363
|
+
};
|
|
54300
54364
|
const tryShortHopCallout = (cx, cy, fill2, name, value, font = FONT2, gap = HOP_GAP, maxLen = SHORT_HOP_MAX) => {
|
|
54301
54365
|
const chipW = stackW(name, value, font);
|
|
54302
54366
|
const chipH = stackH(value !== void 0, font);
|
|
54367
|
+
const D = Math.SQRT1_2;
|
|
54303
54368
|
const dirs = [
|
|
54304
54369
|
[0, -1],
|
|
54305
|
-
// north
|
|
54370
|
+
// north
|
|
54306
54371
|
[0, 1],
|
|
54307
|
-
// south
|
|
54372
|
+
// south
|
|
54308
54373
|
[1, 0],
|
|
54309
|
-
// east
|
|
54310
|
-
[-1, 0]
|
|
54311
|
-
// west
|
|
54374
|
+
// east
|
|
54375
|
+
[-1, 0],
|
|
54376
|
+
// west
|
|
54377
|
+
[D, -D],
|
|
54378
|
+
// NE
|
|
54379
|
+
[D, D],
|
|
54380
|
+
// SE
|
|
54381
|
+
[-D, -D],
|
|
54382
|
+
// NW
|
|
54383
|
+
[-D, D]
|
|
54384
|
+
// SW
|
|
54312
54385
|
];
|
|
54313
54386
|
let best = null;
|
|
54314
54387
|
for (const [dx, dy] of dirs) {
|
|
@@ -54358,13 +54431,16 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54358
54431
|
(p) => segmentRectOverlap(cx, cy, innerX, innerY, p)
|
|
54359
54432
|
))
|
|
54360
54433
|
continue;
|
|
54361
|
-
|
|
54434
|
+
const clear = clearanceAt(ccx, ccy, dx, dy);
|
|
54435
|
+
const better = best === null || clear > best.clear + CLEAR_TOL || clear >= best.clear - CLEAR_TOL && len < best.len;
|
|
54436
|
+
if (better)
|
|
54362
54437
|
best = {
|
|
54363
54438
|
x: ccx,
|
|
54364
54439
|
y: ccy,
|
|
54365
54440
|
rect,
|
|
54366
54441
|
leader: [cx, cy, innerX, innerY],
|
|
54367
|
-
len
|
|
54442
|
+
len,
|
|
54443
|
+
clear
|
|
54368
54444
|
};
|
|
54369
54445
|
}
|
|
54370
54446
|
return best ? { x: best.x, y: best.y, rect: best.rect, leader: best.leader } : null;
|
|
@@ -55025,6 +55101,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
55025
55101
|
const nx = -dy / len;
|
|
55026
55102
|
const ny = dx / len;
|
|
55027
55103
|
const perp = labelH + 2;
|
|
55104
|
+
const stroke2 = lg.width ?? W_MIN;
|
|
55105
|
+
const clearW = stroke2 / 2 + labelH / 2 + 3;
|
|
55106
|
+
const offList = stroke2 >= 4 ? [clearW, -clearW, clearW + perp, -(clearW + perp), 0] : [0, perp, -perp, 2 * perp, -2 * perp];
|
|
55028
55107
|
const pm = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
55029
55108
|
lg.d
|
|
55030
55109
|
);
|
|
@@ -55044,7 +55123,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
55044
55123
|
return [a.cx + dx * t, a.cy + dy * t];
|
|
55045
55124
|
};
|
|
55046
55125
|
const candidates = [];
|
|
55047
|
-
for (const off of
|
|
55126
|
+
for (const off of offList)
|
|
55048
55127
|
for (const t of T_LIST) {
|
|
55049
55128
|
const [bx, by] = pointAt(t);
|
|
55050
55129
|
candidates.push([bx + nx * off, by + ny * off - 4]);
|
|
@@ -55796,6 +55875,12 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
55796
55875
|
}
|
|
55797
55876
|
}
|
|
55798
55877
|
wireSync(p, leg.lineNumber);
|
|
55878
|
+
if (leg.value !== void 0) {
|
|
55879
|
+
p.attr("data-value", leg.value);
|
|
55880
|
+
p.append("title").text(
|
|
55881
|
+
leg.label !== void 0 ? `${leg.label}: ${leg.value}` : `${leg.value}`
|
|
55882
|
+
);
|
|
55883
|
+
}
|
|
55799
55884
|
if (arrowTri) {
|
|
55800
55885
|
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);
|
|
55801
55886
|
}
|
package/dist/advanced.d.cts
CHANGED
|
@@ -299,9 +299,10 @@ interface MapPoi {
|
|
|
299
299
|
readonly lineNumber: number;
|
|
300
300
|
}
|
|
301
301
|
/** One leg of a route (§24B.6): an edge from the previous stop to `dest`. Reuses
|
|
302
|
-
* the edge arrow idiom — in-arrow text = leg label,
|
|
303
|
-
*
|
|
304
|
-
* the LINE (§24B.6); `label:`/`as` still
|
|
302
|
+
* the indented edge arrow idiom (same as sitemap) — in-arrow text = leg label,
|
|
303
|
+
* `value:` = leg thickness, the arrow glyph = shape (`-…->` straight, `~…~>`
|
|
304
|
+
* arc). A tag on the leg line colours the LINE (§24B.6); `label:`/`as` still
|
|
305
|
+
* name the DESTINATION stop. The arrow is required — a bare destination errors. */
|
|
305
306
|
interface MapRouteLeg {
|
|
306
307
|
readonly label?: string;
|
|
307
308
|
readonly style: 'straight' | 'arc';
|
|
@@ -314,16 +315,16 @@ interface MapRouteLeg {
|
|
|
314
315
|
readonly tags: Readonly<Record<string, string>>;
|
|
315
316
|
readonly lineNumber: number;
|
|
316
317
|
}
|
|
317
|
-
/** An ordered, auto-numbered route (§24B.6): `route <origin
|
|
318
|
-
*
|
|
319
|
-
*
|
|
318
|
+
/** An ordered, auto-numbered route (§24B.6): `route <origin>` + a sequence of
|
|
319
|
+
* indented arrow legs, each continuing from the previous stop. Leg shape is
|
|
320
|
+
* per-leg (the arrow glyph) — there is no header-level shape option. Repeat the
|
|
321
|
+
* origin as a leg's destination to close a loop. */
|
|
320
322
|
interface MapRoute {
|
|
321
323
|
readonly origin: PoiPos;
|
|
322
324
|
readonly originAlias?: string;
|
|
323
325
|
readonly originLabel?: string;
|
|
324
326
|
readonly originValue?: string;
|
|
325
327
|
readonly originTags: Readonly<Record<string, string>>;
|
|
326
|
-
readonly style: 'straight' | 'arc';
|
|
327
328
|
readonly legs: readonly MapRouteLeg[];
|
|
328
329
|
readonly lineNumber: number;
|
|
329
330
|
}
|
|
@@ -5014,6 +5015,10 @@ interface MapLayoutLeg {
|
|
|
5014
5015
|
* (§24B.6). Omitted when the leg carries no tag. */
|
|
5015
5016
|
readonly tags?: Readonly<Record<string, string>>;
|
|
5016
5017
|
readonly label?: string;
|
|
5018
|
+
/** The leg's numeric weight (the `value:` metadata) when present and positive.
|
|
5019
|
+
* Drives {@link width}, but kept here verbatim so the renderer can surface it
|
|
5020
|
+
* on hover (a `<title>` tooltip) — the width alone is lossy. */
|
|
5021
|
+
readonly value?: number;
|
|
5017
5022
|
readonly labelX?: number;
|
|
5018
5023
|
readonly labelY?: number;
|
|
5019
5024
|
/** Text colour for the label — contrast-picked against the background fill the
|
package/dist/advanced.d.ts
CHANGED
|
@@ -299,9 +299,10 @@ interface MapPoi {
|
|
|
299
299
|
readonly lineNumber: number;
|
|
300
300
|
}
|
|
301
301
|
/** One leg of a route (§24B.6): an edge from the previous stop to `dest`. Reuses
|
|
302
|
-
* the edge arrow idiom — in-arrow text = leg label,
|
|
303
|
-
*
|
|
304
|
-
* the LINE (§24B.6); `label:`/`as` still
|
|
302
|
+
* the indented edge arrow idiom (same as sitemap) — in-arrow text = leg label,
|
|
303
|
+
* `value:` = leg thickness, the arrow glyph = shape (`-…->` straight, `~…~>`
|
|
304
|
+
* arc). A tag on the leg line colours the LINE (§24B.6); `label:`/`as` still
|
|
305
|
+
* name the DESTINATION stop. The arrow is required — a bare destination errors. */
|
|
305
306
|
interface MapRouteLeg {
|
|
306
307
|
readonly label?: string;
|
|
307
308
|
readonly style: 'straight' | 'arc';
|
|
@@ -314,16 +315,16 @@ interface MapRouteLeg {
|
|
|
314
315
|
readonly tags: Readonly<Record<string, string>>;
|
|
315
316
|
readonly lineNumber: number;
|
|
316
317
|
}
|
|
317
|
-
/** An ordered, auto-numbered route (§24B.6): `route <origin
|
|
318
|
-
*
|
|
319
|
-
*
|
|
318
|
+
/** An ordered, auto-numbered route (§24B.6): `route <origin>` + a sequence of
|
|
319
|
+
* indented arrow legs, each continuing from the previous stop. Leg shape is
|
|
320
|
+
* per-leg (the arrow glyph) — there is no header-level shape option. Repeat the
|
|
321
|
+
* origin as a leg's destination to close a loop. */
|
|
320
322
|
interface MapRoute {
|
|
321
323
|
readonly origin: PoiPos;
|
|
322
324
|
readonly originAlias?: string;
|
|
323
325
|
readonly originLabel?: string;
|
|
324
326
|
readonly originValue?: string;
|
|
325
327
|
readonly originTags: Readonly<Record<string, string>>;
|
|
326
|
-
readonly style: 'straight' | 'arc';
|
|
327
328
|
readonly legs: readonly MapRouteLeg[];
|
|
328
329
|
readonly lineNumber: number;
|
|
329
330
|
}
|
|
@@ -5014,6 +5015,10 @@ interface MapLayoutLeg {
|
|
|
5014
5015
|
* (§24B.6). Omitted when the leg carries no tag. */
|
|
5015
5016
|
readonly tags?: Readonly<Record<string, string>>;
|
|
5016
5017
|
readonly label?: string;
|
|
5018
|
+
/** The leg's numeric weight (the `value:` metadata) when present and positive.
|
|
5019
|
+
* Drives {@link width}, but kept here verbatim so the renderer can surface it
|
|
5020
|
+
* on hover (a `<title>` tooltip) — the width alone is lossy. */
|
|
5021
|
+
readonly value?: number;
|
|
5017
5022
|
readonly labelX?: number;
|
|
5018
5023
|
readonly labelY?: number;
|
|
5019
5024
|
/** Text colour for the label — contrast-picked against the background fill the
|