@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/dist/index.js
CHANGED
|
@@ -16681,6 +16681,7 @@ function parseInfra(content) {
|
|
|
16681
16681
|
let currentGroup = null;
|
|
16682
16682
|
let currentTagGroup = null;
|
|
16683
16683
|
let baseIndent = 0;
|
|
16684
|
+
let firstLineConsumed = false;
|
|
16684
16685
|
function finishCurrentNode() {
|
|
16685
16686
|
if (!currentNode) return;
|
|
16686
16687
|
const key = currentNode.id;
|
|
@@ -16688,7 +16689,8 @@ function parseInfra(content) {
|
|
|
16688
16689
|
if (existing) {
|
|
16689
16690
|
const incomingDisplay = displayName(currentNode.label);
|
|
16690
16691
|
const existingDisplay = displayName(existing.label);
|
|
16691
|
-
|
|
16692
|
+
const isAliasRef = nameAliasMap.get(currentNode.label) === key;
|
|
16693
|
+
if (incomingDisplay !== existingDisplay && !isAliasRef) {
|
|
16692
16694
|
result.diagnostics.push(
|
|
16693
16695
|
makeDgmoError(
|
|
16694
16696
|
currentNode.lineNumber,
|
|
@@ -16754,19 +16756,22 @@ function parseInfra(content) {
|
|
|
16754
16756
|
if (indent === 0 && currentNode && !trimmed.startsWith("-")) {
|
|
16755
16757
|
finishCurrentNode();
|
|
16756
16758
|
}
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
|
|
16765
|
-
|
|
16766
|
-
|
|
16767
|
-
|
|
16759
|
+
if (!firstLineConsumed) {
|
|
16760
|
+
firstLineConsumed = true;
|
|
16761
|
+
const firstLineResult = parseFirstLine(trimmed);
|
|
16762
|
+
if (firstLineResult) {
|
|
16763
|
+
if (firstLineResult.chartType !== "infra") {
|
|
16764
|
+
setError(
|
|
16765
|
+
lineNumber,
|
|
16766
|
+
`Expected chart type 'infra', got '${firstLineResult.chartType}'`
|
|
16767
|
+
);
|
|
16768
|
+
}
|
|
16769
|
+
if (firstLineResult.title) {
|
|
16770
|
+
result.title = firstLineResult.title;
|
|
16771
|
+
result.titleLineNumber = lineNumber;
|
|
16772
|
+
}
|
|
16773
|
+
continue;
|
|
16768
16774
|
}
|
|
16769
|
-
continue;
|
|
16770
16775
|
}
|
|
16771
16776
|
if (/^direction-tb$/i.test(trimmed)) {
|
|
16772
16777
|
result.direction = "TB";
|
|
@@ -16832,18 +16837,20 @@ function parseInfra(content) {
|
|
|
16832
16837
|
if (compMatch) {
|
|
16833
16838
|
finishCurrentNode();
|
|
16834
16839
|
finishCurrentTagGroup();
|
|
16835
|
-
const
|
|
16840
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
16841
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
16836
16842
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
16837
16843
|
const name = peeled.label;
|
|
16838
|
-
const
|
|
16844
|
+
const alias = asAlias ?? peeled.alias;
|
|
16845
|
+
const rest = meta ?? "";
|
|
16839
16846
|
const { tags } = extractPipeMetadata(rest);
|
|
16840
16847
|
warnUnknownMetaKeys(
|
|
16841
16848
|
tags,
|
|
16842
16849
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
16843
16850
|
(msg) => warn(lineNumber, msg)
|
|
16844
16851
|
);
|
|
16845
|
-
const id = nodeId2(name);
|
|
16846
|
-
if (
|
|
16852
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
16853
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
16847
16854
|
const isEdge = EDGE_NODE_NAMES.has(id.toLowerCase());
|
|
16848
16855
|
currentNode = {
|
|
16849
16856
|
id,
|
|
@@ -16927,18 +16934,20 @@ function parseInfra(content) {
|
|
|
16927
16934
|
const compMatch = trimmed.match(COMPONENT_RE);
|
|
16928
16935
|
if (compMatch) {
|
|
16929
16936
|
finishCurrentTagGroup();
|
|
16930
|
-
const
|
|
16937
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
16938
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
16931
16939
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
16932
16940
|
const name = peeled.label;
|
|
16933
|
-
const
|
|
16941
|
+
const alias = asAlias ?? peeled.alias;
|
|
16942
|
+
const rest = meta ?? "";
|
|
16934
16943
|
const { tags: nodeTags } = extractPipeMetadata(rest);
|
|
16935
16944
|
warnUnknownMetaKeys(
|
|
16936
16945
|
nodeTags,
|
|
16937
16946
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
16938
16947
|
(msg) => warn(lineNumber, msg)
|
|
16939
16948
|
);
|
|
16940
|
-
const id = nodeId2(name);
|
|
16941
|
-
if (
|
|
16949
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
16950
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
16942
16951
|
const tags = currentGroup.metadata ? { ...currentGroup.metadata, ...nodeTags } : nodeTags;
|
|
16943
16952
|
currentNode = {
|
|
16944
16953
|
id,
|
|
@@ -17129,6 +17138,11 @@ function parseInfra(content) {
|
|
|
17129
17138
|
}
|
|
17130
17139
|
continue;
|
|
17131
17140
|
}
|
|
17141
|
+
if (currentNode && tagAliasSet.has(normalizeName(key))) {
|
|
17142
|
+
const mut = nodeMutableTags.get(currentNode.id);
|
|
17143
|
+
if (mut) mut[tagAttrKey(key)] = rawVal;
|
|
17144
|
+
continue;
|
|
17145
|
+
}
|
|
17132
17146
|
if (!INFRA_BEHAVIOR_KEYS.has(key) && !EDGE_ONLY_KEYS.has(key)) {
|
|
17133
17147
|
const allKeys = [...INFRA_BEHAVIOR_KEYS, ...EDGE_ONLY_KEYS];
|
|
17134
17148
|
const hint = suggest(key, allKeys);
|
|
@@ -17194,18 +17208,20 @@ function parseInfra(content) {
|
|
|
17194
17208
|
finishCurrentNode();
|
|
17195
17209
|
const compMatch = trimmed.match(COMPONENT_RE);
|
|
17196
17210
|
if (compMatch) {
|
|
17197
|
-
const
|
|
17211
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
17212
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
17198
17213
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
17199
17214
|
const name = peeled.label;
|
|
17200
|
-
const
|
|
17215
|
+
const alias = asAlias ?? peeled.alias;
|
|
17216
|
+
const rest = meta ?? "";
|
|
17201
17217
|
const { tags: nodeTags } = extractPipeMetadata(rest);
|
|
17202
17218
|
warnUnknownMetaKeys(
|
|
17203
17219
|
nodeTags,
|
|
17204
17220
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
17205
17221
|
(msg) => warn(lineNumber, msg)
|
|
17206
17222
|
);
|
|
17207
|
-
const id = nodeId2(name);
|
|
17208
|
-
if (
|
|
17223
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
17224
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
17209
17225
|
const tags = currentGroup.metadata ? { ...currentGroup.metadata, ...nodeTags } : nodeTags;
|
|
17210
17226
|
currentNode = {
|
|
17211
17227
|
id,
|
|
@@ -17227,18 +17243,20 @@ function parseInfra(content) {
|
|
|
17227
17243
|
finishCurrentNode();
|
|
17228
17244
|
finishCurrentTagGroup();
|
|
17229
17245
|
currentGroup = null;
|
|
17230
|
-
const
|
|
17246
|
+
const { qname, uname, alias: asAlias, meta } = compMatch.groups;
|
|
17247
|
+
const rawName = (qname ?? uname ?? "").trim();
|
|
17231
17248
|
const peeled = peelInfraDecorations(rawName, lineNumber);
|
|
17232
17249
|
const name = peeled.label;
|
|
17233
|
-
const
|
|
17250
|
+
const alias = asAlias ?? peeled.alias;
|
|
17251
|
+
const rest = meta ?? "";
|
|
17234
17252
|
const { tags } = extractPipeMetadata(rest);
|
|
17235
17253
|
warnUnknownMetaKeys(
|
|
17236
17254
|
tags,
|
|
17237
17255
|
withTagAliases(INFRA_REGISTRY, tagAliasSet),
|
|
17238
17256
|
(msg) => warn(lineNumber, msg)
|
|
17239
17257
|
);
|
|
17240
|
-
const id = nodeId2(name);
|
|
17241
|
-
if (
|
|
17258
|
+
const id = nameAliasMap.get(name) ?? nodeId2(name);
|
|
17259
|
+
if (alias) nameAliasMap.set(alias, id);
|
|
17242
17260
|
currentNode = {
|
|
17243
17261
|
id,
|
|
17244
17262
|
label: name,
|
|
@@ -17438,7 +17456,7 @@ var init_parser15 = __esm({
|
|
|
17438
17456
|
DEPRECATED_FANOUT_RE = /\bx(\d+)\s*$/;
|
|
17439
17457
|
GROUP_RE = /^\[([^\]]+)\]\s*(?:as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*)?((?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
|
|
17440
17458
|
TAG_VALUE_RE = /^(\w[\w\s]+?)\s*$/;
|
|
17441
|
-
COMPONENT_RE = /^(?:"([^"]+)"|([a-zA-Z_][\w -]*?))\s*((?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
|
|
17459
|
+
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*:.*))?$/;
|
|
17442
17460
|
PIPE_META_RE = /[|,]\s*(\w+)\s*:\s*([^|,]+)/g;
|
|
17443
17461
|
SAMELINE_META_RE = /(?:^|[\s,])(\w+)\s*:\s*([^,]+?)(?=\s*,|\s*$)/g;
|
|
17444
17462
|
PROPERTY_RE = /^([\w-]+):\s*(.+)$/;
|
|
@@ -18922,8 +18940,8 @@ function parseMap(content, palette) {
|
|
|
18922
18940
|
continue;
|
|
18923
18941
|
}
|
|
18924
18942
|
if (open.route && indent > open.route.indent) {
|
|
18925
|
-
const leg = parseLeg(trimmed, lineNumber
|
|
18926
|
-
open.route.route.legs.push(leg);
|
|
18943
|
+
const leg = parseLeg(trimmed, lineNumber);
|
|
18944
|
+
if (leg) open.route.route.legs.push(leg);
|
|
18927
18945
|
continue;
|
|
18928
18946
|
}
|
|
18929
18947
|
if (open.poi && indent > open.poi.indent) {
|
|
@@ -18952,6 +18970,14 @@ function parseMap(content, palette) {
|
|
|
18952
18970
|
Object.assign(open.poi.poi.meta, split.meta);
|
|
18953
18971
|
continue;
|
|
18954
18972
|
}
|
|
18973
|
+
if (split.name) {
|
|
18974
|
+
const src = open.poi.poi.alias ?? poiName(open.poi.poi.pos) ?? "the poi";
|
|
18975
|
+
pushError(
|
|
18976
|
+
lineNumber,
|
|
18977
|
+
`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)`
|
|
18978
|
+
);
|
|
18979
|
+
continue;
|
|
18980
|
+
}
|
|
18955
18981
|
}
|
|
18956
18982
|
closeBlocks();
|
|
18957
18983
|
const firstWord = trimmed.split(/\s+/)[0];
|
|
@@ -19200,31 +19226,44 @@ function parseMap(content, palette) {
|
|
|
19200
19226
|
const { tags, meta } = partitionMeta(split.meta, tagGroupNames());
|
|
19201
19227
|
const originLabel = meta["label"];
|
|
19202
19228
|
const originValue = meta["value"];
|
|
19203
|
-
|
|
19229
|
+
if (meta["style"] !== void 0) {
|
|
19230
|
+
pushError(
|
|
19231
|
+
line11,
|
|
19232
|
+
"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)"
|
|
19233
|
+
);
|
|
19234
|
+
}
|
|
19204
19235
|
const route = {
|
|
19205
19236
|
origin: pos,
|
|
19206
19237
|
...split.alias !== void 0 && { originAlias: split.alias },
|
|
19207
19238
|
...originLabel !== void 0 && { originLabel },
|
|
19208
19239
|
...originValue !== void 0 && { originValue },
|
|
19209
19240
|
originTags: tags,
|
|
19210
|
-
style,
|
|
19211
19241
|
legs: [],
|
|
19212
19242
|
lineNumber: line11
|
|
19213
19243
|
};
|
|
19214
19244
|
routes.push(route);
|
|
19215
19245
|
open.route = { route, indent };
|
|
19216
19246
|
}
|
|
19217
|
-
function parseLeg(trimmed, line11
|
|
19218
|
-
let arrowStyle = "straight";
|
|
19219
|
-
let label;
|
|
19220
|
-
let rest = trimmed;
|
|
19247
|
+
function parseLeg(trimmed, line11) {
|
|
19221
19248
|
const m = trimmed.match(LEG_ARROW_RE);
|
|
19222
|
-
if (m) {
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
|
|
19249
|
+
if (!m) {
|
|
19250
|
+
pushError(
|
|
19251
|
+
line11,
|
|
19252
|
+
`Malformed route leg: "${trimmed}" \u2014 a leg needs an arrow glyph (\`-> dest\`, \`~> dest\`, or labeled \`-label-> dest\` / \`~label~> dest\`). (\xA724B.6)`
|
|
19253
|
+
);
|
|
19254
|
+
return null;
|
|
19227
19255
|
}
|
|
19256
|
+
const arr = classifyArrow(m[1], line11);
|
|
19257
|
+
if (!arr.directed) {
|
|
19258
|
+
pushError(
|
|
19259
|
+
line11,
|
|
19260
|
+
`A route leg is directional \u2014 "${trimmed}" uses an undirected glyph; use \`-> \` (straight) or \`~> \` (arc). (\xA724B.6)`
|
|
19261
|
+
);
|
|
19262
|
+
return null;
|
|
19263
|
+
}
|
|
19264
|
+
const arrowStyle = arr.style;
|
|
19265
|
+
const label = arr.label;
|
|
19266
|
+
const rest = m[2];
|
|
19228
19267
|
const split = splitNameAndMeta(
|
|
19229
19268
|
rest,
|
|
19230
19269
|
registry(),
|
|
@@ -19240,10 +19279,9 @@ function parseMap(content, palette) {
|
|
|
19240
19279
|
const { tags, meta } = partitionMeta(split.meta, tagGroupNames());
|
|
19241
19280
|
const value = meta["value"];
|
|
19242
19281
|
const destLabel = meta["label"];
|
|
19243
|
-
const style = arrowStyle === "arc" || headerStyle === "arc" ? "arc" : "straight";
|
|
19244
19282
|
return {
|
|
19245
19283
|
...label !== void 0 && { label },
|
|
19246
|
-
style,
|
|
19284
|
+
style: arrowStyle,
|
|
19247
19285
|
...value !== void 0 && { value },
|
|
19248
19286
|
dest: pos,
|
|
19249
19287
|
...split.alias !== void 0 && { destAlias: split.alias },
|
|
@@ -53745,13 +53783,15 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53745
53783
|
labelY: lp.y
|
|
53746
53784
|
};
|
|
53747
53785
|
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53786
|
+
const routeVal = Number(leg.value);
|
|
53748
53787
|
legs.push({
|
|
53749
53788
|
d: legPath(a, b, bow.curved, bow.offset),
|
|
53750
|
-
width: routeWidthFor(
|
|
53789
|
+
width: routeWidthFor(routeVal),
|
|
53751
53790
|
color: lineColor(leg.tags) ?? mix(palette.text, palette.bg, 72),
|
|
53752
53791
|
arrow: true,
|
|
53753
53792
|
fromId: leg.fromId,
|
|
53754
53793
|
toId: leg.toId,
|
|
53794
|
+
...Number.isFinite(routeVal) && routeVal > 0 && { value: routeVal },
|
|
53755
53795
|
...Object.keys(leg.tags).length > 0 && { tags: leg.tags },
|
|
53756
53796
|
lineNumber: leg.lineNumber,
|
|
53757
53797
|
...leg.label !== void 0 && {
|
|
@@ -53798,6 +53838,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53798
53838
|
labelY: lp.y
|
|
53799
53839
|
};
|
|
53800
53840
|
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
53841
|
+
const edgeVal = Number(e.meta["value"]);
|
|
53801
53842
|
legs.push({
|
|
53802
53843
|
d: legPath(a, b, bow.curved, bow.offset),
|
|
53803
53844
|
width: widthFor(e),
|
|
@@ -53805,6 +53846,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53805
53846
|
arrow: e.directed,
|
|
53806
53847
|
fromId: e.fromId,
|
|
53807
53848
|
toId: e.toId,
|
|
53849
|
+
...Number.isFinite(edgeVal) && edgeVal > 0 && { value: edgeVal },
|
|
53808
53850
|
...Object.keys(e.tags).length > 0 && { tags: e.tags },
|
|
53809
53851
|
lineNumber: e.lineNumber,
|
|
53810
53852
|
...e.label !== void 0 && {
|
|
@@ -53962,18 +54004,49 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
53962
54004
|
const SHORT_HOP_MAX = Math.max(46, Math.min(width, height) * 0.11);
|
|
53963
54005
|
const STEP = 4;
|
|
53964
54006
|
const HOP_GAP = 9;
|
|
54007
|
+
const CLEAR_STEP = 6;
|
|
54008
|
+
const CLEAR_MAX = SHORT_HOP_MAX * 3;
|
|
54009
|
+
const CLEAR_TOL = CLEAR_STEP;
|
|
54010
|
+
const clearanceAt = (px, py, ox, oy) => {
|
|
54011
|
+
const baseAng = Math.atan2(oy, ox);
|
|
54012
|
+
let minClear = CLEAR_MAX;
|
|
54013
|
+
for (let k = -2; k <= 2; k++) {
|
|
54014
|
+
const a = baseAng + k * Math.PI / 4;
|
|
54015
|
+
const rx = Math.cos(a);
|
|
54016
|
+
const ry = Math.sin(a);
|
|
54017
|
+
let d = CLEAR_STEP;
|
|
54018
|
+
for (; d <= CLEAR_MAX; d += CLEAR_STEP) {
|
|
54019
|
+
const qx = px + rx * d;
|
|
54020
|
+
const qy = py + ry * d;
|
|
54021
|
+
if (qx < FIT_PAD || qy < topPad || qx > width - FIT_PAD || qy > height - FIT_PAD)
|
|
54022
|
+
break;
|
|
54023
|
+
if (fillAt(qx, qy) !== water) break;
|
|
54024
|
+
}
|
|
54025
|
+
if (d < minClear) minClear = d;
|
|
54026
|
+
}
|
|
54027
|
+
return minClear;
|
|
54028
|
+
};
|
|
53965
54029
|
const tryShortHopCallout = (cx, cy, fill2, name, value, font = FONT2, gap = HOP_GAP, maxLen = SHORT_HOP_MAX) => {
|
|
53966
54030
|
const chipW = stackW(name, value, font);
|
|
53967
54031
|
const chipH = stackH(value !== void 0, font);
|
|
54032
|
+
const D = Math.SQRT1_2;
|
|
53968
54033
|
const dirs = [
|
|
53969
54034
|
[0, -1],
|
|
53970
|
-
// north
|
|
54035
|
+
// north
|
|
53971
54036
|
[0, 1],
|
|
53972
|
-
// south
|
|
54037
|
+
// south
|
|
53973
54038
|
[1, 0],
|
|
53974
|
-
// east
|
|
53975
|
-
[-1, 0]
|
|
53976
|
-
// west
|
|
54039
|
+
// east
|
|
54040
|
+
[-1, 0],
|
|
54041
|
+
// west
|
|
54042
|
+
[D, -D],
|
|
54043
|
+
// NE
|
|
54044
|
+
[D, D],
|
|
54045
|
+
// SE
|
|
54046
|
+
[-D, -D],
|
|
54047
|
+
// NW
|
|
54048
|
+
[-D, D]
|
|
54049
|
+
// SW
|
|
53977
54050
|
];
|
|
53978
54051
|
let best = null;
|
|
53979
54052
|
for (const [dx, dy] of dirs) {
|
|
@@ -54023,13 +54096,16 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54023
54096
|
(p) => segmentRectOverlap(cx, cy, innerX, innerY, p)
|
|
54024
54097
|
))
|
|
54025
54098
|
continue;
|
|
54026
|
-
|
|
54099
|
+
const clear = clearanceAt(ccx, ccy, dx, dy);
|
|
54100
|
+
const better = best === null || clear > best.clear + CLEAR_TOL || clear >= best.clear - CLEAR_TOL && len < best.len;
|
|
54101
|
+
if (better)
|
|
54027
54102
|
best = {
|
|
54028
54103
|
x: ccx,
|
|
54029
54104
|
y: ccy,
|
|
54030
54105
|
rect,
|
|
54031
54106
|
leader: [cx, cy, innerX, innerY],
|
|
54032
|
-
len
|
|
54107
|
+
len,
|
|
54108
|
+
clear
|
|
54033
54109
|
};
|
|
54034
54110
|
}
|
|
54035
54111
|
return best ? { x: best.x, y: best.y, rect: best.rect, leader: best.leader } : null;
|
|
@@ -54690,6 +54766,9 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54690
54766
|
const nx = -dy / len;
|
|
54691
54767
|
const ny = dx / len;
|
|
54692
54768
|
const perp = labelH + 2;
|
|
54769
|
+
const stroke2 = lg.width ?? W_MIN;
|
|
54770
|
+
const clearW = stroke2 / 2 + labelH / 2 + 3;
|
|
54771
|
+
const offList = stroke2 >= 4 ? [clearW, -clearW, clearW + perp, -(clearW + perp), 0] : [0, perp, -perp, 2 * perp, -2 * perp];
|
|
54693
54772
|
const pm = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
54694
54773
|
lg.d
|
|
54695
54774
|
);
|
|
@@ -54709,7 +54788,7 @@ function layoutMap(resolvedIn, data, size, opts) {
|
|
|
54709
54788
|
return [a.cx + dx * t, a.cy + dy * t];
|
|
54710
54789
|
};
|
|
54711
54790
|
const candidates = [];
|
|
54712
|
-
for (const off of
|
|
54791
|
+
for (const off of offList)
|
|
54713
54792
|
for (const t of T_LIST) {
|
|
54714
54793
|
const [bx, by] = pointAt(t);
|
|
54715
54794
|
candidates.push([bx + nx * off, by + ny * off - 4]);
|
|
@@ -55460,6 +55539,12 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
55460
55539
|
}
|
|
55461
55540
|
}
|
|
55462
55541
|
wireSync(p, leg.lineNumber);
|
|
55542
|
+
if (leg.value !== void 0) {
|
|
55543
|
+
p.attr("data-value", leg.value);
|
|
55544
|
+
p.append("title").text(
|
|
55545
|
+
leg.label !== void 0 ? `${leg.label}: ${leg.value}` : `${leg.value}`
|
|
55546
|
+
);
|
|
55547
|
+
}
|
|
55463
55548
|
if (arrowTri) {
|
|
55464
55549
|
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);
|
|
55465
55550
|
}
|
package/docs/ai-integration.md
CHANGED
|
@@ -167,6 +167,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
167
167
|
|
|
168
168
|
**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.
|
|
169
169
|
|
|
170
|
+
**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.
|
|
171
|
+
|
|
170
172
|
### Common examples (curated, parse-clean)
|
|
171
173
|
|
|
172
174
|
_The most common types, inline so you can generate them without a fetch. For the other 37, get the per-type section (see below)._
|
|
@@ -152,6 +152,8 @@ Valid markup is the floor, not the goal. A good diagram reads at a glance. Apply
|
|
|
152
152
|
| `function` | mathematical expressions (colon required: `f(x): x^2`) |
|
|
153
153
|
|
|
154
154
|
**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.
|
|
155
|
+
|
|
156
|
+
**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.
|
|
155
157
|
<!-- AI-CORE:TYPE-INDEX end -->
|
|
156
158
|
|
|
157
159
|
<!-- The grouped data-chart / matrix ids share one documented section. This map is the single source of truth for which TYPE block each id resolves to (read by gen-ai-core.mjs and the MCP slicer). -->
|
|
@@ -3264,15 +3266,17 @@ poi Austin red # direct marker color (trailing token, §1.5
|
|
|
3264
3266
|
|
|
3265
3267
|
### Routes & connectors
|
|
3266
3268
|
|
|
3267
|
-
`route <origin
|
|
3269
|
+
`route <origin>` — an ordered, auto-numbered voyage; the origin gets a distinct marker, and the header takes **no shape option**. Each indented line is a `<arrow> destination` leg that continues from the previous stop, using the same indented arrow idiom as a sitemap — a leg is an edge (in-arrow label, `value:` thickness, and the **arrow glyph alone sets shape**: `-…->` straight, `~…~>` arc, mixable per leg). The **arrow is required and must be directional** — a bare destination errors, and so does an undirected `--`/`~~` glyph (a voyage always flows forward; use `->`/`~>`). A **tag on a leg colours the LINE** (categorical leg-type colouring — flights vs cruises vs trains); `label:`/`as` still name the destination stop. Repeat the origin as a leg's destination to close a loop (no second marker):
|
|
3268
3270
|
|
|
3269
3271
|
```
|
|
3270
|
-
route Miami
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3272
|
+
route Miami
|
|
3273
|
+
~weigh anchor~> Havana value: 40 # arc leg (its own ~> glyph)
|
|
3274
|
+
~> Kingston
|
|
3275
|
+
~> Miami # destination == origin → closed loop
|
|
3274
3276
|
```
|
|
3275
3277
|
|
|
3278
|
+
There is no header `style:` — bow a whole voyage by using `~>` on every leg.
|
|
3279
|
+
|
|
3276
3280
|
Native edges handle any other connection (no `link`/`leg` keyword). A token draws an arrowhead iff it ends in `>`, an arc iff it starts with `~`; drop the `>` for a plain line when an arrow would mislead. The label always sits between the delimiters:
|
|
3277
3281
|
|
|
3278
3282
|
| | no label | labeled |
|
|
@@ -3292,9 +3296,9 @@ JFK ~daily~> LHR # (NOT indented — indented legs are only valid
|
|
|
3292
3296
|
JFK ~2x daily~> SFO # inside a `route` block, which is an ordered voyage)
|
|
3293
3297
|
```
|
|
3294
3298
|
|
|
3295
|
-
Each native edge is ONE full line — `<origin> <connector> <destination>`.
|
|
3299
|
+
Each native edge is ONE full line — `<origin> <connector> <destination>`. A top-level indented `-> dest` with no parent errors as "Malformed edge"; to fan out, either repeat the origin on each line as above OR indent the spokes under a `poi` (a hub — `poi JFK` then indented `~> LAX`, `~> LHR`). An indented child under a `poi` that names a place **must carry an arrow** — a bare name there errors as a malformed hub edge. Endpoints auto-create their POIs, so a connected map needs no separate `poi` lines.
|
|
3296
3300
|
|
|
3297
|
-
There is no geographic path-finding and no `surface:` — legs are plain straight or arced geometry (
|
|
3301
|
+
There is no geographic path-finding and no `surface:` — legs are plain straight or arced geometry (use a `~>` glyph to bow one) and may cross land.
|
|
3298
3302
|
|
|
3299
3303
|
**Tagging the line.** A tag value on any connector or route leg colours the **line itself** (the universal tag model applied to edges) — the "trip-leg type" idiom. Edge-only tag groups show in the legend as a line-colour key; hovering a legend entry dims the lines that don't match. To categorise a **stop** instead, tag its own `poi` line. An edge-only tag group never tints regions or suppresses the colorize dress.
|
|
3300
3304
|
|