@diagrammo/dgmo 0.6.0 → 0.6.2
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 +76 -0
- package/dist/cli.cjs +164 -162
- package/dist/index.cjs +1146 -647
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -21
- package/dist/index.d.ts +9 -21
- package/dist/index.js +1146 -647
- package/dist/index.js.map +1 -1
- package/docs/ai-integration.md +33 -50
- package/package.json +4 -3
- package/src/c4/layout.ts +75 -72
- package/src/c4/renderer.ts +122 -119
- package/src/cli.ts +130 -40
- package/src/d3.ts +55 -35
- package/src/echarts.ts +24 -24
- package/src/er/classify.ts +206 -0
- package/src/er/layout.ts +259 -94
- package/src/er/renderer.ts +246 -26
- package/src/index.ts +2 -2
- package/src/infra/compute.ts +1 -21
- package/src/infra/layout.ts +60 -13
- package/src/infra/parser.ts +5 -32
- package/src/infra/renderer.ts +403 -196
- package/src/infra/types.ts +1 -11
- package/src/initiative-status/layout.ts +46 -27
- package/src/kanban/renderer.ts +28 -24
- package/src/org/renderer.ts +24 -23
- package/src/render.ts +2 -2
- package/src/sequence/renderer.ts +24 -19
- package/src/sitemap/layout.ts +7 -14
- package/src/sitemap/renderer.ts +30 -29
- package/src/utils/legend-constants.ts +25 -0
- package/.claude/skills/dgmo-chart/SKILL.md +0 -141
- package/.claude/skills/dgmo-flowchart/SKILL.md +0 -61
- package/.claude/skills/dgmo-generate/SKILL.md +0 -59
- package/.claude/skills/dgmo-sequence/SKILL.md +0 -104
package/dist/index.cjs
CHANGED
|
@@ -1467,6 +1467,29 @@ var init_tag_groups = __esm({
|
|
|
1467
1467
|
}
|
|
1468
1468
|
});
|
|
1469
1469
|
|
|
1470
|
+
// src/utils/legend-constants.ts
|
|
1471
|
+
var LEGEND_HEIGHT, LEGEND_PILL_PAD, LEGEND_PILL_FONT_SIZE, LEGEND_PILL_FONT_W, LEGEND_CAPSULE_PAD, LEGEND_DOT_R, LEGEND_ENTRY_FONT_SIZE, LEGEND_ENTRY_FONT_W, LEGEND_ENTRY_DOT_GAP, LEGEND_ENTRY_TRAIL, LEGEND_GROUP_GAP, LEGEND_EYE_SIZE, LEGEND_EYE_GAP, EYE_OPEN_PATH, EYE_CLOSED_PATH;
|
|
1472
|
+
var init_legend_constants = __esm({
|
|
1473
|
+
"src/utils/legend-constants.ts"() {
|
|
1474
|
+
"use strict";
|
|
1475
|
+
LEGEND_HEIGHT = 28;
|
|
1476
|
+
LEGEND_PILL_PAD = 16;
|
|
1477
|
+
LEGEND_PILL_FONT_SIZE = 11;
|
|
1478
|
+
LEGEND_PILL_FONT_W = LEGEND_PILL_FONT_SIZE * 0.6;
|
|
1479
|
+
LEGEND_CAPSULE_PAD = 4;
|
|
1480
|
+
LEGEND_DOT_R = 4;
|
|
1481
|
+
LEGEND_ENTRY_FONT_SIZE = 10;
|
|
1482
|
+
LEGEND_ENTRY_FONT_W = LEGEND_ENTRY_FONT_SIZE * 0.6;
|
|
1483
|
+
LEGEND_ENTRY_DOT_GAP = 4;
|
|
1484
|
+
LEGEND_ENTRY_TRAIL = 8;
|
|
1485
|
+
LEGEND_GROUP_GAP = 12;
|
|
1486
|
+
LEGEND_EYE_SIZE = 14;
|
|
1487
|
+
LEGEND_EYE_GAP = 6;
|
|
1488
|
+
EYE_OPEN_PATH = "M1 7s2.5-5 6-5 6 5 6 5-2.5 5-6 5-6-5-6-5z M7 9.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z";
|
|
1489
|
+
EYE_CLOSED_PATH = "M2.5 2.5l9 9 M1.5 7s2.2-4 5.5-4c1.2 0 2.2.5 3 1.1 M12.5 7s-2.2 4-5.5 4c-1.2 0-2.2-.5-3-1.1";
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1470
1493
|
// src/sequence/participant-inference.ts
|
|
1471
1494
|
function inferParticipantType(name) {
|
|
1472
1495
|
for (const rule of PARTICIPANT_RULES) {
|
|
@@ -4777,10 +4800,12 @@ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacit
|
|
|
4777
4800
|
if (type === "category" && data && data.length > 0) {
|
|
4778
4801
|
const maxLabelLen = Math.max(...data.map((l) => l.length));
|
|
4779
4802
|
const count = data.length;
|
|
4780
|
-
|
|
4781
|
-
|
|
4803
|
+
const step = intervalOverride != null && intervalOverride > 0 ? intervalOverride + 1 : 1;
|
|
4804
|
+
const visibleCount = Math.ceil(count / step);
|
|
4805
|
+
if (visibleCount > 10 || maxLabelLen > 20) catFontSize = 10;
|
|
4806
|
+
else if (visibleCount > 5 || maxLabelLen > 14) catFontSize = 11;
|
|
4782
4807
|
else if (maxLabelLen > 8) catFontSize = 12;
|
|
4783
|
-
if (chartWidthHint && count > 0) {
|
|
4808
|
+
if ((intervalOverride == null || intervalOverride === 0) && chartWidthHint && count > 0) {
|
|
4784
4809
|
const availPerLabel = Math.floor(chartWidthHint * 0.85 / count);
|
|
4785
4810
|
catLabelExtras = {
|
|
4786
4811
|
width: availPerLabel,
|
|
@@ -4798,6 +4823,9 @@ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacit
|
|
|
4798
4823
|
fontFamily: FONT_FAMILY,
|
|
4799
4824
|
...type === "category" && {
|
|
4800
4825
|
interval: intervalOverride ?? 0,
|
|
4826
|
+
// Prevent ECharts auto-rotation: it measures raw slot width (chartWidth/N),
|
|
4827
|
+
// which is too narrow when an interval skips most labels, and rotates to 90°.
|
|
4828
|
+
rotate: 0,
|
|
4801
4829
|
formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2"),
|
|
4802
4830
|
...catLabelExtras
|
|
4803
4831
|
}
|
|
@@ -4873,21 +4901,13 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
|
|
|
4873
4901
|
]
|
|
4874
4902
|
};
|
|
4875
4903
|
}
|
|
4876
|
-
function
|
|
4904
|
+
function buildIntervalStep(labels) {
|
|
4877
4905
|
const count = labels.length;
|
|
4878
|
-
if (count <=
|
|
4879
|
-
const snapSteps = [1, 2,
|
|
4880
|
-
const raw = Math.ceil(count /
|
|
4906
|
+
if (count <= 6) return 0;
|
|
4907
|
+
const snapSteps = [1, 2, 5, 10, 25, 50, 100];
|
|
4908
|
+
const raw = Math.ceil(count / 5);
|
|
4881
4909
|
const N = [...snapSteps].reverse().find((s) => s <= raw) ?? 1;
|
|
4882
|
-
|
|
4883
|
-
for (let i = 0; i < count; i += N) pinned.add(i);
|
|
4884
|
-
for (const era of eras) {
|
|
4885
|
-
const si = labels.indexOf(era.start);
|
|
4886
|
-
const ei = labels.indexOf(era.end);
|
|
4887
|
-
if (si >= 0) pinned.add(si);
|
|
4888
|
-
if (ei >= 0) pinned.add(ei);
|
|
4889
|
-
}
|
|
4890
|
-
return (index) => pinned.has(index);
|
|
4910
|
+
return N - 1;
|
|
4891
4911
|
}
|
|
4892
4912
|
function buildMarkArea(eras, labels, textColor, defaultColor) {
|
|
4893
4913
|
if (eras.length === 0) return void 0;
|
|
@@ -4922,7 +4942,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4922
4942
|
const labels = parsed.data.map((d) => d.label);
|
|
4923
4943
|
const values = parsed.data.map((d) => d.value);
|
|
4924
4944
|
const eras = parsed.eras ?? [];
|
|
4925
|
-
const interval =
|
|
4945
|
+
const interval = buildIntervalStep(labels);
|
|
4926
4946
|
const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
|
|
4927
4947
|
return {
|
|
4928
4948
|
...CHART_BASE,
|
|
@@ -4954,7 +4974,7 @@ function buildMultiLineOption(parsed, palette, textColor, axisLineColor, splitLi
|
|
|
4954
4974
|
const seriesNames = parsed.seriesNames ?? [];
|
|
4955
4975
|
const labels = parsed.data.map((d) => d.label);
|
|
4956
4976
|
const eras = parsed.eras ?? [];
|
|
4957
|
-
const interval =
|
|
4977
|
+
const interval = buildIntervalStep(labels);
|
|
4958
4978
|
const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
|
|
4959
4979
|
const series = seriesNames.map((name, idx) => {
|
|
4960
4980
|
const color = parsed.seriesNameColors?.[idx] ?? colors[idx % colors.length];
|
|
@@ -4998,7 +5018,7 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4998
5018
|
const labels = parsed.data.map((d) => d.label);
|
|
4999
5019
|
const values = parsed.data.map((d) => d.value);
|
|
5000
5020
|
const eras = parsed.eras ?? [];
|
|
5001
|
-
const interval =
|
|
5021
|
+
const interval = buildIntervalStep(labels);
|
|
5002
5022
|
const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
|
|
5003
5023
|
return {
|
|
5004
5024
|
...CHART_BASE,
|
|
@@ -7012,7 +7032,6 @@ function parseInfra(content) {
|
|
|
7012
7032
|
edges: [],
|
|
7013
7033
|
groups: [],
|
|
7014
7034
|
tagGroups: [],
|
|
7015
|
-
scenarios: [],
|
|
7016
7035
|
options: {},
|
|
7017
7036
|
diagnostics: [],
|
|
7018
7037
|
error: null
|
|
@@ -7115,16 +7134,7 @@ function parseInfra(content) {
|
|
|
7115
7134
|
continue;
|
|
7116
7135
|
}
|
|
7117
7136
|
if (/^scenario\s*:/i.test(trimmed)) {
|
|
7118
|
-
|
|
7119
|
-
finishCurrentTagGroup();
|
|
7120
|
-
currentGroup = null;
|
|
7121
|
-
const scenarioName = trimmed.replace(/^scenario\s*:\s*/i, "").trim();
|
|
7122
|
-
const scenario = {
|
|
7123
|
-
name: scenarioName,
|
|
7124
|
-
overrides: {},
|
|
7125
|
-
lineNumber
|
|
7126
|
-
};
|
|
7127
|
-
let scenarioNodeId = null;
|
|
7137
|
+
console.warn("[dgmo warn] scenario syntax is deprecated and will be ignored");
|
|
7128
7138
|
let si = i + 1;
|
|
7129
7139
|
while (si < lines.length) {
|
|
7130
7140
|
const sLine = lines[si];
|
|
@@ -7135,23 +7145,9 @@ function parseInfra(content) {
|
|
|
7135
7145
|
}
|
|
7136
7146
|
const sIndent = sLine.length - sLine.trimStart().length;
|
|
7137
7147
|
if (sIndent === 0) break;
|
|
7138
|
-
if (sIndent <= 2) {
|
|
7139
|
-
scenarioNodeId = nodeId2(sTrimmed.replace(/\|.*$/, "").trim());
|
|
7140
|
-
if (!scenario.overrides[scenarioNodeId]) {
|
|
7141
|
-
scenario.overrides[scenarioNodeId] = {};
|
|
7142
|
-
}
|
|
7143
|
-
} else if (scenarioNodeId) {
|
|
7144
|
-
const pm = sTrimmed.match(PROPERTY_RE);
|
|
7145
|
-
if (pm) {
|
|
7146
|
-
const key = pm[1].toLowerCase();
|
|
7147
|
-
const val = parsePropertyValue(pm[2].trim());
|
|
7148
|
-
scenario.overrides[scenarioNodeId][key] = val;
|
|
7149
|
-
}
|
|
7150
|
-
}
|
|
7151
7148
|
si++;
|
|
7152
7149
|
}
|
|
7153
7150
|
i = si - 1;
|
|
7154
|
-
result.scenarios.push(scenario);
|
|
7155
7151
|
continue;
|
|
7156
7152
|
}
|
|
7157
7153
|
const tagMatch = trimmed.match(TAG_GROUP_RE);
|
|
@@ -7683,14 +7679,14 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
|
|
|
7683
7679
|
const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
|
|
7684
7680
|
const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
|
|
7685
7681
|
if (visibleEntries.length === 0) continue;
|
|
7686
|
-
const pillWidth = group.name.length *
|
|
7682
|
+
const pillWidth = group.name.length * LEGEND_PILL_FONT_W2 + LEGEND_PILL_PAD2;
|
|
7687
7683
|
const minPillWidth = pillWidth;
|
|
7688
7684
|
let entriesWidth = 0;
|
|
7689
7685
|
for (const entry of visibleEntries) {
|
|
7690
|
-
entriesWidth +=
|
|
7686
|
+
entriesWidth += LEGEND_DOT_R2 * 2 + LEGEND_ENTRY_DOT_GAP2 + entry.value.length * LEGEND_ENTRY_FONT_W2 + LEGEND_ENTRY_TRAIL2;
|
|
7691
7687
|
}
|
|
7692
|
-
const eyeSpace = showEyeIcons ?
|
|
7693
|
-
const capsuleWidth =
|
|
7688
|
+
const eyeSpace = showEyeIcons ? LEGEND_EYE_SIZE2 + LEGEND_EYE_GAP2 : 0;
|
|
7689
|
+
const capsuleWidth = LEGEND_CAPSULE_PAD2 * 2 + pillWidth + 4 + eyeSpace + entriesWidth;
|
|
7694
7690
|
groups.push({
|
|
7695
7691
|
name: group.name,
|
|
7696
7692
|
alias: group.alias,
|
|
@@ -7701,9 +7697,9 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
|
|
|
7701
7697
|
x: 0,
|
|
7702
7698
|
y: 0,
|
|
7703
7699
|
width: capsuleWidth,
|
|
7704
|
-
height:
|
|
7700
|
+
height: LEGEND_HEIGHT2,
|
|
7705
7701
|
minifiedWidth: minPillWidth,
|
|
7706
|
-
minifiedHeight:
|
|
7702
|
+
minifiedHeight: LEGEND_HEIGHT2
|
|
7707
7703
|
});
|
|
7708
7704
|
}
|
|
7709
7705
|
return groups;
|
|
@@ -7733,7 +7729,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
7733
7729
|
for (const g of legendGroups2) {
|
|
7734
7730
|
g.x = MARGIN;
|
|
7735
7731
|
g.y = cy;
|
|
7736
|
-
cy +=
|
|
7732
|
+
cy += LEGEND_HEIGHT2 + LEGEND_GROUP_GAP2;
|
|
7737
7733
|
if (g.width > maxWidth2) maxWidth2 = g.width;
|
|
7738
7734
|
}
|
|
7739
7735
|
return {
|
|
@@ -7742,7 +7738,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
7742
7738
|
containers: [],
|
|
7743
7739
|
legend: legendGroups2,
|
|
7744
7740
|
width: maxWidth2 + MARGIN * 2,
|
|
7745
|
-
height: cy -
|
|
7741
|
+
height: cy - LEGEND_GROUP_GAP2 + MARGIN
|
|
7746
7742
|
};
|
|
7747
7743
|
}
|
|
7748
7744
|
injectDefaultMetadata(parsed.roots, parsed.tagGroups);
|
|
@@ -8272,7 +8268,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
8272
8268
|
const effectiveH = (g) => activeTagGroup != null || allExpanded ? g.height : g.minifiedHeight;
|
|
8273
8269
|
if (visibleGroups.length > 0) {
|
|
8274
8270
|
if (legendPosition === "bottom") {
|
|
8275
|
-
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
8271
|
+
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP2;
|
|
8276
8272
|
const neededWidth = totalGroupsWidth + MARGIN * 2;
|
|
8277
8273
|
if (neededWidth > totalWidth) {
|
|
8278
8274
|
finalWidth = neededWidth;
|
|
@@ -8290,22 +8286,22 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
8290
8286
|
for (const g of visibleGroups) {
|
|
8291
8287
|
g.x = cx;
|
|
8292
8288
|
g.y = legendY;
|
|
8293
|
-
cx += effectiveW(g) +
|
|
8289
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP2;
|
|
8294
8290
|
}
|
|
8295
|
-
finalHeight = totalHeight + LEGEND_GAP +
|
|
8291
|
+
finalHeight = totalHeight + LEGEND_GAP + LEGEND_HEIGHT2;
|
|
8296
8292
|
} else {
|
|
8297
|
-
const legendShift =
|
|
8293
|
+
const legendShift = LEGEND_HEIGHT2 + LEGEND_GROUP_GAP2;
|
|
8298
8294
|
for (const n of layoutNodes) n.y += legendShift;
|
|
8299
8295
|
for (const c of containers) c.y += legendShift;
|
|
8300
8296
|
for (const e of layoutEdges) {
|
|
8301
8297
|
for (const p of e.points) p.y += legendShift;
|
|
8302
8298
|
}
|
|
8303
|
-
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
8299
|
+
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP2;
|
|
8304
8300
|
let cx = MARGIN;
|
|
8305
8301
|
for (const g of visibleGroups) {
|
|
8306
8302
|
g.x = cx;
|
|
8307
8303
|
g.y = MARGIN;
|
|
8308
|
-
cx += effectiveW(g) +
|
|
8304
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP2;
|
|
8309
8305
|
}
|
|
8310
8306
|
finalHeight += legendShift;
|
|
8311
8307
|
const neededWidth = totalGroupsWidth + MARGIN * 2;
|
|
@@ -8323,7 +8319,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
8323
8319
|
height: finalHeight
|
|
8324
8320
|
};
|
|
8325
8321
|
}
|
|
8326
|
-
var import_d3_hierarchy, CHAR_WIDTH, META_LINE_HEIGHT, HEADER_HEIGHT, SEPARATOR_GAP, CARD_H_PAD, CARD_V_PAD, MIN_CARD_WIDTH, H_GAP, V_GAP, MARGIN, CONTAINER_PAD_X, CONTAINER_PAD_BOTTOM, CONTAINER_LABEL_HEIGHT, CONTAINER_META_LINE_HEIGHT, STACK_V_GAP, LEGEND_GAP,
|
|
8322
|
+
var import_d3_hierarchy, CHAR_WIDTH, META_LINE_HEIGHT, HEADER_HEIGHT, SEPARATOR_GAP, CARD_H_PAD, CARD_V_PAD, MIN_CARD_WIDTH, H_GAP, V_GAP, MARGIN, CONTAINER_PAD_X, CONTAINER_PAD_BOTTOM, CONTAINER_LABEL_HEIGHT, CONTAINER_META_LINE_HEIGHT, STACK_V_GAP, LEGEND_GAP, LEGEND_HEIGHT2, LEGEND_PILL_PAD2, LEGEND_PILL_FONT_W2, LEGEND_CAPSULE_PAD2, LEGEND_DOT_R2, LEGEND_ENTRY_FONT_W2, LEGEND_ENTRY_DOT_GAP2, LEGEND_ENTRY_TRAIL2, LEGEND_GROUP_GAP2, LEGEND_EYE_SIZE2, LEGEND_EYE_GAP2;
|
|
8327
8323
|
var init_layout = __esm({
|
|
8328
8324
|
"src/org/layout.ts"() {
|
|
8329
8325
|
"use strict";
|
|
@@ -8345,17 +8341,17 @@ var init_layout = __esm({
|
|
|
8345
8341
|
CONTAINER_META_LINE_HEIGHT = 16;
|
|
8346
8342
|
STACK_V_GAP = 20;
|
|
8347
8343
|
LEGEND_GAP = 30;
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8344
|
+
LEGEND_HEIGHT2 = 28;
|
|
8345
|
+
LEGEND_PILL_PAD2 = 16;
|
|
8346
|
+
LEGEND_PILL_FONT_W2 = 11 * 0.6;
|
|
8347
|
+
LEGEND_CAPSULE_PAD2 = 4;
|
|
8348
|
+
LEGEND_DOT_R2 = 4;
|
|
8349
|
+
LEGEND_ENTRY_FONT_W2 = 10 * 0.6;
|
|
8350
|
+
LEGEND_ENTRY_DOT_GAP2 = 4;
|
|
8351
|
+
LEGEND_ENTRY_TRAIL2 = 8;
|
|
8352
|
+
LEGEND_GROUP_GAP2 = 12;
|
|
8353
|
+
LEGEND_EYE_SIZE2 = 14;
|
|
8354
|
+
LEGEND_EYE_GAP2 = 6;
|
|
8359
8355
|
}
|
|
8360
8356
|
});
|
|
8361
8357
|
|
|
@@ -8452,11 +8448,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
|
|
|
8452
8448
|
if (width <= 0 || height <= 0) return;
|
|
8453
8449
|
const titleOffset = parsed.title ? TITLE_HEIGHT : 0;
|
|
8454
8450
|
const legendOnly = layout.nodes.length === 0;
|
|
8455
|
-
const legendPosition = parsed.options?.["legend-position"] ?? "
|
|
8451
|
+
const legendPosition = parsed.options?.["legend-position"] ?? "bottom";
|
|
8456
8452
|
const hasLegend = layout.legend.length > 0;
|
|
8457
|
-
const layoutLegendShift =
|
|
8453
|
+
const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
8458
8454
|
const fixedLegend = !exportDims && hasLegend && !legendOnly;
|
|
8459
|
-
const legendReserve = fixedLegend ?
|
|
8455
|
+
const legendReserve = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
|
|
8460
8456
|
const fixedTitle = !exportDims && !!parsed.title;
|
|
8461
8457
|
const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
|
|
8462
8458
|
const diagramW = layout.width;
|
|
@@ -8611,56 +8607,60 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
|
|
|
8611
8607
|
if (fixedLegend && visibleGroups.length > 0) {
|
|
8612
8608
|
fixedPositions = /* @__PURE__ */ new Map();
|
|
8613
8609
|
const effectiveW = (g) => activeTagGroup != null ? g.width : g.minifiedWidth;
|
|
8614
|
-
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
8610
|
+
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
8615
8611
|
let cx = (width - totalW) / 2;
|
|
8616
8612
|
for (const g of visibleGroups) {
|
|
8617
8613
|
fixedPositions.set(g.name, cx);
|
|
8618
|
-
cx += effectiveW(g) +
|
|
8614
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
8619
8615
|
}
|
|
8620
8616
|
}
|
|
8621
|
-
const
|
|
8617
|
+
const legendParentBase = fixedLegend ? svg.append("g").attr("class", "org-legend-fixed").attr(
|
|
8622
8618
|
"transform",
|
|
8623
|
-
legendPosition === "bottom" ? `translate(0, ${height - DIAGRAM_PADDING -
|
|
8619
|
+
legendPosition === "bottom" ? `translate(0, ${height - DIAGRAM_PADDING - LEGEND_HEIGHT})` : `translate(0, ${DIAGRAM_PADDING + titleReserve})`
|
|
8624
8620
|
) : contentG;
|
|
8621
|
+
const legendParent = legendParentBase;
|
|
8622
|
+
if (fixedLegend && activeTagGroup) {
|
|
8623
|
+
legendParentBase.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
8624
|
+
}
|
|
8625
8625
|
for (const group of visibleGroups) {
|
|
8626
8626
|
const isActive = legendOnly || activeTagGroup != null && group.name.toLowerCase() === activeTagGroup.toLowerCase();
|
|
8627
8627
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
8628
8628
|
const pillLabel = group.name;
|
|
8629
|
-
const pillWidth = pillLabel.length *
|
|
8629
|
+
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
8630
8630
|
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
8631
8631
|
const gY = fixedPositions ? 0 : group.y;
|
|
8632
8632
|
const gEl = legendParent.append("g").attr("transform", `translate(${gX}, ${gY})`).attr("class", "org-legend-group").attr("data-legend-group", group.name.toLowerCase()).style("cursor", legendOnly ? "default" : "pointer");
|
|
8633
8633
|
if (isActive) {
|
|
8634
|
-
gEl.append("rect").attr("width", group.width).attr("height",
|
|
8634
|
+
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
8635
8635
|
}
|
|
8636
|
-
const pillXOff = isActive ?
|
|
8637
|
-
const pillYOff = isActive ?
|
|
8638
|
-
const pillH =
|
|
8636
|
+
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
8637
|
+
const pillYOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
8638
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
8639
8639
|
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isActive ? palette.bg : groupBg);
|
|
8640
8640
|
if (isActive) {
|
|
8641
8641
|
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
8642
8642
|
}
|
|
8643
|
-
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y",
|
|
8643
|
+
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(pillLabel);
|
|
8644
8644
|
if (isActive && fixedLegend) {
|
|
8645
8645
|
const groupKey = group.name.toLowerCase();
|
|
8646
8646
|
const isHidden = hiddenAttributes?.has(groupKey) ?? false;
|
|
8647
|
-
const eyeX = pillXOff + pillWidth +
|
|
8648
|
-
const eyeY = (
|
|
8647
|
+
const eyeX = pillXOff + pillWidth + LEGEND_EYE_GAP;
|
|
8648
|
+
const eyeY = (LEGEND_HEIGHT - LEGEND_EYE_SIZE) / 2;
|
|
8649
8649
|
const hitPad = 6;
|
|
8650
8650
|
const eyeG = gEl.append("g").attr("class", "org-legend-eye").attr("data-legend-visibility", groupKey).style("cursor", "pointer").attr("opacity", isHidden ? 0.4 : 0.7);
|
|
8651
|
-
eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width",
|
|
8651
|
+
eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width", LEGEND_EYE_SIZE + hitPad * 2).attr("height", LEGEND_EYE_SIZE + hitPad * 2).attr("fill", "transparent").attr("pointer-events", "all");
|
|
8652
8652
|
eyeG.append("path").attr("d", isHidden ? EYE_CLOSED_PATH : EYE_OPEN_PATH).attr("transform", `translate(${eyeX}, ${eyeY})`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.2).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
8653
8653
|
}
|
|
8654
8654
|
if (isActive) {
|
|
8655
|
-
const eyeShift = fixedLegend ?
|
|
8655
|
+
const eyeShift = fixedLegend ? LEGEND_EYE_SIZE + LEGEND_EYE_GAP : 0;
|
|
8656
8656
|
let entryX = pillXOff + pillWidth + 4 + eyeShift;
|
|
8657
8657
|
for (const entry of group.entries) {
|
|
8658
8658
|
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
8659
|
-
entryG.append("circle").attr("cx", entryX +
|
|
8660
|
-
const textX = entryX +
|
|
8659
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
8660
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
8661
8661
|
const entryLabel = entry.value;
|
|
8662
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
8663
|
-
entryX = textX + entryLabel.length *
|
|
8662
|
+
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).text(entryLabel);
|
|
8663
|
+
entryX = textX + entryLabel.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
8664
8664
|
}
|
|
8665
8665
|
}
|
|
8666
8666
|
}
|
|
@@ -8699,7 +8699,7 @@ function renderOrgForExport(content, theme, palette) {
|
|
|
8699
8699
|
document.body.removeChild(container);
|
|
8700
8700
|
}
|
|
8701
8701
|
}
|
|
8702
|
-
var d3Selection, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, TITLE_FONT_SIZE, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET,
|
|
8702
|
+
var d3Selection, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, TITLE_FONT_SIZE, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, LEGEND_FIXED_GAP;
|
|
8703
8703
|
var init_renderer = __esm({
|
|
8704
8704
|
"src/org/renderer.ts"() {
|
|
8705
8705
|
"use strict";
|
|
@@ -8708,6 +8708,7 @@ var init_renderer = __esm({
|
|
|
8708
8708
|
init_color_utils();
|
|
8709
8709
|
init_parser4();
|
|
8710
8710
|
init_layout();
|
|
8711
|
+
init_legend_constants();
|
|
8711
8712
|
DIAGRAM_PADDING = 20;
|
|
8712
8713
|
MAX_SCALE = 3;
|
|
8713
8714
|
TITLE_HEIGHT = 30;
|
|
@@ -8727,22 +8728,7 @@ var init_renderer = __esm({
|
|
|
8727
8728
|
CONTAINER_HEADER_HEIGHT = 28;
|
|
8728
8729
|
COLLAPSE_BAR_HEIGHT = 6;
|
|
8729
8730
|
COLLAPSE_BAR_INSET = 0;
|
|
8730
|
-
LEGEND_HEIGHT2 = 28;
|
|
8731
|
-
LEGEND_PILL_PAD2 = 16;
|
|
8732
|
-
LEGEND_PILL_FONT_SIZE = 11;
|
|
8733
|
-
LEGEND_PILL_FONT_W2 = LEGEND_PILL_FONT_SIZE * 0.6;
|
|
8734
|
-
LEGEND_CAPSULE_PAD2 = 4;
|
|
8735
|
-
LEGEND_DOT_R2 = 4;
|
|
8736
|
-
LEGEND_ENTRY_FONT_SIZE = 10;
|
|
8737
|
-
LEGEND_ENTRY_FONT_W2 = LEGEND_ENTRY_FONT_SIZE * 0.6;
|
|
8738
|
-
LEGEND_ENTRY_DOT_GAP2 = 4;
|
|
8739
|
-
LEGEND_ENTRY_TRAIL2 = 8;
|
|
8740
|
-
LEGEND_GROUP_GAP2 = 12;
|
|
8741
|
-
LEGEND_EYE_SIZE2 = 14;
|
|
8742
|
-
LEGEND_EYE_GAP2 = 6;
|
|
8743
8731
|
LEGEND_FIXED_GAP = 8;
|
|
8744
|
-
EYE_OPEN_PATH = "M1 7s2.5-5 6-5 6 5 6 5-2.5 5-6 5-6-5-6-5z M7 9.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z";
|
|
8745
|
-
EYE_CLOSED_PATH = "M2.5 2.5l9 9 M1.5 7s2.2-4 5.5-4c1.2 0 2.2.5 3 1.1 M12.5 7s-2.2 4-5.5 4c-1.2 0-2.2-.5-3-1.1";
|
|
8746
8732
|
}
|
|
8747
8733
|
});
|
|
8748
8734
|
|
|
@@ -9166,23 +9152,17 @@ function layoutSitemap(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, e
|
|
|
9166
9152
|
const effectiveW = (g2) => activeTagGroup != null || allExpanded ? g2.width : g2.minifiedWidth;
|
|
9167
9153
|
if (visibleGroups.length > 0) {
|
|
9168
9154
|
const legendShift = LEGEND_HEIGHT3 + LEGEND_GROUP_GAP3;
|
|
9169
|
-
for (const n of layoutNodes) n.y += legendShift;
|
|
9170
|
-
for (const c of layoutContainers) c.y += legendShift;
|
|
9171
|
-
for (const e of layoutEdges) {
|
|
9172
|
-
for (const p of e.points) p.y += legendShift;
|
|
9173
|
-
}
|
|
9174
9155
|
const totalGroupsWidth = visibleGroups.reduce((s, g2) => s + effectiveW(g2), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP3;
|
|
9175
|
-
|
|
9156
|
+
const neededWidth = totalGroupsWidth + MARGIN2 * 2;
|
|
9157
|
+
if (neededWidth > totalWidth) totalWidth = neededWidth;
|
|
9158
|
+
let cx = (totalWidth - totalGroupsWidth) / 2;
|
|
9159
|
+
const legendY = totalHeight + LEGEND_GROUP_GAP3;
|
|
9176
9160
|
for (const g2 of visibleGroups) {
|
|
9177
9161
|
g2.x = cx;
|
|
9178
|
-
g2.y =
|
|
9162
|
+
g2.y = legendY;
|
|
9179
9163
|
cx += effectiveW(g2) + LEGEND_GROUP_GAP3;
|
|
9180
9164
|
}
|
|
9181
9165
|
totalHeight += legendShift;
|
|
9182
|
-
const neededWidth = totalGroupsWidth + MARGIN2 * 2;
|
|
9183
|
-
if (neededWidth > totalWidth) {
|
|
9184
|
-
totalWidth = neededWidth;
|
|
9185
|
-
}
|
|
9186
9166
|
}
|
|
9187
9167
|
return {
|
|
9188
9168
|
nodes: layoutNodes,
|
|
@@ -9376,25 +9356,26 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
9376
9356
|
const height = exportDims?.height ?? container.clientHeight;
|
|
9377
9357
|
if (width <= 0 || height <= 0) return;
|
|
9378
9358
|
const hasLegend = layout.legend.length > 0;
|
|
9379
|
-
const layoutLegendShift =
|
|
9359
|
+
const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
9380
9360
|
const fixedLegend = !exportDims && hasLegend;
|
|
9381
9361
|
const fixedTitle = fixedLegend && !!parsed.title;
|
|
9382
9362
|
const fixedTitleH = fixedTitle ? TITLE_HEIGHT2 : 0;
|
|
9383
|
-
const legendReserveH = fixedLegend ?
|
|
9384
|
-
const
|
|
9363
|
+
const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP2 : 0;
|
|
9364
|
+
const fixedReserveTop = fixedTitleH;
|
|
9365
|
+
const fixedReserveBottom = legendReserveH;
|
|
9385
9366
|
const titleOffset = !fixedTitle && parsed.title ? TITLE_HEIGHT2 : 0;
|
|
9386
9367
|
const diagramW = layout.width;
|
|
9387
9368
|
let diagramH = layout.height + titleOffset;
|
|
9388
9369
|
if (fixedLegend) {
|
|
9389
9370
|
diagramH -= layoutLegendShift;
|
|
9390
9371
|
}
|
|
9391
|
-
const availH = height - DIAGRAM_PADDING2 * 2 -
|
|
9372
|
+
const availH = height - DIAGRAM_PADDING2 * 2 - fixedReserveTop - fixedReserveBottom;
|
|
9392
9373
|
const scaleX = (width - DIAGRAM_PADDING2 * 2) / diagramW;
|
|
9393
9374
|
const scaleY = availH / diagramH;
|
|
9394
9375
|
const scale = Math.min(MAX_SCALE2, scaleX, scaleY);
|
|
9395
9376
|
const scaledW = diagramW * scale;
|
|
9396
9377
|
const offsetX = (width - scaledW) / 2;
|
|
9397
|
-
const offsetY = DIAGRAM_PADDING2 +
|
|
9378
|
+
const offsetY = DIAGRAM_PADDING2 + fixedReserveTop;
|
|
9398
9379
|
const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("font-family", FONT_FAMILY);
|
|
9399
9380
|
const defs = svg.append("defs");
|
|
9400
9381
|
defs.append("marker").attr("id", "sm-arrow").attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", palette.textMuted);
|
|
@@ -9535,7 +9516,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
9535
9516
|
titleEl.text(parsed.title);
|
|
9536
9517
|
}
|
|
9537
9518
|
if (fixedLegend) {
|
|
9538
|
-
const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr("transform", `translate(0, ${DIAGRAM_PADDING2
|
|
9519
|
+
const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr("transform", `translate(0, ${height - DIAGRAM_PADDING2 - LEGEND_HEIGHT})`);
|
|
9520
|
+
if (activeTagGroup) {
|
|
9521
|
+
legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
9522
|
+
}
|
|
9539
9523
|
renderLegend(legendParent, layout.legend, palette, isDark, activeTagGroup, width, hiddenAttributes);
|
|
9540
9524
|
}
|
|
9541
9525
|
}
|
|
@@ -9547,49 +9531,49 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
|
|
|
9547
9531
|
if (fixedWidth != null && visibleGroups.length > 0) {
|
|
9548
9532
|
fixedPositions = /* @__PURE__ */ new Map();
|
|
9549
9533
|
const effectiveW = (g) => activeTagGroup != null ? g.width : g.minifiedWidth;
|
|
9550
|
-
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
9534
|
+
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
9551
9535
|
let cx = (fixedWidth - totalW) / 2;
|
|
9552
9536
|
for (const g of visibleGroups) {
|
|
9553
9537
|
fixedPositions.set(g.name, cx);
|
|
9554
|
-
cx += effectiveW(g) +
|
|
9538
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
9555
9539
|
}
|
|
9556
9540
|
}
|
|
9557
9541
|
for (const group of visibleGroups) {
|
|
9558
9542
|
const isActive = activeTagGroup != null;
|
|
9559
|
-
const pillW = group.name.length *
|
|
9543
|
+
const pillW = group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
9560
9544
|
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
9561
9545
|
const gY = fixedPositions ? 0 : group.y;
|
|
9562
9546
|
const legendG = parent.append("g").attr("transform", `translate(${gX}, ${gY})`).attr("class", "sitemap-legend-group").attr("data-legend-group", group.name.toLowerCase()).style("cursor", "pointer");
|
|
9563
9547
|
if (isActive) {
|
|
9564
|
-
legendG.append("rect").attr("width", group.width).attr("height",
|
|
9548
|
+
legendG.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
9565
9549
|
}
|
|
9566
|
-
const pillXOff = isActive ?
|
|
9567
|
-
const pillYOff = isActive ?
|
|
9568
|
-
const pillH =
|
|
9550
|
+
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
9551
|
+
const pillYOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
9552
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
9569
9553
|
legendG.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillW).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isActive ? palette.bg : groupBg);
|
|
9570
9554
|
if (isActive) {
|
|
9571
9555
|
legendG.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillW).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
9572
9556
|
}
|
|
9573
|
-
legendG.append("text").attr("x", pillXOff + pillW / 2).attr("y",
|
|
9557
|
+
legendG.append("text").attr("x", pillXOff + pillW / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(group.name);
|
|
9574
9558
|
if (isActive && fixedWidth != null) {
|
|
9575
9559
|
const groupKey = group.name.toLowerCase();
|
|
9576
9560
|
const isHidden = hiddenAttributes?.has(groupKey) ?? false;
|
|
9577
|
-
const eyeX = pillXOff + pillW +
|
|
9578
|
-
const eyeY = (
|
|
9561
|
+
const eyeX = pillXOff + pillW + LEGEND_EYE_GAP;
|
|
9562
|
+
const eyeY = (LEGEND_HEIGHT - LEGEND_EYE_SIZE) / 2;
|
|
9579
9563
|
const hitPad = 6;
|
|
9580
9564
|
const eyeG = legendG.append("g").attr("class", "sitemap-legend-eye").attr("data-legend-visibility", groupKey).style("cursor", "pointer").attr("opacity", isHidden ? 0.4 : 0.7);
|
|
9581
|
-
eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width",
|
|
9582
|
-
eyeG.append("path").attr("d", isHidden ?
|
|
9565
|
+
eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width", LEGEND_EYE_SIZE + hitPad * 2).attr("height", LEGEND_EYE_SIZE + hitPad * 2).attr("fill", "transparent").attr("pointer-events", "all");
|
|
9566
|
+
eyeG.append("path").attr("d", isHidden ? EYE_CLOSED_PATH : EYE_OPEN_PATH).attr("transform", `translate(${eyeX}, ${eyeY})`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.2).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
9583
9567
|
}
|
|
9584
9568
|
if (isActive) {
|
|
9585
|
-
const eyeShift = fixedWidth != null ?
|
|
9569
|
+
const eyeShift = fixedWidth != null ? LEGEND_EYE_SIZE + LEGEND_EYE_GAP : 0;
|
|
9586
9570
|
let entryX = pillXOff + pillW + 4 + eyeShift;
|
|
9587
9571
|
for (const entry of group.entries) {
|
|
9588
9572
|
const entryG = legendG.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
9589
|
-
entryG.append("circle").attr("cx", entryX +
|
|
9590
|
-
const textX = entryX +
|
|
9591
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
9592
|
-
entryX = textX + entry.value.length *
|
|
9573
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
9574
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
9575
|
+
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).text(entry.value);
|
|
9576
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
9593
9577
|
}
|
|
9594
9578
|
}
|
|
9595
9579
|
}
|
|
@@ -9635,7 +9619,7 @@ async function renderSitemapForExport(content, theme, palette) {
|
|
|
9635
9619
|
const brandColor = theme === "transparent" ? "#888" : effectivePalette.textMuted;
|
|
9636
9620
|
return injectBranding2(svgHtml, brandColor);
|
|
9637
9621
|
}
|
|
9638
|
-
var d3Selection2, d3Shape, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, TITLE_FONT_SIZE2, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT4, HEADER_HEIGHT4, SEPARATOR_GAP4, EDGE_STROKE_WIDTH2, NODE_STROKE_WIDTH2, CARD_RADIUS2, CONTAINER_RADIUS2, CONTAINER_LABEL_FONT_SIZE2, CONTAINER_META_FONT_SIZE2, CONTAINER_META_LINE_HEIGHT4, CONTAINER_HEADER_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, COLLAPSE_BAR_HEIGHT2,
|
|
9622
|
+
var d3Selection2, d3Shape, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, TITLE_FONT_SIZE2, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT4, HEADER_HEIGHT4, SEPARATOR_GAP4, EDGE_STROKE_WIDTH2, NODE_STROKE_WIDTH2, CARD_RADIUS2, CONTAINER_RADIUS2, CONTAINER_LABEL_FONT_SIZE2, CONTAINER_META_FONT_SIZE2, CONTAINER_META_LINE_HEIGHT4, CONTAINER_HEADER_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, COLLAPSE_BAR_HEIGHT2, LEGEND_FIXED_GAP2, lineGenerator;
|
|
9639
9623
|
var init_renderer2 = __esm({
|
|
9640
9624
|
"src/sitemap/renderer.ts"() {
|
|
9641
9625
|
"use strict";
|
|
@@ -9643,6 +9627,7 @@ var init_renderer2 = __esm({
|
|
|
9643
9627
|
d3Shape = __toESM(require("d3-shape"), 1);
|
|
9644
9628
|
init_fonts();
|
|
9645
9629
|
init_color_utils();
|
|
9630
|
+
init_legend_constants();
|
|
9646
9631
|
DIAGRAM_PADDING2 = 20;
|
|
9647
9632
|
MAX_SCALE2 = 3;
|
|
9648
9633
|
TITLE_HEIGHT2 = 30;
|
|
@@ -9664,23 +9649,8 @@ var init_renderer2 = __esm({
|
|
|
9664
9649
|
ARROWHEAD_H = 7;
|
|
9665
9650
|
EDGE_LABEL_FONT_SIZE = 11;
|
|
9666
9651
|
COLLAPSE_BAR_HEIGHT2 = 6;
|
|
9667
|
-
LEGEND_HEIGHT4 = 28;
|
|
9668
9652
|
LEGEND_FIXED_GAP2 = 8;
|
|
9669
|
-
LEGEND_PILL_PAD4 = 16;
|
|
9670
|
-
LEGEND_PILL_FONT_SIZE2 = 11;
|
|
9671
|
-
LEGEND_PILL_FONT_W4 = LEGEND_PILL_FONT_SIZE2 * 0.6;
|
|
9672
|
-
LEGEND_CAPSULE_PAD4 = 4;
|
|
9673
|
-
LEGEND_DOT_R4 = 4;
|
|
9674
|
-
LEGEND_ENTRY_FONT_SIZE2 = 10;
|
|
9675
|
-
LEGEND_ENTRY_FONT_W4 = LEGEND_ENTRY_FONT_SIZE2 * 0.6;
|
|
9676
|
-
LEGEND_ENTRY_DOT_GAP4 = 4;
|
|
9677
|
-
LEGEND_ENTRY_TRAIL4 = 8;
|
|
9678
|
-
LEGEND_GROUP_GAP4 = 12;
|
|
9679
|
-
LEGEND_EYE_SIZE4 = 14;
|
|
9680
|
-
LEGEND_EYE_GAP4 = 6;
|
|
9681
9653
|
lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
|
|
9682
|
-
EYE_OPEN_PATH2 = "M1 7s2.5-5 6-5 6 5 6 5-2.5 5-6 5-6-5-6-5z M7 9.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z";
|
|
9683
|
-
EYE_CLOSED_PATH2 = "M2.5 2.5l9 9 M1.5 7s2.2-4 5.5-4c1.2 0 2.2.5 3 1.1 M12.5 7s-2.2 4-5.5 4c-1.2 0-2.2-.5-3-1.1";
|
|
9684
9654
|
}
|
|
9685
9655
|
});
|
|
9686
9656
|
|
|
@@ -9882,8 +9852,7 @@ function resolveCardTagColor(card, tagGroups, activeTagGroup) {
|
|
|
9882
9852
|
return entry?.color;
|
|
9883
9853
|
}
|
|
9884
9854
|
function computeLayout(parsed, _palette) {
|
|
9885
|
-
const
|
|
9886
|
-
const headerHeight = hasHeader ? Math.max(TITLE_HEIGHT3, LEGEND_HEIGHT5) + 8 : 0;
|
|
9855
|
+
const headerHeight = parsed.title ? TITLE_HEIGHT3 + 8 : 0;
|
|
9887
9856
|
const startY = DIAGRAM_PADDING3 + headerHeight;
|
|
9888
9857
|
const charWidth = CARD_TITLE_FONT_SIZE * 0.6;
|
|
9889
9858
|
const columnLayouts = [];
|
|
@@ -9940,7 +9909,8 @@ function computeLayout(parsed, _palette) {
|
|
|
9940
9909
|
currentX += cl.width + COLUMN_GAP;
|
|
9941
9910
|
}
|
|
9942
9911
|
const totalWidth = currentX - COLUMN_GAP + DIAGRAM_PADDING3;
|
|
9943
|
-
const
|
|
9912
|
+
const legendSpace = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT : 0;
|
|
9913
|
+
const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3 + legendSpace;
|
|
9944
9914
|
return { columns: columnLayouts, totalWidth, totalHeight };
|
|
9945
9915
|
}
|
|
9946
9916
|
function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exportDims, activeTagGroup) {
|
|
@@ -9953,42 +9923,45 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
|
|
|
9953
9923
|
svg.append("text").attr("class", "chart-title").attr("data-line-number", parsed.titleLineNumber ?? 0).attr("x", DIAGRAM_PADDING3).attr("y", DIAGRAM_PADDING3 + TITLE_FONT_SIZE3).attr("font-size", TITLE_FONT_SIZE3).attr("font-weight", "bold").attr("fill", palette.text).text(parsed.title);
|
|
9954
9924
|
}
|
|
9955
9925
|
if (parsed.tagGroups.length > 0) {
|
|
9956
|
-
const legendY =
|
|
9957
|
-
|
|
9958
|
-
let legendX = DIAGRAM_PADDING3 + titleTextWidth;
|
|
9926
|
+
const legendY = height - LEGEND_HEIGHT;
|
|
9927
|
+
let legendX = DIAGRAM_PADDING3;
|
|
9959
9928
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
9960
|
-
const capsulePad =
|
|
9929
|
+
const capsulePad = LEGEND_CAPSULE_PAD;
|
|
9930
|
+
const legendContainer = svg.append("g").attr("class", "kanban-legend");
|
|
9931
|
+
if (activeTagGroup) {
|
|
9932
|
+
legendContainer.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
9933
|
+
}
|
|
9961
9934
|
for (const group of parsed.tagGroups) {
|
|
9962
9935
|
const isActive = activeTagGroup?.toLowerCase() === group.name.toLowerCase();
|
|
9963
9936
|
if (activeTagGroup != null && !isActive) continue;
|
|
9964
|
-
const pillTextWidth = group.name.length *
|
|
9937
|
+
const pillTextWidth = group.name.length * LEGEND_PILL_FONT_SIZE * 0.6;
|
|
9965
9938
|
const pillWidth = pillTextWidth + 16;
|
|
9966
9939
|
let capsuleContentWidth = pillWidth;
|
|
9967
9940
|
if (isActive) {
|
|
9968
9941
|
capsuleContentWidth += 4;
|
|
9969
9942
|
for (const entry of group.entries) {
|
|
9970
|
-
capsuleContentWidth +=
|
|
9943
|
+
capsuleContentWidth += LEGEND_DOT_R * 2 + 4 + entry.value.length * LEGEND_ENTRY_FONT_SIZE * 0.6 + 8;
|
|
9971
9944
|
}
|
|
9972
9945
|
}
|
|
9973
9946
|
const capsuleWidth = capsuleContentWidth + capsulePad * 2;
|
|
9974
9947
|
if (isActive) {
|
|
9975
|
-
|
|
9948
|
+
legendContainer.append("rect").attr("x", legendX).attr("y", legendY).attr("width", capsuleWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
9976
9949
|
}
|
|
9977
9950
|
const pillX = legendX + (isActive ? capsulePad : 0);
|
|
9978
9951
|
const pillBg = isActive ? palette.bg : groupBg;
|
|
9979
|
-
|
|
9952
|
+
legendContainer.append("rect").attr("x", pillX).attr("y", legendY + (isActive ? capsulePad : 0)).attr("width", pillWidth).attr("height", LEGEND_HEIGHT - (isActive ? capsulePad * 2 : 0)).attr("rx", (LEGEND_HEIGHT - (isActive ? capsulePad * 2 : 0)) / 2).attr("fill", pillBg).attr("class", "kanban-legend-group").attr("data-legend-group", group.name.toLowerCase());
|
|
9980
9953
|
if (isActive) {
|
|
9981
|
-
|
|
9954
|
+
legendContainer.append("rect").attr("x", pillX).attr("y", legendY + capsulePad).attr("width", pillWidth).attr("height", LEGEND_HEIGHT - capsulePad * 2).attr("rx", (LEGEND_HEIGHT - capsulePad * 2) / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
9982
9955
|
}
|
|
9983
|
-
|
|
9956
|
+
legendContainer.append("text").attr("x", pillX + pillWidth / 2).attr("y", legendY + LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(group.name);
|
|
9984
9957
|
if (isActive) {
|
|
9985
9958
|
let entryX = pillX + pillWidth + 4;
|
|
9986
9959
|
for (const entry of group.entries) {
|
|
9987
|
-
const entryG =
|
|
9988
|
-
entryG.append("circle").attr("cx", entryX +
|
|
9989
|
-
const entryTextX = entryX +
|
|
9990
|
-
entryG.append("text").attr("x", entryTextX).attr("y", legendY +
|
|
9991
|
-
entryX = entryTextX + entry.value.length *
|
|
9960
|
+
const entryG = legendContainer.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
9961
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", legendY + LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
9962
|
+
const entryTextX = entryX + LEGEND_DOT_R * 2 + 4;
|
|
9963
|
+
entryG.append("text").attr("x", entryTextX).attr("y", legendY + LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).text(entry.value);
|
|
9964
|
+
entryX = entryTextX + entry.value.length * LEGEND_ENTRY_FONT_SIZE * 0.6 + 8;
|
|
9992
9965
|
}
|
|
9993
9966
|
legendX += capsuleWidth + 12;
|
|
9994
9967
|
} else {
|
|
@@ -10075,7 +10048,7 @@ function renderKanbanForExport(content, theme, palette) {
|
|
|
10075
10048
|
const svgEl = container.querySelector("svg");
|
|
10076
10049
|
return svgEl?.outerHTML ?? "";
|
|
10077
10050
|
}
|
|
10078
|
-
var d3Selection3, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_RADIUS3, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, TITLE_FONT_SIZE3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS
|
|
10051
|
+
var d3Selection3, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_RADIUS3, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, TITLE_FONT_SIZE3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS;
|
|
10079
10052
|
var init_renderer3 = __esm({
|
|
10080
10053
|
"src/kanban/renderer.ts"() {
|
|
10081
10054
|
"use strict";
|
|
@@ -10085,6 +10058,7 @@ var init_renderer3 = __esm({
|
|
|
10085
10058
|
init_inline_markdown();
|
|
10086
10059
|
init_parser5();
|
|
10087
10060
|
init_mutations();
|
|
10061
|
+
init_legend_constants();
|
|
10088
10062
|
DIAGRAM_PADDING3 = 20;
|
|
10089
10063
|
COLUMN_GAP = 16;
|
|
10090
10064
|
COLUMN_HEADER_HEIGHT = 36;
|
|
@@ -10106,10 +10080,6 @@ var init_renderer3 = __esm({
|
|
|
10106
10080
|
WIP_FONT_SIZE = 10;
|
|
10107
10081
|
COLUMN_RADIUS = 8;
|
|
10108
10082
|
COLUMN_HEADER_RADIUS = 8;
|
|
10109
|
-
LEGEND_HEIGHT5 = 28;
|
|
10110
|
-
LEGEND_FONT_SIZE = 11;
|
|
10111
|
-
LEGEND_DOT_R5 = 4;
|
|
10112
|
-
LEGEND_ENTRY_FONT_SIZE3 = 10;
|
|
10113
10083
|
}
|
|
10114
10084
|
});
|
|
10115
10085
|
|
|
@@ -10527,110 +10497,211 @@ function computeNodeDimensions2(table) {
|
|
|
10527
10497
|
const height = headerHeight + columnsHeight + (columnsHeight === 0 ? 4 : 0);
|
|
10528
10498
|
return { width, height, headerHeight, columnsHeight };
|
|
10529
10499
|
}
|
|
10500
|
+
function findConnectedComponents(tableIds, relationships) {
|
|
10501
|
+
const adj = /* @__PURE__ */ new Map();
|
|
10502
|
+
for (const id of tableIds) adj.set(id, /* @__PURE__ */ new Set());
|
|
10503
|
+
for (const rel of relationships) {
|
|
10504
|
+
adj.get(rel.source)?.add(rel.target);
|
|
10505
|
+
adj.get(rel.target)?.add(rel.source);
|
|
10506
|
+
}
|
|
10507
|
+
const visited = /* @__PURE__ */ new Set();
|
|
10508
|
+
const components = [];
|
|
10509
|
+
for (const id of tableIds) {
|
|
10510
|
+
if (visited.has(id)) continue;
|
|
10511
|
+
const comp = [];
|
|
10512
|
+
const queue = [id];
|
|
10513
|
+
while (queue.length > 0) {
|
|
10514
|
+
const cur = queue.shift();
|
|
10515
|
+
if (visited.has(cur)) continue;
|
|
10516
|
+
visited.add(cur);
|
|
10517
|
+
comp.push(cur);
|
|
10518
|
+
for (const nb of adj.get(cur) ?? []) {
|
|
10519
|
+
if (!visited.has(nb)) queue.push(nb);
|
|
10520
|
+
}
|
|
10521
|
+
}
|
|
10522
|
+
components.push(comp);
|
|
10523
|
+
}
|
|
10524
|
+
return components;
|
|
10525
|
+
}
|
|
10526
|
+
function layoutComponent(tables, rels, dimMap) {
|
|
10527
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
10528
|
+
const edgePoints = /* @__PURE__ */ new Map();
|
|
10529
|
+
if (tables.length === 1) {
|
|
10530
|
+
const dims = dimMap.get(tables[0].id);
|
|
10531
|
+
nodePositions.set(tables[0].id, { x: dims.width / 2, y: dims.height / 2, ...dims });
|
|
10532
|
+
return { nodePositions, edgePoints, width: dims.width, height: dims.height };
|
|
10533
|
+
}
|
|
10534
|
+
const g = new import_dagre3.default.graphlib.Graph({ multigraph: true });
|
|
10535
|
+
g.setGraph({ rankdir: "LR", nodesep: 40, ranksep: 80, edgesep: 20 });
|
|
10536
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
10537
|
+
for (const table of tables) {
|
|
10538
|
+
const dims = dimMap.get(table.id);
|
|
10539
|
+
g.setNode(table.id, { width: dims.width, height: dims.height });
|
|
10540
|
+
}
|
|
10541
|
+
for (const rel of rels) {
|
|
10542
|
+
g.setEdge(rel.source, rel.target, { label: rel.label ?? "" }, String(rel.lineNumber));
|
|
10543
|
+
}
|
|
10544
|
+
import_dagre3.default.layout(g);
|
|
10545
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
10546
|
+
for (const table of tables) {
|
|
10547
|
+
const pos = g.node(table.id);
|
|
10548
|
+
const dims = dimMap.get(table.id);
|
|
10549
|
+
minX = Math.min(minX, pos.x - dims.width / 2);
|
|
10550
|
+
minY = Math.min(minY, pos.y - dims.height / 2);
|
|
10551
|
+
maxX = Math.max(maxX, pos.x + dims.width / 2);
|
|
10552
|
+
maxY = Math.max(maxY, pos.y + dims.height / 2);
|
|
10553
|
+
}
|
|
10554
|
+
for (const rel of rels) {
|
|
10555
|
+
const ed = g.edge(rel.source, rel.target, String(rel.lineNumber));
|
|
10556
|
+
for (const pt of ed?.points ?? []) {
|
|
10557
|
+
minX = Math.min(minX, pt.x);
|
|
10558
|
+
minY = Math.min(minY, pt.y);
|
|
10559
|
+
maxX = Math.max(maxX, pt.x);
|
|
10560
|
+
maxY = Math.max(maxY, pt.y);
|
|
10561
|
+
}
|
|
10562
|
+
if (rel.label && (ed?.points ?? []).length > 0) {
|
|
10563
|
+
const pts = ed.points;
|
|
10564
|
+
const mid = pts[Math.floor(pts.length / 2)];
|
|
10565
|
+
const hw = (rel.label.length * 7 + 8) / 2;
|
|
10566
|
+
minX = Math.min(minX, mid.x - hw);
|
|
10567
|
+
maxX = Math.max(maxX, mid.x + hw);
|
|
10568
|
+
}
|
|
10569
|
+
}
|
|
10570
|
+
for (const table of tables) {
|
|
10571
|
+
const pos = g.node(table.id);
|
|
10572
|
+
const dims = dimMap.get(table.id);
|
|
10573
|
+
nodePositions.set(table.id, {
|
|
10574
|
+
x: pos.x - minX,
|
|
10575
|
+
y: pos.y - minY,
|
|
10576
|
+
...dims
|
|
10577
|
+
});
|
|
10578
|
+
}
|
|
10579
|
+
for (const rel of rels) {
|
|
10580
|
+
const ed = g.edge(rel.source, rel.target, String(rel.lineNumber));
|
|
10581
|
+
edgePoints.set(
|
|
10582
|
+
rel.lineNumber,
|
|
10583
|
+
(ed?.points ?? []).map((pt) => ({ x: pt.x - minX, y: pt.y - minY }))
|
|
10584
|
+
);
|
|
10585
|
+
}
|
|
10586
|
+
return {
|
|
10587
|
+
nodePositions,
|
|
10588
|
+
edgePoints,
|
|
10589
|
+
width: Math.max(0, maxX - minX),
|
|
10590
|
+
height: Math.max(0, maxY - minY)
|
|
10591
|
+
};
|
|
10592
|
+
}
|
|
10593
|
+
function packComponents(items) {
|
|
10594
|
+
if (items.length === 0) return [];
|
|
10595
|
+
const sorted = [...items].sort((a, b) => {
|
|
10596
|
+
const aConnected = a.compIds.length > 1 ? 1 : 0;
|
|
10597
|
+
const bConnected = b.compIds.length > 1 ? 1 : 0;
|
|
10598
|
+
if (aConnected !== bConnected) return bConnected - aConnected;
|
|
10599
|
+
return b.compLayout.height - a.compLayout.height;
|
|
10600
|
+
});
|
|
10601
|
+
const totalArea = items.reduce(
|
|
10602
|
+
(s, c) => s + (c.compLayout.width || MIN_WIDTH2) * (c.compLayout.height || HEADER_BASE2),
|
|
10603
|
+
0
|
|
10604
|
+
);
|
|
10605
|
+
const targetW = Math.max(
|
|
10606
|
+
Math.sqrt(totalArea) * 1.5,
|
|
10607
|
+
sorted[0].compLayout.width
|
|
10608
|
+
// at least as wide as the widest component
|
|
10609
|
+
);
|
|
10610
|
+
const placements = [];
|
|
10611
|
+
let curX = 0;
|
|
10612
|
+
let curY = 0;
|
|
10613
|
+
let rowH = 0;
|
|
10614
|
+
for (const item of sorted) {
|
|
10615
|
+
const w = item.compLayout.width || MIN_WIDTH2;
|
|
10616
|
+
const h = item.compLayout.height || HEADER_BASE2;
|
|
10617
|
+
if (curX > 0 && curX + w > targetW) {
|
|
10618
|
+
curY += rowH + COMP_GAP;
|
|
10619
|
+
curX = 0;
|
|
10620
|
+
rowH = 0;
|
|
10621
|
+
}
|
|
10622
|
+
placements.push({ compIds: item.compIds, compLayout: item.compLayout, offsetX: curX, offsetY: curY });
|
|
10623
|
+
curX += w + COMP_GAP;
|
|
10624
|
+
rowH = Math.max(rowH, h);
|
|
10625
|
+
}
|
|
10626
|
+
return placements;
|
|
10627
|
+
}
|
|
10530
10628
|
function layoutERDiagram(parsed) {
|
|
10531
10629
|
if (parsed.tables.length === 0) {
|
|
10532
10630
|
return { nodes: [], edges: [], width: 0, height: 0 };
|
|
10533
10631
|
}
|
|
10534
|
-
const g = new import_dagre3.default.graphlib.Graph();
|
|
10535
|
-
g.setGraph({
|
|
10536
|
-
rankdir: "TB",
|
|
10537
|
-
nodesep: 60,
|
|
10538
|
-
ranksep: 80,
|
|
10539
|
-
edgesep: 20
|
|
10540
|
-
});
|
|
10541
|
-
g.setDefaultEdgeLabel(() => ({}));
|
|
10542
10632
|
const dimMap = /* @__PURE__ */ new Map();
|
|
10543
10633
|
for (const table of parsed.tables) {
|
|
10544
|
-
|
|
10545
|
-
dimMap.set(table.id, dims);
|
|
10546
|
-
g.setNode(table.id, {
|
|
10547
|
-
label: table.name,
|
|
10548
|
-
width: dims.width,
|
|
10549
|
-
height: dims.height
|
|
10550
|
-
});
|
|
10634
|
+
dimMap.set(table.id, computeNodeDimensions2(table));
|
|
10551
10635
|
}
|
|
10552
|
-
|
|
10553
|
-
|
|
10636
|
+
const compIdSets = findConnectedComponents(
|
|
10637
|
+
parsed.tables.map((t) => t.id),
|
|
10638
|
+
parsed.relationships
|
|
10639
|
+
);
|
|
10640
|
+
const tableById = new Map(parsed.tables.map((t) => [t.id, t]));
|
|
10641
|
+
const componentItems = compIdSets.map((ids) => {
|
|
10642
|
+
const tables = ids.map((id) => tableById.get(id));
|
|
10643
|
+
const rels = parsed.relationships.filter((r) => ids.includes(r.source));
|
|
10644
|
+
return { compIds: ids, compLayout: layoutComponent(tables, rels, dimMap) };
|
|
10645
|
+
});
|
|
10646
|
+
const packed = packComponents(componentItems);
|
|
10647
|
+
const placementByTableId = /* @__PURE__ */ new Map();
|
|
10648
|
+
for (const p of packed) {
|
|
10649
|
+
for (const id of p.compIds) placementByTableId.set(id, p);
|
|
10650
|
+
}
|
|
10651
|
+
const placementByRelLine = /* @__PURE__ */ new Map();
|
|
10652
|
+
for (const p of packed) {
|
|
10653
|
+
for (const lineNum of p.compLayout.edgePoints.keys()) {
|
|
10654
|
+
placementByRelLine.set(lineNum, p);
|
|
10655
|
+
}
|
|
10554
10656
|
}
|
|
10555
|
-
import_dagre3.default.layout(g);
|
|
10556
10657
|
const layoutNodes = parsed.tables.map((table) => {
|
|
10557
|
-
const
|
|
10558
|
-
const
|
|
10658
|
+
const p = placementByTableId.get(table.id);
|
|
10659
|
+
const pos = p.compLayout.nodePositions.get(table.id);
|
|
10559
10660
|
return {
|
|
10560
10661
|
...table,
|
|
10561
|
-
x: pos.x,
|
|
10562
|
-
y: pos.y,
|
|
10563
|
-
width:
|
|
10564
|
-
height:
|
|
10565
|
-
headerHeight:
|
|
10566
|
-
columnsHeight:
|
|
10662
|
+
x: pos.x + p.offsetX + HALF_MARGIN,
|
|
10663
|
+
y: pos.y + p.offsetY + HALF_MARGIN,
|
|
10664
|
+
width: pos.width,
|
|
10665
|
+
height: pos.height,
|
|
10666
|
+
headerHeight: pos.headerHeight,
|
|
10667
|
+
columnsHeight: pos.columnsHeight
|
|
10567
10668
|
};
|
|
10568
10669
|
});
|
|
10569
10670
|
const layoutEdges = parsed.relationships.map((rel) => {
|
|
10570
|
-
const
|
|
10671
|
+
const p = placementByRelLine.get(rel.lineNumber);
|
|
10672
|
+
const pts = p?.compLayout.edgePoints.get(rel.lineNumber) ?? [];
|
|
10571
10673
|
return {
|
|
10572
10674
|
source: rel.source,
|
|
10573
10675
|
target: rel.target,
|
|
10574
10676
|
cardinality: rel.cardinality,
|
|
10575
|
-
points:
|
|
10677
|
+
points: pts.map((pt) => ({
|
|
10678
|
+
x: pt.x + (p?.offsetX ?? 0) + HALF_MARGIN,
|
|
10679
|
+
y: pt.y + (p?.offsetY ?? 0) + HALF_MARGIN
|
|
10680
|
+
})),
|
|
10576
10681
|
label: rel.label,
|
|
10577
10682
|
lineNumber: rel.lineNumber
|
|
10578
10683
|
};
|
|
10579
10684
|
});
|
|
10580
|
-
let minX = Infinity;
|
|
10581
|
-
let minY = Infinity;
|
|
10582
10685
|
let maxX = 0;
|
|
10583
10686
|
let maxY = 0;
|
|
10584
10687
|
for (const node of layoutNodes) {
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
const top = node.y - node.height / 2;
|
|
10588
|
-
const bottom = node.y + node.height / 2;
|
|
10589
|
-
if (left < minX) minX = left;
|
|
10590
|
-
if (right > maxX) maxX = right;
|
|
10591
|
-
if (top < minY) minY = top;
|
|
10592
|
-
if (bottom > maxY) maxY = bottom;
|
|
10688
|
+
maxX = Math.max(maxX, node.x + node.width / 2);
|
|
10689
|
+
maxY = Math.max(maxY, node.y + node.height / 2);
|
|
10593
10690
|
}
|
|
10594
10691
|
for (const edge of layoutEdges) {
|
|
10595
10692
|
for (const pt of edge.points) {
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
if (pt.y < minY) minY = pt.y;
|
|
10599
|
-
if (pt.y > maxY) maxY = pt.y;
|
|
10600
|
-
}
|
|
10601
|
-
if (edge.label && edge.points.length > 0) {
|
|
10602
|
-
const midPt = edge.points[Math.floor(edge.points.length / 2)];
|
|
10603
|
-
const labelHalfW = (edge.label.length * 7 + 8) / 2;
|
|
10604
|
-
if (midPt.x + labelHalfW > maxX) maxX = midPt.x + labelHalfW;
|
|
10605
|
-
if (midPt.x - labelHalfW < minX) minX = midPt.x - labelHalfW;
|
|
10693
|
+
maxX = Math.max(maxX, pt.x);
|
|
10694
|
+
maxY = Math.max(maxY, pt.y);
|
|
10606
10695
|
}
|
|
10607
10696
|
}
|
|
10608
|
-
const EDGE_MARGIN2 = 60;
|
|
10609
|
-
const HALF_MARGIN = EDGE_MARGIN2 / 2;
|
|
10610
|
-
const shiftX = -minX + HALF_MARGIN;
|
|
10611
|
-
const shiftY = -minY + HALF_MARGIN;
|
|
10612
|
-
for (const node of layoutNodes) {
|
|
10613
|
-
node.x += shiftX;
|
|
10614
|
-
node.y += shiftY;
|
|
10615
|
-
}
|
|
10616
|
-
for (const edge of layoutEdges) {
|
|
10617
|
-
for (const pt of edge.points) {
|
|
10618
|
-
pt.x += shiftX;
|
|
10619
|
-
pt.y += shiftY;
|
|
10620
|
-
}
|
|
10621
|
-
}
|
|
10622
|
-
maxX += shiftX;
|
|
10623
|
-
maxY += shiftY;
|
|
10624
|
-
const totalWidth = maxX + HALF_MARGIN;
|
|
10625
|
-
const totalHeight = maxY + HALF_MARGIN;
|
|
10626
10697
|
return {
|
|
10627
10698
|
nodes: layoutNodes,
|
|
10628
10699
|
edges: layoutEdges,
|
|
10629
|
-
width:
|
|
10630
|
-
height:
|
|
10700
|
+
width: maxX + HALF_MARGIN,
|
|
10701
|
+
height: maxY + HALF_MARGIN
|
|
10631
10702
|
};
|
|
10632
10703
|
}
|
|
10633
|
-
var import_dagre3, MIN_WIDTH2, CHAR_WIDTH4, PADDING_X2, HEADER_BASE2, MEMBER_LINE_HEIGHT3, COMPARTMENT_PADDING_Y3, SEPARATOR_HEIGHT2;
|
|
10704
|
+
var import_dagre3, MIN_WIDTH2, CHAR_WIDTH4, PADDING_X2, HEADER_BASE2, MEMBER_LINE_HEIGHT3, COMPARTMENT_PADDING_Y3, SEPARATOR_HEIGHT2, HALF_MARGIN, COMP_GAP;
|
|
10634
10705
|
var init_layout4 = __esm({
|
|
10635
10706
|
"src/er/layout.ts"() {
|
|
10636
10707
|
"use strict";
|
|
@@ -10642,6 +10713,135 @@ var init_layout4 = __esm({
|
|
|
10642
10713
|
MEMBER_LINE_HEIGHT3 = 18;
|
|
10643
10714
|
COMPARTMENT_PADDING_Y3 = 8;
|
|
10644
10715
|
SEPARATOR_HEIGHT2 = 1;
|
|
10716
|
+
HALF_MARGIN = 30;
|
|
10717
|
+
COMP_GAP = 60;
|
|
10718
|
+
}
|
|
10719
|
+
});
|
|
10720
|
+
|
|
10721
|
+
// src/er/classify.ts
|
|
10722
|
+
function classifyEREntities(tables, relationships) {
|
|
10723
|
+
const result = /* @__PURE__ */ new Map();
|
|
10724
|
+
if (tables.length === 0) return result;
|
|
10725
|
+
const indegreeMap = {};
|
|
10726
|
+
for (const t of tables) indegreeMap[t.id] = 0;
|
|
10727
|
+
for (const rel of relationships) {
|
|
10728
|
+
if (rel.source === rel.target) continue;
|
|
10729
|
+
if (rel.cardinality.from === "1" && rel.cardinality.to !== "1") {
|
|
10730
|
+
indegreeMap[rel.source] = (indegreeMap[rel.source] ?? 0) + 1;
|
|
10731
|
+
}
|
|
10732
|
+
if (rel.cardinality.to === "1" && rel.cardinality.from !== "1") {
|
|
10733
|
+
indegreeMap[rel.target] = (indegreeMap[rel.target] ?? 0) + 1;
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
const tableStarNeighbors = /* @__PURE__ */ new Map();
|
|
10737
|
+
for (const rel of relationships) {
|
|
10738
|
+
if (rel.source === rel.target) continue;
|
|
10739
|
+
if (rel.cardinality.from === "*") {
|
|
10740
|
+
if (!tableStarNeighbors.has(rel.source)) tableStarNeighbors.set(rel.source, /* @__PURE__ */ new Set());
|
|
10741
|
+
tableStarNeighbors.get(rel.source).add(rel.target);
|
|
10742
|
+
}
|
|
10743
|
+
if (rel.cardinality.to === "*") {
|
|
10744
|
+
if (!tableStarNeighbors.has(rel.target)) tableStarNeighbors.set(rel.target, /* @__PURE__ */ new Set());
|
|
10745
|
+
tableStarNeighbors.get(rel.target).add(rel.source);
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
const mmParticipants = /* @__PURE__ */ new Set();
|
|
10749
|
+
for (const [id, neighbors] of tableStarNeighbors) {
|
|
10750
|
+
if (neighbors.size >= 2) mmParticipants.add(id);
|
|
10751
|
+
}
|
|
10752
|
+
const indegreeValues = Object.values(indegreeMap);
|
|
10753
|
+
const mean = indegreeValues.reduce((a, b) => a + b, 0) / indegreeValues.length;
|
|
10754
|
+
const variance = indegreeValues.reduce((a, b) => a + (b - mean) ** 2, 0) / indegreeValues.length;
|
|
10755
|
+
const stddev = Math.sqrt(variance);
|
|
10756
|
+
const sorted = [...indegreeValues].sort((a, b) => a - b);
|
|
10757
|
+
const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
|
|
10758
|
+
for (const table of tables) {
|
|
10759
|
+
const id = table.id;
|
|
10760
|
+
const cols = table.columns;
|
|
10761
|
+
const fkCols = cols.filter((c) => c.constraints.includes("fk"));
|
|
10762
|
+
const pkFkCols = cols.filter(
|
|
10763
|
+
(c) => c.constraints.includes("pk") && c.constraints.includes("fk")
|
|
10764
|
+
);
|
|
10765
|
+
const fkCount = fkCols.length;
|
|
10766
|
+
const fkRatio = cols.length === 0 ? 0 : fkCount / cols.length;
|
|
10767
|
+
const indegree = indegreeMap[id] ?? 0;
|
|
10768
|
+
const nameLower = table.name.toLowerCase();
|
|
10769
|
+
const externalRels = relationships.filter(
|
|
10770
|
+
(r) => (r.source === id || r.target === id) && r.source !== r.target
|
|
10771
|
+
);
|
|
10772
|
+
const hasSelfRef = relationships.some((r) => r.source === id && r.target === id);
|
|
10773
|
+
const externalTargets = /* @__PURE__ */ new Set();
|
|
10774
|
+
for (const rel of externalRels) {
|
|
10775
|
+
externalTargets.add(rel.source === id ? rel.target : rel.source);
|
|
10776
|
+
}
|
|
10777
|
+
if (hasSelfRef && externalRels.length === 0) {
|
|
10778
|
+
result.set(id, "self-referential");
|
|
10779
|
+
continue;
|
|
10780
|
+
}
|
|
10781
|
+
const isInheritancePattern = pkFkCols.length >= 2 && externalTargets.size === 1;
|
|
10782
|
+
const junctionByRatio = fkRatio >= 0.6 && !isInheritancePattern;
|
|
10783
|
+
const junctionByCompositePk = pkFkCols.length >= 2 && externalTargets.size >= 2;
|
|
10784
|
+
const junctionByMm = mmParticipants.has(id);
|
|
10785
|
+
if (junctionByRatio || junctionByCompositePk || junctionByMm) {
|
|
10786
|
+
result.set(id, "junction");
|
|
10787
|
+
continue;
|
|
10788
|
+
}
|
|
10789
|
+
if (fkRatio >= 0.4 && fkRatio < 0.6 && pkFkCols.length < 2 && !mmParticipants.has(id)) {
|
|
10790
|
+
result.set(id, "ambiguous");
|
|
10791
|
+
continue;
|
|
10792
|
+
}
|
|
10793
|
+
const nameMatchesLookup = LOOKUP_NAME_SUFFIXES.some((s) => nameLower.endsWith(s));
|
|
10794
|
+
if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median) {
|
|
10795
|
+
result.set(id, "lookup");
|
|
10796
|
+
continue;
|
|
10797
|
+
}
|
|
10798
|
+
if (tables.length >= 6 && indegree > 0 && indegree > mean + 1.5 * stddev && indegree >= 2 * mean) {
|
|
10799
|
+
result.set(id, "hub");
|
|
10800
|
+
continue;
|
|
10801
|
+
}
|
|
10802
|
+
if (fkCount > 0) {
|
|
10803
|
+
result.set(id, "dependent");
|
|
10804
|
+
continue;
|
|
10805
|
+
}
|
|
10806
|
+
result.set(id, "core");
|
|
10807
|
+
}
|
|
10808
|
+
return result;
|
|
10809
|
+
}
|
|
10810
|
+
var ROLE_COLORS, ROLE_LABELS, ROLE_ORDER, LOOKUP_NAME_SUFFIXES;
|
|
10811
|
+
var init_classify = __esm({
|
|
10812
|
+
"src/er/classify.ts"() {
|
|
10813
|
+
"use strict";
|
|
10814
|
+
ROLE_COLORS = {
|
|
10815
|
+
core: "green",
|
|
10816
|
+
dependent: "blue",
|
|
10817
|
+
junction: "red",
|
|
10818
|
+
ambiguous: "purple",
|
|
10819
|
+
lookup: "yellow",
|
|
10820
|
+
hub: "orange",
|
|
10821
|
+
"self-referential": "teal",
|
|
10822
|
+
unclassified: "gray"
|
|
10823
|
+
};
|
|
10824
|
+
ROLE_LABELS = {
|
|
10825
|
+
core: "Core entity",
|
|
10826
|
+
dependent: "Dependent",
|
|
10827
|
+
junction: "Junction / M:M",
|
|
10828
|
+
ambiguous: "Bridge",
|
|
10829
|
+
lookup: "Lookup / Reference",
|
|
10830
|
+
hub: "Hub",
|
|
10831
|
+
"self-referential": "Self-referential",
|
|
10832
|
+
unclassified: "Unclassified"
|
|
10833
|
+
};
|
|
10834
|
+
ROLE_ORDER = [
|
|
10835
|
+
"core",
|
|
10836
|
+
"dependent",
|
|
10837
|
+
"junction",
|
|
10838
|
+
"ambiguous",
|
|
10839
|
+
"lookup",
|
|
10840
|
+
"hub",
|
|
10841
|
+
"self-referential",
|
|
10842
|
+
"unclassified"
|
|
10843
|
+
];
|
|
10844
|
+
LOOKUP_NAME_SUFFIXES = ["_type", "_status", "_code", "_category"];
|
|
10645
10845
|
}
|
|
10646
10846
|
});
|
|
10647
10847
|
|
|
@@ -10711,25 +10911,41 @@ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels) {
|
|
|
10711
10911
|
g.append("line").attr("x1", bx + px * spread).attr("y1", by + py * spread).attr("x2", bx - px * spread).attr("y2", by - py * spread).attr("stroke", color).attr("stroke-width", sw);
|
|
10712
10912
|
}
|
|
10713
10913
|
}
|
|
10714
|
-
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup) {
|
|
10914
|
+
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, semanticColorsActive) {
|
|
10715
10915
|
d3Selection5.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
10716
|
-
const
|
|
10717
|
-
const
|
|
10718
|
-
if (width <= 0 || height <= 0) return;
|
|
10916
|
+
const useSemanticColors = parsed.tagGroups.length === 0 && layout.nodes.every((n) => !n.color);
|
|
10917
|
+
const legendReserveH = useSemanticColors ? LEGEND_HEIGHT + DIAGRAM_PADDING5 : 0;
|
|
10719
10918
|
const titleHeight = parsed.title ? 40 : 0;
|
|
10720
10919
|
const diagramW = layout.width;
|
|
10721
10920
|
const diagramH = layout.height;
|
|
10722
|
-
const
|
|
10723
|
-
const
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10921
|
+
const naturalW = diagramW + DIAGRAM_PADDING5 * 2;
|
|
10922
|
+
const naturalH = diagramH + titleHeight + legendReserveH + DIAGRAM_PADDING5 * 2;
|
|
10923
|
+
let viewW;
|
|
10924
|
+
let viewH;
|
|
10925
|
+
let scale;
|
|
10926
|
+
let offsetX;
|
|
10927
|
+
let offsetY;
|
|
10928
|
+
if (exportDims) {
|
|
10929
|
+
viewW = exportDims.width ?? naturalW;
|
|
10930
|
+
viewH = exportDims.height ?? naturalH;
|
|
10931
|
+
const availH = viewH - titleHeight - legendReserveH;
|
|
10932
|
+
const scaleX = (viewW - DIAGRAM_PADDING5 * 2) / diagramW;
|
|
10933
|
+
const scaleY = (availH - DIAGRAM_PADDING5 * 2) / diagramH;
|
|
10934
|
+
scale = Math.min(MAX_SCALE4, scaleX, scaleY);
|
|
10935
|
+
const scaledW = diagramW * scale;
|
|
10936
|
+
offsetX = (viewW - scaledW) / 2;
|
|
10937
|
+
offsetY = titleHeight + DIAGRAM_PADDING5;
|
|
10938
|
+
} else {
|
|
10939
|
+
viewW = naturalW;
|
|
10940
|
+
viewH = naturalH;
|
|
10941
|
+
scale = 1;
|
|
10942
|
+
offsetX = DIAGRAM_PADDING5;
|
|
10943
|
+
offsetY = titleHeight + DIAGRAM_PADDING5;
|
|
10944
|
+
}
|
|
10945
|
+
if (viewW <= 0 || viewH <= 0) return;
|
|
10946
|
+
const svg = d3Selection5.select(container).append("svg").attr("width", exportDims ? viewW : "100%").attr("height", exportDims ? viewH : "100%").attr("viewBox", `0 0 ${viewW} ${viewH}`).attr("preserveAspectRatio", "xMidYMid meet").style("font-family", FONT_FAMILY);
|
|
10731
10947
|
if (parsed.title) {
|
|
10732
|
-
const titleEl = svg.append("text").attr("class", "chart-title").attr("x",
|
|
10948
|
+
const titleEl = svg.append("text").attr("class", "chart-title").attr("x", viewW / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(parsed.title);
|
|
10733
10949
|
if (parsed.titleLineNumber) {
|
|
10734
10950
|
titleEl.attr("data-line-number", parsed.titleLineNumber);
|
|
10735
10951
|
if (onClickItem) {
|
|
@@ -10743,6 +10959,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10743
10959
|
}
|
|
10744
10960
|
const contentG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
|
|
10745
10961
|
const seriesColors2 = getSeriesColors(palette);
|
|
10962
|
+
const semanticRoles = useSemanticColors ? classifyEREntities(parsed.tables, parsed.relationships) : null;
|
|
10963
|
+
const semanticActive = semanticRoles !== null && (semanticColorsActive ?? true);
|
|
10746
10964
|
const useLabels = parsed.options.notation === "labels";
|
|
10747
10965
|
for (const edge of layout.edges) {
|
|
10748
10966
|
if (edge.points.length < 2) continue;
|
|
@@ -10782,7 +11000,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10782
11000
|
for (let ni = 0; ni < layout.nodes.length; ni++) {
|
|
10783
11001
|
const node = layout.nodes[ni];
|
|
10784
11002
|
const tagColor = resolveTagColor(node.metadata, parsed.tagGroups, activeTagGroup ?? null);
|
|
10785
|
-
const
|
|
11003
|
+
const semanticColor = semanticActive ? palette.colors[ROLE_COLORS[semanticRoles.get(node.id) ?? "unclassified"]] : semanticRoles ? palette.primary : void 0;
|
|
11004
|
+
const nodeColor2 = node.color ?? tagColor ?? semanticColor ?? seriesColors2[ni % seriesColors2.length];
|
|
10786
11005
|
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "er-table").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
|
|
10787
11006
|
if (activeTagGroup) {
|
|
10788
11007
|
const tagKey = activeTagGroup.toLowerCase();
|
|
@@ -10791,6 +11010,10 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10791
11010
|
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
10792
11011
|
}
|
|
10793
11012
|
}
|
|
11013
|
+
if (semanticRoles) {
|
|
11014
|
+
const role = semanticRoles.get(node.id);
|
|
11015
|
+
if (role) nodeG.attr("data-er-role", role);
|
|
11016
|
+
}
|
|
10794
11017
|
if (onClickItem) {
|
|
10795
11018
|
nodeG.style("cursor", "pointer").on("click", () => {
|
|
10796
11019
|
onClickItem(node.lineNumber);
|
|
@@ -10823,32 +11046,87 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10823
11046
|
}
|
|
10824
11047
|
}
|
|
10825
11048
|
if (parsed.tagGroups.length > 0) {
|
|
10826
|
-
const
|
|
10827
|
-
const
|
|
10828
|
-
const LEGEND_PILL_RX = 11;
|
|
10829
|
-
const LEGEND_PILL_PAD9 = 10;
|
|
11049
|
+
const LEGEND_PILL_H = LEGEND_HEIGHT - 6;
|
|
11050
|
+
const LEGEND_PILL_RX = Math.floor(LEGEND_PILL_H / 2);
|
|
10830
11051
|
const LEGEND_GAP2 = 8;
|
|
10831
|
-
const LEGEND_FONT_SIZE2 = 11;
|
|
10832
|
-
const LEGEND_GROUP_GAP7 = 16;
|
|
10833
11052
|
const legendG = svg.append("g").attr("class", "er-tag-legend");
|
|
11053
|
+
if (activeTagGroup) {
|
|
11054
|
+
legendG.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
11055
|
+
}
|
|
10834
11056
|
let legendX = DIAGRAM_PADDING5;
|
|
10835
|
-
let legendY =
|
|
11057
|
+
let legendY = viewH - DIAGRAM_PADDING5;
|
|
10836
11058
|
for (const group of parsed.tagGroups) {
|
|
10837
11059
|
const groupG = legendG.append("g").attr("data-legend-group", group.name.toLowerCase());
|
|
10838
|
-
const labelText = groupG.append("text").attr("x", legendX).attr("y", legendY + LEGEND_PILL_H / 2).attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size",
|
|
11060
|
+
const labelText = groupG.append("text").attr("x", legendX).attr("y", legendY + LEGEND_PILL_H / 2).attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-family", FONT_FAMILY).text(`${group.name}:`);
|
|
10839
11061
|
const labelWidth = (labelText.node()?.getComputedTextLength?.() ?? group.name.length * 7) + 6;
|
|
10840
11062
|
legendX += labelWidth;
|
|
10841
11063
|
for (const entry of group.entries) {
|
|
10842
11064
|
const pillG = groupG.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
10843
|
-
const tmpText = legendG.append("text").attr("font-size",
|
|
11065
|
+
const tmpText = legendG.append("text").attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-family", FONT_FAMILY).text(entry.value);
|
|
10844
11066
|
const textW = tmpText.node()?.getComputedTextLength?.() ?? entry.value.length * 7;
|
|
10845
11067
|
tmpText.remove();
|
|
10846
|
-
const pillW = textW +
|
|
11068
|
+
const pillW = textW + LEGEND_PILL_PAD * 2;
|
|
10847
11069
|
pillG.append("rect").attr("x", legendX).attr("y", legendY).attr("width", pillW).attr("height", LEGEND_PILL_H).attr("rx", LEGEND_PILL_RX).attr("ry", LEGEND_PILL_RX).attr("fill", mix(entry.color, isDark ? palette.surface : palette.bg, 25)).attr("stroke", entry.color).attr("stroke-width", 1);
|
|
10848
|
-
pillG.append("text").attr("x", legendX + pillW / 2).attr("y", legendY + LEGEND_PILL_H / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size",
|
|
11070
|
+
pillG.append("text").attr("x", legendX + pillW / 2).attr("y", legendY + LEGEND_PILL_H / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-family", FONT_FAMILY).text(entry.value);
|
|
10849
11071
|
legendX += pillW + LEGEND_GAP2;
|
|
10850
11072
|
}
|
|
10851
|
-
legendX +=
|
|
11073
|
+
legendX += LEGEND_GROUP_GAP;
|
|
11074
|
+
}
|
|
11075
|
+
}
|
|
11076
|
+
if (semanticRoles) {
|
|
11077
|
+
const presentRoles = ROLE_ORDER.filter((role) => {
|
|
11078
|
+
for (const r of semanticRoles.values()) {
|
|
11079
|
+
if (r === role) return true;
|
|
11080
|
+
}
|
|
11081
|
+
return false;
|
|
11082
|
+
});
|
|
11083
|
+
if (presentRoles.length > 0) {
|
|
11084
|
+
const measureLabelW = (text, fontSize) => {
|
|
11085
|
+
const dummy = svg.append("text").attr("font-size", fontSize).attr("font-family", FONT_FAMILY).attr("visibility", "hidden").text(text);
|
|
11086
|
+
const measured = dummy.node()?.getComputedTextLength?.() ?? 0;
|
|
11087
|
+
dummy.remove();
|
|
11088
|
+
return measured > 0 ? measured : text.length * fontSize * 0.6;
|
|
11089
|
+
};
|
|
11090
|
+
const labelWidths = /* @__PURE__ */ new Map();
|
|
11091
|
+
for (const role of presentRoles) {
|
|
11092
|
+
labelWidths.set(role, measureLabelW(ROLE_LABELS[role], LEGEND_ENTRY_FONT_SIZE));
|
|
11093
|
+
}
|
|
11094
|
+
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
11095
|
+
const groupName = "Role";
|
|
11096
|
+
const pillWidth = groupName.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
11097
|
+
const pillH = LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2;
|
|
11098
|
+
let totalWidth;
|
|
11099
|
+
let entriesWidth = 0;
|
|
11100
|
+
if (semanticActive) {
|
|
11101
|
+
for (const role of presentRoles) {
|
|
11102
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + labelWidths.get(role) + LEGEND_ENTRY_TRAIL;
|
|
11103
|
+
}
|
|
11104
|
+
totalWidth = LEGEND_CAPSULE_PAD * 2 + pillWidth + LEGEND_ENTRY_TRAIL + entriesWidth;
|
|
11105
|
+
} else {
|
|
11106
|
+
totalWidth = pillWidth;
|
|
11107
|
+
}
|
|
11108
|
+
const legendX = (viewW - totalWidth) / 2;
|
|
11109
|
+
const legendY = viewH - DIAGRAM_PADDING5 - LEGEND_HEIGHT;
|
|
11110
|
+
const semanticLegendG = svg.append("g").attr("class", "er-semantic-legend").attr("data-legend-group", "role").attr("transform", `translate(${legendX}, ${legendY})`).style("cursor", "pointer");
|
|
11111
|
+
if (semanticActive) {
|
|
11112
|
+
semanticLegendG.append("rect").attr("width", totalWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
11113
|
+
semanticLegendG.append("rect").attr("x", LEGEND_CAPSULE_PAD).attr("y", LEGEND_CAPSULE_PAD).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", palette.bg);
|
|
11114
|
+
semanticLegendG.append("rect").attr("x", LEGEND_CAPSULE_PAD).attr("y", LEGEND_CAPSULE_PAD).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
11115
|
+
semanticLegendG.append("text").attr("x", LEGEND_CAPSULE_PAD + pillWidth / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", palette.text).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).text(groupName);
|
|
11116
|
+
let entryX = LEGEND_CAPSULE_PAD + pillWidth + LEGEND_ENTRY_TRAIL;
|
|
11117
|
+
for (const role of presentRoles) {
|
|
11118
|
+
const label = ROLE_LABELS[role];
|
|
11119
|
+
const roleColor = palette.colors[ROLE_COLORS[role]];
|
|
11120
|
+
const entryG = semanticLegendG.append("g").attr("data-legend-entry", role);
|
|
11121
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", roleColor);
|
|
11122
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
11123
|
+
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("font-family", FONT_FAMILY).text(label);
|
|
11124
|
+
entryX = textX + labelWidths.get(role) + LEGEND_ENTRY_TRAIL;
|
|
11125
|
+
}
|
|
11126
|
+
} else {
|
|
11127
|
+
semanticLegendG.append("rect").attr("width", pillWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
11128
|
+
semanticLegendG.append("text").attr("x", pillWidth / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", palette.textMuted).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).text(groupName);
|
|
11129
|
+
}
|
|
10852
11130
|
}
|
|
10853
11131
|
}
|
|
10854
11132
|
}
|
|
@@ -10897,8 +11175,10 @@ var init_renderer5 = __esm({
|
|
|
10897
11175
|
init_color_utils();
|
|
10898
11176
|
init_palettes();
|
|
10899
11177
|
init_tag_groups();
|
|
11178
|
+
init_legend_constants();
|
|
10900
11179
|
init_parser3();
|
|
10901
11180
|
init_layout4();
|
|
11181
|
+
init_classify();
|
|
10902
11182
|
DIAGRAM_PADDING5 = 20;
|
|
10903
11183
|
MAX_SCALE4 = 3;
|
|
10904
11184
|
TABLE_FONT_SIZE = 13;
|
|
@@ -11026,9 +11306,10 @@ function layoutInitiativeStatus(parsed, collapseResult) {
|
|
|
11026
11306
|
const dagreEdge = g.edge(edge.source, edge.target, `e${i}`);
|
|
11027
11307
|
const dagrePoints = dagreEdge?.points ?? [];
|
|
11028
11308
|
const hasIntermediateRank = allNodeX.some((x) => x > src.x + 20 && x < tgt.x - 20);
|
|
11029
|
-
const step = Math.min((enterX - exitX) * 0.15, 20);
|
|
11309
|
+
const step = Math.max(0, Math.min((enterX - exitX) * 0.15, 20));
|
|
11030
11310
|
const isBackEdge = tgt.x < src.x - 5;
|
|
11031
|
-
const
|
|
11311
|
+
const isTopExit = !isBackEdge && tgt.x > src.x && !hasIntermediateRank && tgt.y < src.y - NODESEP;
|
|
11312
|
+
const isBottomExit = !isBackEdge && tgt.x > src.x && !hasIntermediateRank && tgt.y > src.y + NODESEP;
|
|
11032
11313
|
let points;
|
|
11033
11314
|
if (isBackEdge) {
|
|
11034
11315
|
const routeAbove = Math.min(src.y, tgt.y) > avgNodeY;
|
|
@@ -11038,28 +11319,43 @@ function layoutInitiativeStatus(parsed, collapseResult) {
|
|
|
11038
11319
|
const spreadDir = avgNodeX < rawMidX ? 1 : -1;
|
|
11039
11320
|
const unclamped = Math.abs(src.x - tgt.x) < NODE_WIDTH ? rawMidX + spreadDir * BACK_EDGE_MIN_SPREAD : rawMidX;
|
|
11040
11321
|
const midX = Math.min(src.x, Math.max(tgt.x, unclamped));
|
|
11322
|
+
const srcDepart = Math.max(midX + 1, src.x - TOP_EXIT_STEP);
|
|
11323
|
+
const tgtApproach = Math.min(midX - 1, tgt.x + TOP_EXIT_STEP);
|
|
11041
11324
|
if (routeAbove) {
|
|
11042
11325
|
const arcY = Math.min(src.y - srcHalfH, tgt.y - tgtHalfH) - BACK_EDGE_MARGIN;
|
|
11043
11326
|
points = [
|
|
11044
11327
|
{ x: src.x, y: src.y - srcHalfH },
|
|
11328
|
+
{ x: srcDepart, y: src.y - srcHalfH - TOP_EXIT_STEP },
|
|
11045
11329
|
{ x: midX, y: arcY },
|
|
11330
|
+
{ x: tgtApproach, y: tgt.y - tgtHalfH - TOP_EXIT_STEP },
|
|
11046
11331
|
{ x: tgt.x, y: tgt.y - tgtHalfH }
|
|
11047
11332
|
];
|
|
11048
11333
|
} else {
|
|
11049
11334
|
const arcY = Math.max(src.y + srcHalfH, tgt.y + tgtHalfH) + BACK_EDGE_MARGIN;
|
|
11050
11335
|
points = [
|
|
11051
11336
|
{ x: src.x, y: src.y + srcHalfH },
|
|
11337
|
+
{ x: srcDepart, y: src.y + srcHalfH + TOP_EXIT_STEP },
|
|
11052
11338
|
{ x: midX, y: arcY },
|
|
11339
|
+
{ x: tgtApproach, y: tgt.y + tgtHalfH + TOP_EXIT_STEP },
|
|
11053
11340
|
{ x: tgt.x, y: tgt.y + tgtHalfH }
|
|
11054
11341
|
];
|
|
11055
11342
|
}
|
|
11056
|
-
} else if (
|
|
11057
|
-
const exitY =
|
|
11058
|
-
const
|
|
11059
|
-
const midY = (exitY + tgt.y) / 2;
|
|
11343
|
+
} else if (isTopExit) {
|
|
11344
|
+
const exitY = src.y - src.height / 2;
|
|
11345
|
+
const p1x = Math.min(Math.max(src.x, src.x + yOffset + TOP_EXIT_STEP), (src.x + enterX) / 2 - 1);
|
|
11060
11346
|
points = [
|
|
11061
11347
|
{ x: src.x, y: exitY },
|
|
11062
|
-
{ x:
|
|
11348
|
+
{ x: p1x, y: exitY - TOP_EXIT_STEP },
|
|
11349
|
+
{ x: enterX - step, y: tgt.y + yOffset },
|
|
11350
|
+
{ x: enterX, y: tgt.y }
|
|
11351
|
+
];
|
|
11352
|
+
} else if (isBottomExit) {
|
|
11353
|
+
const exitY = src.y + src.height / 2;
|
|
11354
|
+
const p1x = Math.min(Math.max(src.x, src.x + yOffset + TOP_EXIT_STEP), (src.x + enterX) / 2 - 1);
|
|
11355
|
+
points = [
|
|
11356
|
+
{ x: src.x, y: exitY },
|
|
11357
|
+
{ x: p1x, y: exitY + TOP_EXIT_STEP },
|
|
11358
|
+
{ x: enterX - step, y: tgt.y + yOffset },
|
|
11063
11359
|
{ x: enterX, y: tgt.y }
|
|
11064
11360
|
];
|
|
11065
11361
|
} else if (tgt.x > src.x && !hasIntermediateRank) {
|
|
@@ -11157,7 +11453,7 @@ function layoutInitiativeStatus(parsed, collapseResult) {
|
|
|
11157
11453
|
totalHeight += 40;
|
|
11158
11454
|
return { nodes: layoutNodes, edges: layoutEdges, groups: layoutGroups, width: totalWidth, height: totalHeight };
|
|
11159
11455
|
}
|
|
11160
|
-
var import_dagre4, STATUS_PRIORITY, PHI, NODE_HEIGHT, NODE_WIDTH, GROUP_PADDING, NODESEP, RANKSEP, PARALLEL_SPACING, PARALLEL_EDGE_MARGIN, MAX_PARALLEL_EDGES, BACK_EDGE_MARGIN, BACK_EDGE_MIN_SPREAD, CHAR_WIDTH_RATIO, NODE_FONT_SIZE, NODE_TEXT_PADDING;
|
|
11456
|
+
var import_dagre4, STATUS_PRIORITY, PHI, NODE_HEIGHT, NODE_WIDTH, GROUP_PADDING, NODESEP, RANKSEP, PARALLEL_SPACING, PARALLEL_EDGE_MARGIN, MAX_PARALLEL_EDGES, BACK_EDGE_MARGIN, BACK_EDGE_MIN_SPREAD, TOP_EXIT_STEP, CHAR_WIDTH_RATIO, NODE_FONT_SIZE, NODE_TEXT_PADDING;
|
|
11161
11457
|
var init_layout5 = __esm({
|
|
11162
11458
|
"src/initiative-status/layout.ts"() {
|
|
11163
11459
|
"use strict";
|
|
@@ -11174,6 +11470,7 @@ var init_layout5 = __esm({
|
|
|
11174
11470
|
MAX_PARALLEL_EDGES = 5;
|
|
11175
11471
|
BACK_EDGE_MARGIN = 40;
|
|
11176
11472
|
BACK_EDGE_MIN_SPREAD = Math.round(NODE_WIDTH * 0.75);
|
|
11473
|
+
TOP_EXIT_STEP = 10;
|
|
11177
11474
|
CHAR_WIDTH_RATIO = 0.6;
|
|
11178
11475
|
NODE_FONT_SIZE = 13;
|
|
11179
11476
|
NODE_TEXT_PADDING = 12;
|
|
@@ -11672,7 +11969,7 @@ __export(layout_exports6, {
|
|
|
11672
11969
|
layoutC4Deployment: () => layoutC4Deployment,
|
|
11673
11970
|
rollUpContextRelationships: () => rollUpContextRelationships
|
|
11674
11971
|
});
|
|
11675
|
-
function computeEdgePenalty(edgeList, nodePositions, degrees) {
|
|
11972
|
+
function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
|
|
11676
11973
|
let penalty = 0;
|
|
11677
11974
|
for (const edge of edgeList) {
|
|
11678
11975
|
const sx = nodePositions.get(edge.source);
|
|
@@ -11682,6 +11979,32 @@ function computeEdgePenalty(edgeList, nodePositions, degrees) {
|
|
|
11682
11979
|
const weight = Math.min(degrees.get(edge.source) ?? 1, degrees.get(edge.target) ?? 1);
|
|
11683
11980
|
penalty += dist * weight;
|
|
11684
11981
|
}
|
|
11982
|
+
if (nodeGeometry) {
|
|
11983
|
+
for (const edge of edgeList) {
|
|
11984
|
+
const geomA = nodeGeometry.get(edge.source);
|
|
11985
|
+
const geomB = nodeGeometry.get(edge.target);
|
|
11986
|
+
if (!geomA || !geomB) continue;
|
|
11987
|
+
const ax = nodePositions.get(edge.source) ?? 0;
|
|
11988
|
+
const bx = nodePositions.get(edge.target) ?? 0;
|
|
11989
|
+
const ay = geomA.y;
|
|
11990
|
+
const by = geomB.y;
|
|
11991
|
+
if (ay === by) continue;
|
|
11992
|
+
const edgeMinX = Math.min(ax, bx);
|
|
11993
|
+
const edgeMaxX = Math.max(ax, bx);
|
|
11994
|
+
const edgeMinY = Math.min(ay, by);
|
|
11995
|
+
const edgeMaxY = Math.max(ay, by);
|
|
11996
|
+
for (const [name, geomC] of nodeGeometry) {
|
|
11997
|
+
if (name === edge.source || name === edge.target) continue;
|
|
11998
|
+
const cx = nodePositions.get(name) ?? 0;
|
|
11999
|
+
const cy = geomC.y;
|
|
12000
|
+
const hw = geomC.width / 2;
|
|
12001
|
+
const hh = geomC.height / 2;
|
|
12002
|
+
if (cx + hw > edgeMinX && cx - hw < edgeMaxX && cy + hh > edgeMinY && cy - hh < edgeMaxY) {
|
|
12003
|
+
penalty += EDGE_NODE_COLLISION_WEIGHT;
|
|
12004
|
+
}
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
12007
|
+
}
|
|
11685
12008
|
return penalty;
|
|
11686
12009
|
}
|
|
11687
12010
|
function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
@@ -11691,6 +12014,11 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11691
12014
|
degrees.set(edge.source, (degrees.get(edge.source) ?? 0) + 1);
|
|
11692
12015
|
degrees.set(edge.target, (degrees.get(edge.target) ?? 0) + 1);
|
|
11693
12016
|
}
|
|
12017
|
+
const nodeGeometry = /* @__PURE__ */ new Map();
|
|
12018
|
+
for (const name of g.nodes()) {
|
|
12019
|
+
const pos = g.node(name);
|
|
12020
|
+
if (pos) nodeGeometry.set(name, { y: pos.y, width: pos.width, height: pos.height });
|
|
12021
|
+
}
|
|
11694
12022
|
const rankMap = /* @__PURE__ */ new Map();
|
|
11695
12023
|
for (const name of g.nodes()) {
|
|
11696
12024
|
const pos = g.node(name);
|
|
@@ -11733,7 +12061,7 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11733
12061
|
const pos = g.node(name);
|
|
11734
12062
|
if (pos) basePositions.set(name, pos.x);
|
|
11735
12063
|
}
|
|
11736
|
-
const currentPenalty = computeEdgePenalty(edgeList, basePositions, degrees);
|
|
12064
|
+
const currentPenalty = computeEdgePenalty(edgeList, basePositions, degrees, nodeGeometry);
|
|
11737
12065
|
let bestPerm = [...partition];
|
|
11738
12066
|
let bestPenalty = currentPenalty;
|
|
11739
12067
|
if (partition.length <= 8) {
|
|
@@ -11743,7 +12071,7 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11743
12071
|
for (let i = 0; i < perm.length; i++) {
|
|
11744
12072
|
testPositions.set(perm[i], xSlots[i]);
|
|
11745
12073
|
}
|
|
11746
|
-
const penalty = computeEdgePenalty(edgeList, testPositions, degrees);
|
|
12074
|
+
const penalty = computeEdgePenalty(edgeList, testPositions, degrees, nodeGeometry);
|
|
11747
12075
|
if (penalty < bestPenalty) {
|
|
11748
12076
|
bestPenalty = penalty;
|
|
11749
12077
|
bestPerm = [...perm];
|
|
@@ -11761,13 +12089,13 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11761
12089
|
for (let k = 0; k < workingOrder.length; k++) {
|
|
11762
12090
|
testPositions.set(workingOrder[k], xSlots[k]);
|
|
11763
12091
|
}
|
|
11764
|
-
const before = computeEdgePenalty(edgeList, testPositions, degrees);
|
|
12092
|
+
const before = computeEdgePenalty(edgeList, testPositions, degrees, nodeGeometry);
|
|
11765
12093
|
[workingOrder[i], workingOrder[i + 1]] = [workingOrder[i + 1], workingOrder[i]];
|
|
11766
12094
|
const testPositions2 = new Map(basePositions);
|
|
11767
12095
|
for (let k = 0; k < workingOrder.length; k++) {
|
|
11768
12096
|
testPositions2.set(workingOrder[k], xSlots[k]);
|
|
11769
12097
|
}
|
|
11770
|
-
const after = computeEdgePenalty(edgeList, testPositions2, degrees);
|
|
12098
|
+
const after = computeEdgePenalty(edgeList, testPositions2, degrees, nodeGeometry);
|
|
11771
12099
|
if (after < before) {
|
|
11772
12100
|
improved = true;
|
|
11773
12101
|
if (after < bestPenalty) {
|
|
@@ -11995,31 +12323,27 @@ function computeC4NodeDimensions(el, options) {
|
|
|
11995
12323
|
height += CARD_V_PAD3;
|
|
11996
12324
|
return { width, height };
|
|
11997
12325
|
}
|
|
11998
|
-
function computeLegendGroups3(tagGroups
|
|
12326
|
+
function computeLegendGroups3(tagGroups) {
|
|
11999
12327
|
const result = [];
|
|
12000
12328
|
for (const group of tagGroups) {
|
|
12001
12329
|
const entries = [];
|
|
12002
12330
|
for (const entry of group.entries) {
|
|
12003
|
-
if (usedValuesByGroup) {
|
|
12004
|
-
const used = usedValuesByGroup.get(group.name.toLowerCase());
|
|
12005
|
-
if (!used?.has(entry.value.toLowerCase())) continue;
|
|
12006
|
-
}
|
|
12007
12331
|
entries.push({ value: entry.value, color: entry.color });
|
|
12008
12332
|
}
|
|
12009
12333
|
if (entries.length === 0) continue;
|
|
12010
|
-
const nameW = group.name.length *
|
|
12011
|
-
let capsuleW =
|
|
12334
|
+
const nameW = group.name.length * LEGEND_PILL_FONT_W4 + LEGEND_PILL_PAD4 * 2;
|
|
12335
|
+
let capsuleW = LEGEND_CAPSULE_PAD4;
|
|
12012
12336
|
for (const e of entries) {
|
|
12013
|
-
capsuleW +=
|
|
12337
|
+
capsuleW += LEGEND_DOT_R4 * 2 + LEGEND_ENTRY_DOT_GAP4 + e.value.length * LEGEND_ENTRY_FONT_W5 + LEGEND_ENTRY_TRAIL4;
|
|
12014
12338
|
}
|
|
12015
|
-
capsuleW +=
|
|
12339
|
+
capsuleW += LEGEND_CAPSULE_PAD4;
|
|
12016
12340
|
result.push({
|
|
12017
12341
|
name: group.name,
|
|
12018
12342
|
entries,
|
|
12019
12343
|
x: 0,
|
|
12020
12344
|
y: 0,
|
|
12021
12345
|
width: nameW + capsuleW,
|
|
12022
|
-
height:
|
|
12346
|
+
height: LEGEND_HEIGHT4
|
|
12023
12347
|
});
|
|
12024
12348
|
}
|
|
12025
12349
|
return result;
|
|
@@ -12139,18 +12463,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
12139
12463
|
}
|
|
12140
12464
|
let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN3 * 2 : 0;
|
|
12141
12465
|
let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN3 * 2 : 0;
|
|
12142
|
-
const
|
|
12143
|
-
for (const el of contextElements) {
|
|
12144
|
-
for (const group of parsed.tagGroups) {
|
|
12145
|
-
const key = group.name.toLowerCase();
|
|
12146
|
-
const val = el.metadata[key];
|
|
12147
|
-
if (val) {
|
|
12148
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
12149
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
12150
|
-
}
|
|
12151
|
-
}
|
|
12152
|
-
}
|
|
12153
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
12466
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
12154
12467
|
if (legendGroups.length > 0) {
|
|
12155
12468
|
const legendY = totalHeight + MARGIN3;
|
|
12156
12469
|
let legendX = MARGIN3;
|
|
@@ -12160,7 +12473,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
12160
12473
|
legendX += lg.width + 12;
|
|
12161
12474
|
}
|
|
12162
12475
|
const legendRight = legendX;
|
|
12163
|
-
const legendBottom = legendY +
|
|
12476
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
12164
12477
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
12165
12478
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
12166
12479
|
}
|
|
@@ -12466,18 +12779,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
12466
12779
|
}
|
|
12467
12780
|
let totalWidth = maxX - minX + MARGIN3 * 2;
|
|
12468
12781
|
let totalHeight = maxY - minY + MARGIN3 * 2;
|
|
12469
|
-
const
|
|
12470
|
-
for (const el of [...containers, ...externals]) {
|
|
12471
|
-
for (const group of parsed.tagGroups) {
|
|
12472
|
-
const key = group.name.toLowerCase();
|
|
12473
|
-
const val = el.metadata[key];
|
|
12474
|
-
if (val) {
|
|
12475
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
12476
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
12477
|
-
}
|
|
12478
|
-
}
|
|
12479
|
-
}
|
|
12480
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
12782
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
12481
12783
|
if (legendGroups.length > 0) {
|
|
12482
12784
|
const legendY = totalHeight + MARGIN3;
|
|
12483
12785
|
let legendX = MARGIN3;
|
|
@@ -12487,7 +12789,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
12487
12789
|
legendX += lg.width + 12;
|
|
12488
12790
|
}
|
|
12489
12791
|
const legendRight = legendX;
|
|
12490
|
-
const legendBottom = legendY +
|
|
12792
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
12491
12793
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
12492
12794
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
12493
12795
|
}
|
|
@@ -12842,21 +13144,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
12842
13144
|
}
|
|
12843
13145
|
let totalWidth = maxX - minX + MARGIN3 * 2;
|
|
12844
13146
|
let totalHeight = maxY - minY + MARGIN3 * 2;
|
|
12845
|
-
const
|
|
12846
|
-
for (const el of [...components, ...externals]) {
|
|
12847
|
-
for (const group of parsed.tagGroups) {
|
|
12848
|
-
const key = group.name.toLowerCase();
|
|
12849
|
-
let val = el.metadata[key];
|
|
12850
|
-
if (!val && components.includes(el)) {
|
|
12851
|
-
val = targetContainer.metadata[key] ?? system.metadata[key];
|
|
12852
|
-
}
|
|
12853
|
-
if (val) {
|
|
12854
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
12855
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
12856
|
-
}
|
|
12857
|
-
}
|
|
12858
|
-
}
|
|
12859
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
13147
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
12860
13148
|
if (legendGroups.length > 0) {
|
|
12861
13149
|
const legendY = totalHeight + MARGIN3;
|
|
12862
13150
|
let legendX = MARGIN3;
|
|
@@ -12866,7 +13154,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
12866
13154
|
legendX += lg.width + 12;
|
|
12867
13155
|
}
|
|
12868
13156
|
const legendRight = legendX;
|
|
12869
|
-
const legendBottom = legendY +
|
|
13157
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
12870
13158
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
12871
13159
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
12872
13160
|
}
|
|
@@ -13111,18 +13399,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
13111
13399
|
}
|
|
13112
13400
|
let totalWidth = maxX - minX + MARGIN3 * 2;
|
|
13113
13401
|
let totalHeight = maxY - minY + MARGIN3 * 2;
|
|
13114
|
-
const
|
|
13115
|
-
for (const r of refEntries) {
|
|
13116
|
-
for (const group of parsed.tagGroups) {
|
|
13117
|
-
const key = group.name.toLowerCase();
|
|
13118
|
-
const val = r.element.metadata[key];
|
|
13119
|
-
if (val) {
|
|
13120
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
13121
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
13122
|
-
}
|
|
13123
|
-
}
|
|
13124
|
-
}
|
|
13125
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
13402
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
13126
13403
|
if (legendGroups.length > 0) {
|
|
13127
13404
|
const legendY = totalHeight + MARGIN3;
|
|
13128
13405
|
let legendX = MARGIN3;
|
|
@@ -13132,13 +13409,13 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
13132
13409
|
legendX += lg.width + 12;
|
|
13133
13410
|
}
|
|
13134
13411
|
const legendRight = legendX;
|
|
13135
|
-
const legendBottom = legendY +
|
|
13412
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
13136
13413
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
13137
13414
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
13138
13415
|
}
|
|
13139
13416
|
return { nodes, edges, legend: legendGroups, groupBoundaries, width: totalWidth, height: totalHeight };
|
|
13140
13417
|
}
|
|
13141
|
-
var import_dagre5, CHAR_WIDTH5, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, DESC_LINE_HEIGHT, DESC_CHAR_WIDTH, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_CHAR_WIDTH, MARGIN3, BOUNDARY_PAD, GROUP_BOUNDARY_PAD,
|
|
13418
|
+
var import_dagre5, CHAR_WIDTH5, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, DESC_LINE_HEIGHT, DESC_CHAR_WIDTH, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_CHAR_WIDTH, MARGIN3, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, LEGEND_HEIGHT4, LEGEND_PILL_FONT_SIZE2, LEGEND_PILL_FONT_W4, LEGEND_PILL_PAD4, LEGEND_DOT_R4, LEGEND_ENTRY_FONT_SIZE2, LEGEND_ENTRY_FONT_W5, LEGEND_ENTRY_DOT_GAP4, LEGEND_ENTRY_TRAIL4, LEGEND_CAPSULE_PAD4, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
|
|
13142
13419
|
var init_layout6 = __esm({
|
|
13143
13420
|
"src/c4/layout.ts"() {
|
|
13144
13421
|
"use strict";
|
|
@@ -13158,16 +13435,17 @@ var init_layout6 = __esm({
|
|
|
13158
13435
|
MARGIN3 = 40;
|
|
13159
13436
|
BOUNDARY_PAD = 40;
|
|
13160
13437
|
GROUP_BOUNDARY_PAD = 24;
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
LEGEND_ENTRY_FONT_W5 =
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13438
|
+
LEGEND_HEIGHT4 = 28;
|
|
13439
|
+
LEGEND_PILL_FONT_SIZE2 = 11;
|
|
13440
|
+
LEGEND_PILL_FONT_W4 = LEGEND_PILL_FONT_SIZE2 * 0.6;
|
|
13441
|
+
LEGEND_PILL_PAD4 = 16;
|
|
13442
|
+
LEGEND_DOT_R4 = 4;
|
|
13443
|
+
LEGEND_ENTRY_FONT_SIZE2 = 10;
|
|
13444
|
+
LEGEND_ENTRY_FONT_W5 = LEGEND_ENTRY_FONT_SIZE2 * 0.6;
|
|
13445
|
+
LEGEND_ENTRY_DOT_GAP4 = 4;
|
|
13446
|
+
LEGEND_ENTRY_TRAIL4 = 8;
|
|
13447
|
+
LEGEND_CAPSULE_PAD4 = 4;
|
|
13448
|
+
EDGE_NODE_COLLISION_WEIGHT = 5e3;
|
|
13171
13449
|
META_EXCLUDE_KEYS = /* @__PURE__ */ new Set(["description", "tech", "technology", "is a"]);
|
|
13172
13450
|
}
|
|
13173
13451
|
});
|
|
@@ -13243,8 +13521,14 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
13243
13521
|
if (width <= 0 || height <= 0) return;
|
|
13244
13522
|
const titleHeight = parsed.title ? TITLE_HEIGHT4 + 10 : 0;
|
|
13245
13523
|
const diagramW = layout.width;
|
|
13246
|
-
const
|
|
13247
|
-
const
|
|
13524
|
+
const hasLegend = layout.legend.length > 0;
|
|
13525
|
+
const C4_LAYOUT_MARGIN = 40;
|
|
13526
|
+
const LEGEND_FIXED_GAP4 = 8;
|
|
13527
|
+
const fixedLegend = !exportDims && hasLegend;
|
|
13528
|
+
const legendLayoutSpace = C4_LAYOUT_MARGIN + LEGEND_HEIGHT;
|
|
13529
|
+
const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : 0;
|
|
13530
|
+
const diagramH = fixedLegend ? layout.height - legendLayoutSpace : layout.height;
|
|
13531
|
+
const availH = height - titleHeight - legendReserveH;
|
|
13248
13532
|
const scaleX = (width - DIAGRAM_PADDING7 * 2) / diagramW;
|
|
13249
13533
|
const scaleY = (availH - DIAGRAM_PADDING7 * 2) / diagramH;
|
|
13250
13534
|
const scale = Math.min(MAX_SCALE6, scaleX, scaleY);
|
|
@@ -13316,6 +13600,20 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
13316
13600
|
}
|
|
13317
13601
|
for (const node of layout.nodes) {
|
|
13318
13602
|
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "c4-card").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
|
|
13603
|
+
if (activeTagGroup) {
|
|
13604
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
13605
|
+
const tagValue = node.metadata[tagKey];
|
|
13606
|
+
if (tagValue) {
|
|
13607
|
+
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
13608
|
+
} else {
|
|
13609
|
+
const tagGroup = parsed.tagGroups.find(
|
|
13610
|
+
(g) => g.name.toLowerCase() === tagKey || g.alias?.toLowerCase() === tagKey
|
|
13611
|
+
);
|
|
13612
|
+
if (tagGroup?.defaultValue) {
|
|
13613
|
+
nodeG.attr(`data-tag-${tagKey}`, tagGroup.defaultValue.toLowerCase());
|
|
13614
|
+
}
|
|
13615
|
+
}
|
|
13616
|
+
}
|
|
13319
13617
|
if (node.importPath) {
|
|
13320
13618
|
nodeG.attr("data-import-path", node.importPath);
|
|
13321
13619
|
}
|
|
@@ -13368,36 +13666,12 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
13368
13666
|
nodeG.append("rect").attr("x", -w / 2).attr("y", h / 2 - DRILL_BAR_HEIGHT).attr("width", w).attr("height", DRILL_BAR_HEIGHT).attr("fill", stroke2).attr("clip-path", `url(#${clipId})`).attr("class", "c4-drill-bar");
|
|
13369
13667
|
}
|
|
13370
13668
|
}
|
|
13371
|
-
if (
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
13375
|
-
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
13376
|
-
const pillLabel = group.name;
|
|
13377
|
-
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W6 + LEGEND_PILL_PAD6;
|
|
13378
|
-
const gEl = contentG.append("g").attr("transform", `translate(${group.x}, ${group.y})`).attr("class", "c4-legend-group").attr("data-legend-group", group.name.toLowerCase()).style("cursor", "pointer");
|
|
13379
|
-
if (isActive) {
|
|
13380
|
-
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT7).attr("rx", LEGEND_HEIGHT7 / 2).attr("fill", groupBg);
|
|
13381
|
-
}
|
|
13382
|
-
const pillX = isActive ? LEGEND_CAPSULE_PAD6 : 0;
|
|
13383
|
-
const pillY = isActive ? LEGEND_CAPSULE_PAD6 : 0;
|
|
13384
|
-
const pillH = LEGEND_HEIGHT7 - (isActive ? LEGEND_CAPSULE_PAD6 * 2 : 0);
|
|
13385
|
-
gEl.append("rect").attr("x", pillX).attr("y", pillY).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isActive ? palette.bg : groupBg);
|
|
13386
|
-
if (isActive) {
|
|
13387
|
-
gEl.append("rect").attr("x", pillX).attr("y", pillY).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
13388
|
-
}
|
|
13389
|
-
gEl.append("text").attr("x", pillX + pillWidth / 2).attr("y", LEGEND_HEIGHT7 / 2 + LEGEND_PILL_FONT_SIZE4 / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE4).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(pillLabel);
|
|
13390
|
-
if (isActive) {
|
|
13391
|
-
let entryX = pillX + pillWidth + 4;
|
|
13392
|
-
for (const entry of group.entries) {
|
|
13393
|
-
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
13394
|
-
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R7).attr("cy", LEGEND_HEIGHT7 / 2).attr("r", LEGEND_DOT_R7).attr("fill", entry.color);
|
|
13395
|
-
const textX = entryX + LEGEND_DOT_R7 * 2 + LEGEND_ENTRY_DOT_GAP6;
|
|
13396
|
-
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT7 / 2 + LEGEND_ENTRY_FONT_SIZE5 / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE5).attr("fill", palette.textMuted).text(entry.value);
|
|
13397
|
-
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W6 + LEGEND_ENTRY_TRAIL6;
|
|
13398
|
-
}
|
|
13399
|
-
}
|
|
13669
|
+
if (hasLegend) {
|
|
13670
|
+
const legendParent = fixedLegend ? svg.append("g").attr("class", "c4-legend-fixed").attr("transform", `translate(0, ${height - DIAGRAM_PADDING7 - LEGEND_HEIGHT})`) : contentG.append("g").attr("class", "c4-legend");
|
|
13671
|
+
if (activeTagGroup) {
|
|
13672
|
+
legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
13400
13673
|
}
|
|
13674
|
+
renderLegend2(legendParent, layout, palette, isDark, activeTagGroup, fixedLegend ? width : null);
|
|
13401
13675
|
}
|
|
13402
13676
|
}
|
|
13403
13677
|
function renderC4ContextForExport(content, theme, palette) {
|
|
@@ -13685,33 +13959,47 @@ function placeEdgeLabels(labels, edges, obstacleRects) {
|
|
|
13685
13959
|
placedRects.push({ x: lbl.x, y: lbl.y, w: lbl.bgW, h: lbl.bgH });
|
|
13686
13960
|
}
|
|
13687
13961
|
}
|
|
13688
|
-
function renderLegend2(
|
|
13689
|
-
|
|
13962
|
+
function renderLegend2(parent, layout, palette, isDark, activeTagGroup, fixedWidth) {
|
|
13963
|
+
const visibleGroups = activeTagGroup != null ? layout.legend.filter((g) => g.name.toLowerCase() === (activeTagGroup ?? "").toLowerCase()) : layout.legend;
|
|
13964
|
+
const pillWidthOf = (g) => g.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
13965
|
+
const effectiveW = (g) => activeTagGroup != null ? g.width : pillWidthOf(g);
|
|
13966
|
+
let fixedPositions = null;
|
|
13967
|
+
if (fixedWidth != null && visibleGroups.length > 0) {
|
|
13968
|
+
fixedPositions = /* @__PURE__ */ new Map();
|
|
13969
|
+
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
13970
|
+
let cx = Math.max(DIAGRAM_PADDING7, (fixedWidth - totalW) / 2);
|
|
13971
|
+
for (const g of visibleGroups) {
|
|
13972
|
+
fixedPositions.set(g.name, cx);
|
|
13973
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
13974
|
+
}
|
|
13975
|
+
}
|
|
13976
|
+
for (const group of visibleGroups) {
|
|
13690
13977
|
const isActive = activeTagGroup != null && group.name.toLowerCase() === (activeTagGroup ?? "").toLowerCase();
|
|
13691
|
-
if (activeTagGroup != null && !isActive) continue;
|
|
13692
13978
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
13693
13979
|
const pillLabel = group.name;
|
|
13694
|
-
const pillWidth =
|
|
13695
|
-
const
|
|
13980
|
+
const pillWidth = pillWidthOf(group);
|
|
13981
|
+
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
13982
|
+
const gY = fixedPositions != null ? 0 : group.y;
|
|
13983
|
+
const gEl = parent.append("g").attr("transform", `translate(${gX}, ${gY})`).attr("class", "c4-legend-group").attr("data-legend-group", group.name.toLowerCase()).style("cursor", "pointer");
|
|
13696
13984
|
if (isActive) {
|
|
13697
|
-
gEl.append("rect").attr("width", group.width).attr("height",
|
|
13985
|
+
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
13698
13986
|
}
|
|
13699
|
-
const pillX = isActive ?
|
|
13700
|
-
const pillY = isActive ?
|
|
13701
|
-
const pillH =
|
|
13987
|
+
const pillX = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
13988
|
+
const pillY = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
13989
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
13702
13990
|
gEl.append("rect").attr("x", pillX).attr("y", pillY).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isActive ? palette.bg : groupBg);
|
|
13703
13991
|
if (isActive) {
|
|
13704
13992
|
gEl.append("rect").attr("x", pillX).attr("y", pillY).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
13705
13993
|
}
|
|
13706
|
-
gEl.append("text").attr("x", pillX + pillWidth / 2).attr("y",
|
|
13994
|
+
gEl.append("text").attr("x", pillX + pillWidth / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(pillLabel);
|
|
13707
13995
|
if (isActive) {
|
|
13708
13996
|
let entryX = pillX + pillWidth + 4;
|
|
13709
13997
|
for (const entry of group.entries) {
|
|
13710
13998
|
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
13711
|
-
entryG.append("circle").attr("cx", entryX +
|
|
13712
|
-
const textX = entryX +
|
|
13713
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
13714
|
-
entryX = textX + entry.value.length *
|
|
13999
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
14000
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
14001
|
+
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).text(entry.value);
|
|
14002
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
13715
14003
|
}
|
|
13716
14004
|
}
|
|
13717
14005
|
}
|
|
@@ -13723,8 +14011,14 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
|
|
|
13723
14011
|
if (width <= 0 || height <= 0) return;
|
|
13724
14012
|
const titleHeight = parsed.title ? TITLE_HEIGHT4 + 10 : 0;
|
|
13725
14013
|
const diagramW = layout.width;
|
|
13726
|
-
const
|
|
13727
|
-
const
|
|
14014
|
+
const hasLegend = layout.legend.length > 0;
|
|
14015
|
+
const C4_LAYOUT_MARGIN = 40;
|
|
14016
|
+
const LEGEND_FIXED_GAP4 = 8;
|
|
14017
|
+
const fixedLegend = !exportDims && hasLegend;
|
|
14018
|
+
const legendLayoutSpace = C4_LAYOUT_MARGIN + LEGEND_HEIGHT;
|
|
14019
|
+
const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : 0;
|
|
14020
|
+
const diagramH = fixedLegend ? layout.height - legendLayoutSpace : layout.height;
|
|
14021
|
+
const availH = height - titleHeight - legendReserveH;
|
|
13728
14022
|
const scaleX = (width - DIAGRAM_PADDING7 * 2) / diagramW;
|
|
13729
14023
|
const scaleY = (availH - DIAGRAM_PADDING7 * 2) / diagramH;
|
|
13730
14024
|
const scale = Math.min(MAX_SCALE6, scaleX, scaleY);
|
|
@@ -13795,6 +14089,20 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
|
|
|
13795
14089
|
renderEdges(contentG, layout.edges, palette, onClickItem, boundaryLabelObstacles);
|
|
13796
14090
|
for (const node of layout.nodes) {
|
|
13797
14091
|
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "c4-card").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
|
|
14092
|
+
if (activeTagGroup) {
|
|
14093
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
14094
|
+
const tagValue = node.metadata[tagKey];
|
|
14095
|
+
if (tagValue) {
|
|
14096
|
+
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
14097
|
+
} else {
|
|
14098
|
+
const tagGroup = parsed.tagGroups.find(
|
|
14099
|
+
(g) => g.name.toLowerCase() === tagKey || g.alias?.toLowerCase() === tagKey
|
|
14100
|
+
);
|
|
14101
|
+
if (tagGroup?.defaultValue) {
|
|
14102
|
+
nodeG.attr(`data-tag-${tagKey}`, tagGroup.defaultValue.toLowerCase());
|
|
14103
|
+
}
|
|
14104
|
+
}
|
|
14105
|
+
}
|
|
13798
14106
|
if (node.shape) {
|
|
13799
14107
|
nodeG.attr("data-shape", node.shape);
|
|
13800
14108
|
}
|
|
@@ -13880,8 +14188,12 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
|
|
|
13880
14188
|
nodeG.append("rect").attr("x", -w / 2).attr("y", h / 2 - DRILL_BAR_HEIGHT).attr("width", w).attr("height", DRILL_BAR_HEIGHT).attr("fill", stroke2).attr("clip-path", `url(#${clipId})`).attr("class", "c4-drill-bar");
|
|
13881
14189
|
}
|
|
13882
14190
|
}
|
|
13883
|
-
if (
|
|
13884
|
-
|
|
14191
|
+
if (hasLegend) {
|
|
14192
|
+
const legendParent = fixedLegend ? svg.append("g").attr("class", "c4-legend-fixed").attr("transform", `translate(0, ${height - DIAGRAM_PADDING7 - LEGEND_HEIGHT})`) : contentG.append("g").attr("class", "c4-legend");
|
|
14193
|
+
if (activeTagGroup) {
|
|
14194
|
+
legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
14195
|
+
}
|
|
14196
|
+
renderLegend2(legendParent, layout, palette, isDark, activeTagGroup, fixedLegend ? width : null);
|
|
13885
14197
|
}
|
|
13886
14198
|
}
|
|
13887
14199
|
function renderC4ContainersForExport(content, systemName, theme, palette) {
|
|
@@ -13983,7 +14295,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
|
|
|
13983
14295
|
document.body.removeChild(el);
|
|
13984
14296
|
}
|
|
13985
14297
|
}
|
|
13986
|
-
var d3Selection7, d3Shape5, DIAGRAM_PADDING7, MAX_SCALE6, TITLE_HEIGHT4, TITLE_FONT_SIZE4, TYPE_FONT_SIZE, NAME_FONT_SIZE, DESC_FONT_SIZE, DESC_LINE_HEIGHT2, DESC_CHAR_WIDTH2, EDGE_LABEL_FONT_SIZE5, TECH_FONT_SIZE, EDGE_STROKE_WIDTH6, NODE_STROKE_WIDTH6, CARD_RADIUS4, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, META_FONT_SIZE3, META_CHAR_WIDTH2, META_LINE_HEIGHT6, 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,
|
|
14298
|
+
var d3Selection7, d3Shape5, DIAGRAM_PADDING7, MAX_SCALE6, TITLE_HEIGHT4, TITLE_FONT_SIZE4, TYPE_FONT_SIZE, NAME_FONT_SIZE, DESC_FONT_SIZE, DESC_LINE_HEIGHT2, DESC_CHAR_WIDTH2, EDGE_LABEL_FONT_SIZE5, TECH_FONT_SIZE, EDGE_STROKE_WIDTH6, NODE_STROKE_WIDTH6, CARD_RADIUS4, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, META_FONT_SIZE3, META_CHAR_WIDTH2, META_LINE_HEIGHT6, 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, lineGenerator5;
|
|
13987
14299
|
var init_renderer7 = __esm({
|
|
13988
14300
|
"src/c4/renderer.ts"() {
|
|
13989
14301
|
"use strict";
|
|
@@ -13994,6 +14306,7 @@ var init_renderer7 = __esm({
|
|
|
13994
14306
|
init_inline_markdown();
|
|
13995
14307
|
init_parser6();
|
|
13996
14308
|
init_layout6();
|
|
14309
|
+
init_legend_constants();
|
|
13997
14310
|
DIAGRAM_PADDING7 = 20;
|
|
13998
14311
|
MAX_SCALE6 = 3;
|
|
13999
14312
|
TITLE_HEIGHT4 = 30;
|
|
@@ -14026,16 +14339,6 @@ var init_renderer7 = __esm({
|
|
|
14026
14339
|
PERSON_LEG_SPAN = 7;
|
|
14027
14340
|
PERSON_ICON_W = PERSON_ARM_SPAN * 2;
|
|
14028
14341
|
PERSON_SW = 1.5;
|
|
14029
|
-
LEGEND_HEIGHT7 = 28;
|
|
14030
|
-
LEGEND_PILL_FONT_SIZE4 = 11;
|
|
14031
|
-
LEGEND_PILL_FONT_W6 = LEGEND_PILL_FONT_SIZE4 * 0.6;
|
|
14032
|
-
LEGEND_PILL_PAD6 = 16;
|
|
14033
|
-
LEGEND_DOT_R7 = 4;
|
|
14034
|
-
LEGEND_ENTRY_FONT_SIZE5 = 10;
|
|
14035
|
-
LEGEND_ENTRY_FONT_W6 = LEGEND_ENTRY_FONT_SIZE5 * 0.6;
|
|
14036
|
-
LEGEND_ENTRY_DOT_GAP6 = 4;
|
|
14037
|
-
LEGEND_ENTRY_TRAIL6 = 8;
|
|
14038
|
-
LEGEND_CAPSULE_PAD6 = 4;
|
|
14039
14342
|
lineGenerator5 = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
|
|
14040
14343
|
}
|
|
14041
14344
|
});
|
|
@@ -14891,23 +15194,6 @@ function computeInfra(parsed, params = {}) {
|
|
|
14891
15194
|
const defaultLatencyMs = parseFloat(parsed.options["default-latency-ms"] ?? "") || 0;
|
|
14892
15195
|
const defaultUptime = parseFloat(parsed.options["default-uptime"] ?? "") || 100;
|
|
14893
15196
|
let effectiveNodes = parsed.nodes;
|
|
14894
|
-
if (params.scenario) {
|
|
14895
|
-
const overrides = params.scenario.overrides;
|
|
14896
|
-
effectiveNodes = parsed.nodes.map((node) => {
|
|
14897
|
-
const nodeOverrides = overrides[node.id];
|
|
14898
|
-
if (!nodeOverrides) return node;
|
|
14899
|
-
const props = node.properties.map((p) => {
|
|
14900
|
-
const ov = nodeOverrides[p.key];
|
|
14901
|
-
return ov != null ? { ...p, value: ov } : p;
|
|
14902
|
-
});
|
|
14903
|
-
for (const [key, val] of Object.entries(nodeOverrides)) {
|
|
14904
|
-
if (!props.some((p) => p.key === key)) {
|
|
14905
|
-
props.push({ key, value: val, lineNumber: node.lineNumber });
|
|
14906
|
-
}
|
|
14907
|
-
}
|
|
14908
|
-
return { ...node, properties: props };
|
|
14909
|
-
});
|
|
14910
|
-
}
|
|
14911
15197
|
if (params.propertyOverrides) {
|
|
14912
15198
|
const propOv = params.propertyOverrides;
|
|
14913
15199
|
effectiveNodes = effectiveNodes.map((node) => {
|
|
@@ -15344,6 +15630,7 @@ var init_compute = __esm({
|
|
|
15344
15630
|
// src/infra/layout.ts
|
|
15345
15631
|
var layout_exports8 = {};
|
|
15346
15632
|
__export(layout_exports8, {
|
|
15633
|
+
fixEdgeWaypoints: () => fixEdgeWaypoints,
|
|
15347
15634
|
layoutInfra: () => layoutInfra,
|
|
15348
15635
|
separateGroups: () => separateGroups
|
|
15349
15636
|
});
|
|
@@ -15527,6 +15814,8 @@ function formatUptime(fraction) {
|
|
|
15527
15814
|
return `${pct.toFixed(1)}%`;
|
|
15528
15815
|
}
|
|
15529
15816
|
function separateGroups(groups, nodes, isLR, maxIterations = 20) {
|
|
15817
|
+
const groupDeltas = /* @__PURE__ */ new Map();
|
|
15818
|
+
let converged = false;
|
|
15530
15819
|
for (let iter = 0; iter < maxIterations; iter++) {
|
|
15531
15820
|
let anyOverlap = false;
|
|
15532
15821
|
for (let i = 0; i < groups.length; i++) {
|
|
@@ -15544,6 +15833,9 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
|
|
|
15544
15833
|
const groupToShift = aCenter <= bCenter ? gb : ga;
|
|
15545
15834
|
if (isLR) groupToShift.y += shift;
|
|
15546
15835
|
else groupToShift.x += shift;
|
|
15836
|
+
const prev = groupDeltas.get(groupToShift.id) ?? { dx: 0, dy: 0 };
|
|
15837
|
+
if (isLR) groupDeltas.set(groupToShift.id, { dx: prev.dx, dy: prev.dy + shift });
|
|
15838
|
+
else groupDeltas.set(groupToShift.id, { dx: prev.dx + shift, dy: prev.dy });
|
|
15547
15839
|
for (const node of nodes) {
|
|
15548
15840
|
if (node.groupId === groupToShift.id) {
|
|
15549
15841
|
if (isLR) node.y += shift;
|
|
@@ -15552,19 +15844,48 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
|
|
|
15552
15844
|
}
|
|
15553
15845
|
}
|
|
15554
15846
|
}
|
|
15555
|
-
if (!anyOverlap)
|
|
15847
|
+
if (!anyOverlap) {
|
|
15848
|
+
converged = true;
|
|
15849
|
+
break;
|
|
15850
|
+
}
|
|
15851
|
+
}
|
|
15852
|
+
if (!converged && maxIterations > 0) {
|
|
15853
|
+
console.warn(`separateGroups: hit maxIterations (${maxIterations}) without fully resolving all group overlaps`);
|
|
15854
|
+
}
|
|
15855
|
+
return groupDeltas;
|
|
15856
|
+
}
|
|
15857
|
+
function fixEdgeWaypoints(edges, nodes, groupDeltas) {
|
|
15858
|
+
if (groupDeltas.size === 0) return;
|
|
15859
|
+
const nodeToGroup = /* @__PURE__ */ new Map();
|
|
15860
|
+
for (const node of nodes) nodeToGroup.set(node.id, node.groupId);
|
|
15861
|
+
for (const edge of edges) {
|
|
15862
|
+
const srcGroup = nodeToGroup.get(edge.sourceId) ?? null;
|
|
15863
|
+
const tgtGroup = nodeToGroup.get(edge.targetId) ?? null;
|
|
15864
|
+
const srcDelta = srcGroup ? groupDeltas.get(srcGroup) : void 0;
|
|
15865
|
+
const tgtDelta = tgtGroup ? groupDeltas.get(tgtGroup) : void 0;
|
|
15866
|
+
if (!srcDelta && !tgtDelta) continue;
|
|
15867
|
+
if (srcDelta && tgtDelta && srcGroup !== tgtGroup) {
|
|
15868
|
+
edge.points = [];
|
|
15869
|
+
continue;
|
|
15870
|
+
}
|
|
15871
|
+
const delta = srcDelta ?? tgtDelta;
|
|
15872
|
+
for (const pt of edge.points) {
|
|
15873
|
+
pt.x += delta.dx;
|
|
15874
|
+
pt.y += delta.dy;
|
|
15875
|
+
}
|
|
15556
15876
|
}
|
|
15557
15877
|
}
|
|
15558
15878
|
function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
15559
15879
|
if (computed.nodes.length === 0) {
|
|
15560
|
-
return { nodes: [], edges: [], groups: [], options: {}, width: 0, height: 0 };
|
|
15880
|
+
return { nodes: [], edges: [], groups: [], options: {}, direction: computed.direction, width: 0, height: 0 };
|
|
15561
15881
|
}
|
|
15882
|
+
const isLR = computed.direction !== "TB";
|
|
15562
15883
|
const g = new import_dagre7.default.graphlib.Graph();
|
|
15563
15884
|
g.setGraph({
|
|
15564
15885
|
rankdir: computed.direction === "TB" ? "TB" : "LR",
|
|
15565
|
-
nodesep:
|
|
15566
|
-
ranksep:
|
|
15567
|
-
edgesep:
|
|
15886
|
+
nodesep: isLR ? 70 : 60,
|
|
15887
|
+
ranksep: isLR ? 150 : 120,
|
|
15888
|
+
edgesep: 30
|
|
15568
15889
|
});
|
|
15569
15890
|
g.setDefaultEdgeLabel(() => ({}));
|
|
15570
15891
|
const groupedNodeIds = /* @__PURE__ */ new Set();
|
|
@@ -15572,7 +15893,6 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
15572
15893
|
if (node.groupId) groupedNodeIds.add(node.id);
|
|
15573
15894
|
}
|
|
15574
15895
|
const GROUP_INFLATE = GROUP_PADDING3 * 2 + GROUP_HEADER_HEIGHT;
|
|
15575
|
-
const isLR = computed.direction !== "TB";
|
|
15576
15896
|
const widthMap = /* @__PURE__ */ new Map();
|
|
15577
15897
|
const heightMap = /* @__PURE__ */ new Map();
|
|
15578
15898
|
for (const node of computed.nodes) {
|
|
@@ -15707,7 +16027,8 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
15707
16027
|
lineNumber: group.lineNumber
|
|
15708
16028
|
};
|
|
15709
16029
|
});
|
|
15710
|
-
separateGroups(layoutGroups, layoutNodes, isLR);
|
|
16030
|
+
const groupDeltas = separateGroups(layoutGroups, layoutNodes, isLR);
|
|
16031
|
+
fixEdgeWaypoints(layoutEdges, layoutNodes, groupDeltas);
|
|
15711
16032
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
15712
16033
|
for (const node of layoutNodes) {
|
|
15713
16034
|
const left = node.x - node.width / 2;
|
|
@@ -15765,6 +16086,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
15765
16086
|
edges: layoutEdges,
|
|
15766
16087
|
groups: layoutGroups,
|
|
15767
16088
|
options: computed.options,
|
|
16089
|
+
direction: computed.direction,
|
|
15768
16090
|
width: totalWidth,
|
|
15769
16091
|
height: totalHeight
|
|
15770
16092
|
};
|
|
@@ -15807,23 +16129,23 @@ var init_layout8 = __esm({
|
|
|
15807
16129
|
]);
|
|
15808
16130
|
DISPLAY_NAMES = {
|
|
15809
16131
|
"cache-hit": "cache hit",
|
|
15810
|
-
"firewall-block": "
|
|
16132
|
+
"firewall-block": "firewall block",
|
|
15811
16133
|
"ratelimit-rps": "rate limit RPS",
|
|
15812
16134
|
"latency-ms": "latency",
|
|
15813
16135
|
"uptime": "uptime",
|
|
15814
16136
|
"instances": "instances",
|
|
15815
16137
|
"max-rps": "max RPS",
|
|
15816
|
-
"cb-error-threshold": "CB error",
|
|
15817
|
-
"cb-latency-threshold-ms": "CB latency",
|
|
16138
|
+
"cb-error-threshold": "CB error threshold",
|
|
16139
|
+
"cb-latency-threshold-ms": "CB latency threshold",
|
|
15818
16140
|
"concurrency": "concurrency",
|
|
15819
16141
|
"duration-ms": "duration",
|
|
15820
16142
|
"cold-start-ms": "cold start",
|
|
15821
16143
|
"buffer": "buffer",
|
|
15822
|
-
"drain-rate": "drain",
|
|
16144
|
+
"drain-rate": "drain rate",
|
|
15823
16145
|
"retention-hours": "retention",
|
|
15824
16146
|
"partitions": "partitions"
|
|
15825
16147
|
};
|
|
15826
|
-
GROUP_GAP =
|
|
16148
|
+
GROUP_GAP = GROUP_PADDING3 * 2 + GROUP_HEADER_HEIGHT;
|
|
15827
16149
|
}
|
|
15828
16150
|
});
|
|
15829
16151
|
|
|
@@ -15896,6 +16218,236 @@ function resolveNodeSlo(node, diagramOptions) {
|
|
|
15896
16218
|
if (availThreshold == null && latencyP90 == null) return null;
|
|
15897
16219
|
return { availThreshold, latencyP90, warningMargin };
|
|
15898
16220
|
}
|
|
16221
|
+
function buildPathD(pts, direction) {
|
|
16222
|
+
const gen = d3Shape7.line().x((d) => d.x).y((d) => d.y);
|
|
16223
|
+
if (pts.length <= 2) {
|
|
16224
|
+
gen.curve(direction === "TB" ? d3Shape7.curveBumpY : d3Shape7.curveBumpX);
|
|
16225
|
+
} else {
|
|
16226
|
+
gen.curve(d3Shape7.curveCatmullRom.alpha(0.5));
|
|
16227
|
+
}
|
|
16228
|
+
return gen(pts) ?? "";
|
|
16229
|
+
}
|
|
16230
|
+
function computePortPts(edges, nodeMap, direction) {
|
|
16231
|
+
const srcPts = /* @__PURE__ */ new Map();
|
|
16232
|
+
const tgtPts = /* @__PURE__ */ new Map();
|
|
16233
|
+
const PAD = 0.1;
|
|
16234
|
+
const activeEdges = edges.filter((e) => e.points.length > 0);
|
|
16235
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
16236
|
+
for (const e of activeEdges) {
|
|
16237
|
+
if (!bySource.has(e.sourceId)) bySource.set(e.sourceId, []);
|
|
16238
|
+
bySource.get(e.sourceId).push(e);
|
|
16239
|
+
}
|
|
16240
|
+
for (const [sourceId, es] of bySource) {
|
|
16241
|
+
if (es.length < 2) continue;
|
|
16242
|
+
const source = nodeMap.get(sourceId);
|
|
16243
|
+
if (!source) continue;
|
|
16244
|
+
const sorted = es.map((e) => ({ e, t: nodeMap.get(e.targetId) })).filter((x) => x.t != null).sort((a, b) => direction === "LR" ? a.t.y - b.t.y : a.t.x - b.t.x);
|
|
16245
|
+
const n = sorted.length;
|
|
16246
|
+
for (let i = 0; i < n; i++) {
|
|
16247
|
+
const frac = n === 1 ? 0.5 : PAD + (1 - 2 * PAD) * i / (n - 1);
|
|
16248
|
+
const { e, t } = sorted[i];
|
|
16249
|
+
const isBackward = direction === "LR" ? t.x < source.x : t.y < source.y;
|
|
16250
|
+
if (direction === "LR") {
|
|
16251
|
+
srcPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16252
|
+
x: isBackward ? source.x - source.width / 2 : source.x + source.width / 2,
|
|
16253
|
+
y: source.y - source.height / 2 + frac * source.height
|
|
16254
|
+
});
|
|
16255
|
+
} else {
|
|
16256
|
+
srcPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16257
|
+
x: source.x - source.width / 2 + frac * source.width,
|
|
16258
|
+
y: isBackward ? source.y - source.height / 2 : source.y + source.height / 2
|
|
16259
|
+
});
|
|
16260
|
+
}
|
|
16261
|
+
}
|
|
16262
|
+
}
|
|
16263
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
16264
|
+
for (const e of activeEdges) {
|
|
16265
|
+
if (!byTarget.has(e.targetId)) byTarget.set(e.targetId, []);
|
|
16266
|
+
byTarget.get(e.targetId).push(e);
|
|
16267
|
+
}
|
|
16268
|
+
for (const [targetId, es] of byTarget) {
|
|
16269
|
+
if (es.length < 2) continue;
|
|
16270
|
+
const target = nodeMap.get(targetId);
|
|
16271
|
+
if (!target) continue;
|
|
16272
|
+
const sorted = es.map((e) => ({ e, s: nodeMap.get(e.sourceId) })).filter((x) => x.s != null).sort((a, b) => direction === "LR" ? a.s.y - b.s.y : a.s.x - b.s.x);
|
|
16273
|
+
const n = sorted.length;
|
|
16274
|
+
for (let i = 0; i < n; i++) {
|
|
16275
|
+
const frac = n === 1 ? 0.5 : PAD + (1 - 2 * PAD) * i / (n - 1);
|
|
16276
|
+
const { e, s } = sorted[i];
|
|
16277
|
+
const isBackward = direction === "LR" ? target.x < s.x : target.y < s.y;
|
|
16278
|
+
if (direction === "LR") {
|
|
16279
|
+
tgtPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16280
|
+
x: isBackward ? target.x + target.width / 2 : target.x - target.width / 2,
|
|
16281
|
+
y: target.y - target.height / 2 + frac * target.height
|
|
16282
|
+
});
|
|
16283
|
+
} else {
|
|
16284
|
+
tgtPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16285
|
+
x: target.x - target.width / 2 + frac * target.width,
|
|
16286
|
+
y: isBackward ? target.y + target.height / 2 : target.y - target.height / 2
|
|
16287
|
+
});
|
|
16288
|
+
}
|
|
16289
|
+
}
|
|
16290
|
+
}
|
|
16291
|
+
return { srcPts, tgtPts };
|
|
16292
|
+
}
|
|
16293
|
+
function findRoutingLane(blocking, targetY, margin) {
|
|
16294
|
+
const MERGE_SLOP = 4;
|
|
16295
|
+
const sorted = [...blocking].sort((a, b) => a.y + a.height / 2 - (b.y + b.height / 2));
|
|
16296
|
+
const merged = [];
|
|
16297
|
+
for (const r of sorted) {
|
|
16298
|
+
const lo = r.y - MERGE_SLOP;
|
|
16299
|
+
const hi = r.y + r.height + MERGE_SLOP;
|
|
16300
|
+
if (merged.length && lo <= merged[merged.length - 1][1]) {
|
|
16301
|
+
merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], hi);
|
|
16302
|
+
} else {
|
|
16303
|
+
merged.push([lo, hi]);
|
|
16304
|
+
}
|
|
16305
|
+
}
|
|
16306
|
+
if (merged.length === 0) return targetY;
|
|
16307
|
+
const MIN_GAP = 10;
|
|
16308
|
+
const candidates = [
|
|
16309
|
+
merged[0][0] - margin,
|
|
16310
|
+
// above all blocking rects
|
|
16311
|
+
merged[merged.length - 1][1] + margin
|
|
16312
|
+
// below all blocking rects
|
|
16313
|
+
];
|
|
16314
|
+
for (let i = 0; i < merged.length - 1; i++) {
|
|
16315
|
+
const gapLo = merged[i][1];
|
|
16316
|
+
const gapHi = merged[i + 1][0];
|
|
16317
|
+
if (gapHi - gapLo >= MIN_GAP) {
|
|
16318
|
+
candidates.push((gapLo + gapHi) / 2);
|
|
16319
|
+
}
|
|
16320
|
+
}
|
|
16321
|
+
return candidates.reduce(
|
|
16322
|
+
(best, c) => Math.abs(c - targetY) < Math.abs(best - targetY) ? c : best,
|
|
16323
|
+
candidates[0]
|
|
16324
|
+
);
|
|
16325
|
+
}
|
|
16326
|
+
function segmentIntersectsRect(p1, p2, rect) {
|
|
16327
|
+
const { x: rx, y: ry, width: rw, height: rh } = rect;
|
|
16328
|
+
const rr = rx + rw;
|
|
16329
|
+
const rb = ry + rh;
|
|
16330
|
+
const inRect = (p) => p.x >= rx && p.x <= rr && p.y >= ry && p.y <= rb;
|
|
16331
|
+
if (inRect(p1) || inRect(p2)) return true;
|
|
16332
|
+
if (Math.max(p1.x, p2.x) < rx || Math.min(p1.x, p2.x) > rr) return false;
|
|
16333
|
+
if (Math.max(p1.y, p2.y) < ry || Math.min(p1.y, p2.y) > rb) return false;
|
|
16334
|
+
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
16335
|
+
const crosses = (a, b) => {
|
|
16336
|
+
const d1 = cross(a, b, p1);
|
|
16337
|
+
const d2 = cross(a, b, p2);
|
|
16338
|
+
const d3 = cross(p1, p2, a);
|
|
16339
|
+
const d4 = cross(p1, p2, b);
|
|
16340
|
+
return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0);
|
|
16341
|
+
};
|
|
16342
|
+
const tl = { x: rx, y: ry };
|
|
16343
|
+
const tr = { x: rr, y: ry };
|
|
16344
|
+
const br = { x: rr, y: rb };
|
|
16345
|
+
const bl = { x: rx, y: rb };
|
|
16346
|
+
return crosses(tl, tr) || crosses(tr, br) || crosses(br, bl) || crosses(bl, tl);
|
|
16347
|
+
}
|
|
16348
|
+
function curveIntersectsRect(sc, tc, rect, direction) {
|
|
16349
|
+
if (direction === "LR") {
|
|
16350
|
+
const midX = (sc.x + tc.x) / 2;
|
|
16351
|
+
const m1 = { x: midX, y: sc.y };
|
|
16352
|
+
const m2 = { x: midX, y: tc.y };
|
|
16353
|
+
return segmentIntersectsRect(sc, m1, rect) || segmentIntersectsRect(m1, m2, rect) || segmentIntersectsRect(m2, tc, rect);
|
|
16354
|
+
} else {
|
|
16355
|
+
const midY = (sc.y + tc.y) / 2;
|
|
16356
|
+
const m1 = { x: sc.x, y: midY };
|
|
16357
|
+
const m2 = { x: tc.x, y: midY };
|
|
16358
|
+
return segmentIntersectsRect(sc, m1, rect) || segmentIntersectsRect(m1, m2, rect) || segmentIntersectsRect(m2, tc, rect);
|
|
16359
|
+
}
|
|
16360
|
+
}
|
|
16361
|
+
function edgeWaypoints(source, target, groups, nodes, direction, margin = 30, srcExitPt, tgtEnterPt) {
|
|
16362
|
+
const sc = { x: source.x, y: source.y };
|
|
16363
|
+
const tc = { x: target.x, y: target.y };
|
|
16364
|
+
const isBackward = direction === "LR" ? tc.x < sc.x : tc.y < sc.y;
|
|
16365
|
+
if (isBackward) {
|
|
16366
|
+
if (direction === "LR") {
|
|
16367
|
+
const xBandObs = [];
|
|
16368
|
+
for (const g of groups) {
|
|
16369
|
+
if (g.x + g.width < tc.x - margin || g.x > sc.x + margin) continue;
|
|
16370
|
+
xBandObs.push({ x: g.x, y: g.y, width: g.width, height: g.height });
|
|
16371
|
+
}
|
|
16372
|
+
for (const n of nodes) {
|
|
16373
|
+
if (n.id === source.id || n.id === target.id) continue;
|
|
16374
|
+
const nLeft = n.x - n.width / 2;
|
|
16375
|
+
const nRight = n.x + n.width / 2;
|
|
16376
|
+
if (nRight < tc.x - margin || nLeft > sc.x + margin) continue;
|
|
16377
|
+
xBandObs.push({ x: nLeft, y: n.y - n.height / 2, width: n.width, height: n.height });
|
|
16378
|
+
}
|
|
16379
|
+
const midY = (sc.y + tc.y) / 2;
|
|
16380
|
+
const routeY2 = xBandObs.length > 0 ? findRoutingLane(xBandObs, midY, margin) : midY;
|
|
16381
|
+
const exitBorder = srcExitPt ?? nodeBorderPoint(source, { x: sc.x, y: routeY2 });
|
|
16382
|
+
const exitPt2 = { x: exitBorder.x, y: routeY2 };
|
|
16383
|
+
const enterPt2 = { x: tc.x, y: routeY2 };
|
|
16384
|
+
const tp2 = tgtEnterPt ?? nodeBorderPoint(target, enterPt2);
|
|
16385
|
+
return srcExitPt ? [srcExitPt, exitPt2, enterPt2, tp2] : [exitBorder, exitPt2, enterPt2, tp2];
|
|
16386
|
+
} else {
|
|
16387
|
+
const yBandObs = [];
|
|
16388
|
+
for (const g of groups) {
|
|
16389
|
+
if (g.y + g.height < tc.y - margin || g.y > sc.y + margin) continue;
|
|
16390
|
+
yBandObs.push({ x: g.x, y: g.y, width: g.width, height: g.height });
|
|
16391
|
+
}
|
|
16392
|
+
for (const n of nodes) {
|
|
16393
|
+
if (n.id === source.id || n.id === target.id) continue;
|
|
16394
|
+
const nTop = n.y - n.height / 2;
|
|
16395
|
+
const nBot = n.y + n.height / 2;
|
|
16396
|
+
if (nBot < tc.y - margin || nTop > sc.y + margin) continue;
|
|
16397
|
+
yBandObs.push({ x: n.x - n.width / 2, y: nTop, width: n.width, height: n.height });
|
|
16398
|
+
}
|
|
16399
|
+
const rotated = yBandObs.map((r) => ({ x: r.y, y: r.x, width: r.height, height: r.width }));
|
|
16400
|
+
const midX = (sc.x + tc.x) / 2;
|
|
16401
|
+
const routeX = rotated.length > 0 ? findRoutingLane(rotated, midX, margin) : midX;
|
|
16402
|
+
const exitPt2 = srcExitPt ?? { x: routeX, y: sc.y };
|
|
16403
|
+
const enterPt2 = { x: routeX, y: tc.y };
|
|
16404
|
+
return [
|
|
16405
|
+
srcExitPt ?? nodeBorderPoint(source, exitPt2),
|
|
16406
|
+
exitPt2,
|
|
16407
|
+
enterPt2,
|
|
16408
|
+
tgtEnterPt ?? nodeBorderPoint(target, enterPt2)
|
|
16409
|
+
];
|
|
16410
|
+
}
|
|
16411
|
+
}
|
|
16412
|
+
const blocking = [];
|
|
16413
|
+
const blockingGroupIds = /* @__PURE__ */ new Set();
|
|
16414
|
+
const pathSrc = srcExitPt ?? sc;
|
|
16415
|
+
const pathTgt = tgtEnterPt ?? tc;
|
|
16416
|
+
for (const g of groups) {
|
|
16417
|
+
if (g.id === source.groupId || g.id === target.groupId) continue;
|
|
16418
|
+
const gRect = { x: g.x, y: g.y, width: g.width, height: g.height };
|
|
16419
|
+
if (curveIntersectsRect(pathSrc, pathTgt, gRect, direction)) {
|
|
16420
|
+
blocking.push(gRect);
|
|
16421
|
+
blockingGroupIds.add(g.id);
|
|
16422
|
+
}
|
|
16423
|
+
}
|
|
16424
|
+
for (const n of nodes) {
|
|
16425
|
+
if (n.id === source.id || n.id === target.id) continue;
|
|
16426
|
+
if (n.groupId && (n.groupId === source.groupId || n.groupId === target.groupId)) continue;
|
|
16427
|
+
if (n.groupId && blockingGroupIds.has(n.groupId)) continue;
|
|
16428
|
+
const nodeRect = { x: n.x - n.width / 2, y: n.y - n.height / 2, width: n.width, height: n.height };
|
|
16429
|
+
if (curveIntersectsRect(pathSrc, pathTgt, nodeRect, direction)) {
|
|
16430
|
+
blocking.push(nodeRect);
|
|
16431
|
+
}
|
|
16432
|
+
}
|
|
16433
|
+
if (blocking.length === 0) {
|
|
16434
|
+
const sp = srcExitPt ?? nodeBorderPoint(source, tc);
|
|
16435
|
+
const tp2 = tgtEnterPt ?? nodeBorderPoint(target, sp);
|
|
16436
|
+
return [sp, tp2];
|
|
16437
|
+
}
|
|
16438
|
+
const obsLeft = Math.min(...blocking.map((o) => o.x));
|
|
16439
|
+
const obsRight = Math.max(...blocking.map((o) => o.x + o.width));
|
|
16440
|
+
const routeY = findRoutingLane(blocking, tc.y, margin);
|
|
16441
|
+
const exitX = direction === "LR" ? Math.max(sc.x, obsLeft - margin) : obsLeft - margin;
|
|
16442
|
+
const enterX = direction === "LR" ? Math.min(tc.x, obsRight + margin) : obsRight + margin;
|
|
16443
|
+
const exitPt = { x: exitX, y: routeY };
|
|
16444
|
+
const enterPt = { x: enterX, y: routeY };
|
|
16445
|
+
const tp = tgtEnterPt ?? nodeBorderPoint(target, enterPt);
|
|
16446
|
+
if (srcExitPt) {
|
|
16447
|
+
return [srcExitPt, exitPt, enterPt, tp];
|
|
16448
|
+
}
|
|
16449
|
+
return [nodeBorderPoint(source, exitPt), exitPt, enterPt, tp];
|
|
16450
|
+
}
|
|
15899
16451
|
function nodeBorderPoint(node, target) {
|
|
15900
16452
|
const hw = node.width / 2;
|
|
15901
16453
|
const hh = node.height / 2;
|
|
@@ -16179,33 +16731,29 @@ function renderGroups(svg, groups, palette, isDark) {
|
|
|
16179
16731
|
}
|
|
16180
16732
|
}
|
|
16181
16733
|
}
|
|
16182
|
-
function renderEdgePaths(svg, edges, nodes, palette, isDark, animate) {
|
|
16734
|
+
function renderEdgePaths(svg, edges, nodes, groups, palette, isDark, animate, direction) {
|
|
16183
16735
|
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
16184
16736
|
const maxRps = Math.max(...edges.map((e) => e.computedRps), 1);
|
|
16737
|
+
const { srcPts, tgtPts } = computePortPts(edges, nodeMap, direction);
|
|
16185
16738
|
for (const edge of edges) {
|
|
16186
16739
|
if (edge.points.length === 0) continue;
|
|
16187
16740
|
const targetNode = nodeMap.get(edge.targetId);
|
|
16188
16741
|
const sourceNode = nodeMap.get(edge.sourceId);
|
|
16189
16742
|
const color = edgeColor(edge, palette);
|
|
16190
16743
|
const strokeW = edgeWidth();
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
if (targetNode && pts.length > 0) {
|
|
16205
|
-
const bp = nodeBorderPoint(targetNode, pts[pts.length - 1]);
|
|
16206
|
-
pts = [...pts, bp];
|
|
16207
|
-
}
|
|
16208
|
-
const pathD = lineGenerator7(pts) ?? "";
|
|
16744
|
+
if (!sourceNode || !targetNode) continue;
|
|
16745
|
+
const key = `${edge.sourceId}:${edge.targetId}`;
|
|
16746
|
+
const pts = edgeWaypoints(
|
|
16747
|
+
sourceNode,
|
|
16748
|
+
targetNode,
|
|
16749
|
+
groups,
|
|
16750
|
+
nodes,
|
|
16751
|
+
direction,
|
|
16752
|
+
30,
|
|
16753
|
+
srcPts.get(key),
|
|
16754
|
+
tgtPts.get(key)
|
|
16755
|
+
);
|
|
16756
|
+
const pathD = buildPathD(pts, direction);
|
|
16209
16757
|
const edgeG = svg.append("g").attr("class", "infra-edge").attr("data-line-number", edge.lineNumber);
|
|
16210
16758
|
edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", color).attr("stroke-width", strokeW);
|
|
16211
16759
|
if (animate && edge.computedRps > 0) {
|
|
@@ -16220,19 +16768,34 @@ function renderEdgePaths(svg, edges, nodes, palette, isDark, animate) {
|
|
|
16220
16768
|
}
|
|
16221
16769
|
}
|
|
16222
16770
|
}
|
|
16223
|
-
function renderEdgeLabels(svg, edges, palette, isDark, animate) {
|
|
16771
|
+
function renderEdgeLabels(svg, edges, nodes, groups, palette, isDark, animate, direction) {
|
|
16772
|
+
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
16773
|
+
const { srcPts, tgtPts } = computePortPts(edges, nodeMap, direction);
|
|
16224
16774
|
for (const edge of edges) {
|
|
16225
16775
|
if (edge.points.length === 0) continue;
|
|
16226
16776
|
if (!edge.label) continue;
|
|
16227
|
-
const
|
|
16228
|
-
const
|
|
16777
|
+
const sourceNode = nodeMap.get(edge.sourceId);
|
|
16778
|
+
const targetNode = nodeMap.get(edge.targetId);
|
|
16779
|
+
if (!sourceNode || !targetNode) continue;
|
|
16780
|
+
const key = `${edge.sourceId}:${edge.targetId}`;
|
|
16781
|
+
const wps = edgeWaypoints(
|
|
16782
|
+
sourceNode,
|
|
16783
|
+
targetNode,
|
|
16784
|
+
groups,
|
|
16785
|
+
nodes,
|
|
16786
|
+
direction,
|
|
16787
|
+
30,
|
|
16788
|
+
srcPts.get(key),
|
|
16789
|
+
tgtPts.get(key)
|
|
16790
|
+
);
|
|
16791
|
+
const midPt = wps[Math.floor(wps.length / 2)];
|
|
16229
16792
|
const labelText = edge.label;
|
|
16230
16793
|
const g = svg.append("g").attr("class", animate ? "infra-edge-label" : "");
|
|
16231
16794
|
const textWidth = labelText.length * 6.5 + 8;
|
|
16232
16795
|
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);
|
|
16233
16796
|
g.append("text").attr("x", midPt.x).attr("y", midPt.y + 4).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("fill", palette.textMuted).text(labelText);
|
|
16234
16797
|
if (animate) {
|
|
16235
|
-
const pathD =
|
|
16798
|
+
const pathD = buildPathD(wps, direction);
|
|
16236
16799
|
g.insert("path", ":first-child").attr("d", pathD).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", 20);
|
|
16237
16800
|
}
|
|
16238
16801
|
}
|
|
@@ -16504,16 +17067,16 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
16504
17067
|
color: r.color,
|
|
16505
17068
|
key: r.name.toLowerCase().replace(/\s+/g, "-")
|
|
16506
17069
|
}));
|
|
16507
|
-
const pillWidth = "Capabilities".length *
|
|
17070
|
+
const pillWidth = "Capabilities".length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
16508
17071
|
let entriesWidth = 0;
|
|
16509
17072
|
for (const e of entries) {
|
|
16510
|
-
entriesWidth +=
|
|
17073
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + e.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
16511
17074
|
}
|
|
16512
17075
|
groups.push({
|
|
16513
17076
|
name: "Capabilities",
|
|
16514
17077
|
type: "role",
|
|
16515
17078
|
entries,
|
|
16516
|
-
width:
|
|
17079
|
+
width: LEGEND_CAPSULE_PAD * 2 + pillWidth + 4 + entriesWidth,
|
|
16517
17080
|
minifiedWidth: pillWidth
|
|
16518
17081
|
});
|
|
16519
17082
|
}
|
|
@@ -16529,123 +17092,72 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
16529
17092
|
}
|
|
16530
17093
|
}
|
|
16531
17094
|
if (entries.length === 0) continue;
|
|
16532
|
-
const pillWidth = tg.name.length *
|
|
17095
|
+
const pillWidth = tg.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
16533
17096
|
let entriesWidth = 0;
|
|
16534
17097
|
for (const e of entries) {
|
|
16535
|
-
entriesWidth +=
|
|
17098
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + e.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
16536
17099
|
}
|
|
16537
17100
|
groups.push({
|
|
16538
17101
|
name: tg.name,
|
|
16539
17102
|
type: "tag",
|
|
16540
17103
|
tagKey: (tg.alias ?? tg.name).toLowerCase(),
|
|
16541
17104
|
entries,
|
|
16542
|
-
width:
|
|
17105
|
+
width: LEGEND_CAPSULE_PAD * 2 + pillWidth + 4 + entriesWidth,
|
|
16543
17106
|
minifiedWidth: pillWidth
|
|
16544
17107
|
});
|
|
16545
17108
|
}
|
|
16546
17109
|
return groups;
|
|
16547
17110
|
}
|
|
16548
|
-
function
|
|
16549
|
-
if (
|
|
16550
|
-
const pillWidth = "Playback".length * LEGEND_PILL_FONT_W7 + LEGEND_PILL_PAD7;
|
|
16551
|
-
if (!playback.expanded) return pillWidth;
|
|
16552
|
-
let entriesW = 8;
|
|
16553
|
-
entriesW += LEGEND_PILL_FONT_SIZE5 * 0.8 + 6;
|
|
16554
|
-
for (const s of playback.speedOptions) {
|
|
16555
|
-
entriesW += `${s}x`.length * LEGEND_ENTRY_FONT_W7 + SPEED_BADGE_H_PAD * 2 + SPEED_BADGE_GAP;
|
|
16556
|
-
}
|
|
16557
|
-
return LEGEND_CAPSULE_PAD7 * 2 + pillWidth + entriesW;
|
|
16558
|
-
}
|
|
16559
|
-
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback) {
|
|
16560
|
-
if (legendGroups.length === 0 && !playback) return;
|
|
17111
|
+
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup) {
|
|
17112
|
+
if (legendGroups.length === 0) return;
|
|
16561
17113
|
const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
|
|
17114
|
+
if (activeGroup) {
|
|
17115
|
+
legendG.attr("data-legend-active", activeGroup.toLowerCase());
|
|
17116
|
+
}
|
|
16562
17117
|
const effectiveW = (g) => activeGroup != null && g.name.toLowerCase() === activeGroup.toLowerCase() ? g.width : g.minifiedWidth;
|
|
16563
|
-
const
|
|
16564
|
-
const trailingGaps = legendGroups.length > 0 && playbackW > 0 ? LEGEND_GROUP_GAP5 : 0;
|
|
16565
|
-
const totalLegendW = legendGroups.reduce((s, g) => s + effectiveW(g), 0) + (legendGroups.length - 1) * LEGEND_GROUP_GAP5 + trailingGaps + playbackW;
|
|
17118
|
+
const totalLegendW = legendGroups.reduce((s, g) => s + effectiveW(g), 0) + (legendGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
16566
17119
|
let cursorX = (totalWidth - totalLegendW) / 2;
|
|
16567
17120
|
for (const group of legendGroups) {
|
|
16568
17121
|
const isActive = activeGroup != null && group.name.toLowerCase() === activeGroup.toLowerCase();
|
|
16569
17122
|
const groupBg = isDark ? mix(palette.bg, palette.text, 85) : mix(palette.bg, palette.text, 92);
|
|
16570
17123
|
const pillLabel = group.name;
|
|
16571
|
-
const pillWidth = pillLabel.length *
|
|
16572
|
-
const gEl = legendG.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "infra-legend-group").attr("data-legend-group", group.name.toLowerCase()).
|
|
17124
|
+
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
17125
|
+
const gEl = legendG.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "infra-legend-group").attr("data-legend-group", group.name.toLowerCase()).style("cursor", "pointer");
|
|
16573
17126
|
if (isActive) {
|
|
16574
|
-
gEl.append("rect").attr("width", group.width).attr("height",
|
|
17127
|
+
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
16575
17128
|
}
|
|
16576
|
-
const pillXOff = isActive ?
|
|
16577
|
-
const pillYOff = isActive ?
|
|
16578
|
-
const pillH =
|
|
17129
|
+
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
17130
|
+
const pillYOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
17131
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
16579
17132
|
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isActive ? palette.bg : groupBg);
|
|
16580
17133
|
if (isActive) {
|
|
16581
|
-
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke",
|
|
17134
|
+
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
16582
17135
|
}
|
|
16583
|
-
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y",
|
|
17136
|
+
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-family", FONT_FAMILY).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(pillLabel);
|
|
16584
17137
|
if (isActive) {
|
|
16585
17138
|
let entryX = pillXOff + pillWidth + 4;
|
|
16586
17139
|
for (const entry of group.entries) {
|
|
16587
|
-
const entryG = gEl.append("g").attr("class", "infra-legend-entry").attr("data-legend-entry", entry.key).attr("data-legend-type", group.type).attr("data-legend-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
const textX = entryX + LEGEND_DOT_R8 * 2 + LEGEND_ENTRY_DOT_GAP7;
|
|
16593
|
-
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT8 / 2 + LEGEND_ENTRY_FONT_SIZE6 / 2 - 1).attr("font-family", FONT_FAMILY).attr("font-size", LEGEND_ENTRY_FONT_SIZE6).attr("fill", palette.textMuted).text(entry.value);
|
|
16594
|
-
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W7 + LEGEND_ENTRY_TRAIL7;
|
|
17140
|
+
const entryG = gEl.append("g").attr("class", "infra-legend-entry").attr("data-legend-entry", entry.key.toLowerCase()).attr("data-legend-color", entry.color).attr("data-legend-type", group.type).attr("data-legend-tag-group", group.type === "tag" ? group.tagKey ?? "" : null).style("cursor", "pointer");
|
|
17141
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
17142
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
17143
|
+
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-family", FONT_FAMILY).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).text(entry.value);
|
|
17144
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
16595
17145
|
}
|
|
16596
17146
|
}
|
|
16597
|
-
cursorX += effectiveW(group) +
|
|
17147
|
+
cursorX += effectiveW(group) + LEGEND_GROUP_GAP;
|
|
16598
17148
|
}
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
const groupBg = isDark ? mix(palette.bg, palette.text, 85) : mix(palette.bg, palette.text, 92);
|
|
16602
|
-
const pillLabel = "Playback";
|
|
16603
|
-
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W7 + LEGEND_PILL_PAD7;
|
|
16604
|
-
const fullW = computePlaybackWidth(playback);
|
|
16605
|
-
const pbG = legendG.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "infra-legend-group infra-playback-pill").style("cursor", "pointer");
|
|
16606
|
-
if (isExpanded) {
|
|
16607
|
-
pbG.append("rect").attr("width", fullW).attr("height", LEGEND_HEIGHT8).attr("rx", LEGEND_HEIGHT8 / 2).attr("fill", groupBg);
|
|
16608
|
-
}
|
|
16609
|
-
const pillXOff = isExpanded ? LEGEND_CAPSULE_PAD7 : 0;
|
|
16610
|
-
const pillYOff = isExpanded ? LEGEND_CAPSULE_PAD7 : 0;
|
|
16611
|
-
const pillH = LEGEND_HEIGHT8 - (isExpanded ? LEGEND_CAPSULE_PAD7 * 2 : 0);
|
|
16612
|
-
pbG.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", isExpanded ? palette.bg : groupBg);
|
|
16613
|
-
if (isExpanded) {
|
|
16614
|
-
pbG.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
16615
|
-
}
|
|
16616
|
-
pbG.append("text").attr("x", pillXOff + pillWidth / 2).attr("y", LEGEND_HEIGHT8 / 2 + LEGEND_PILL_FONT_SIZE5 / 2 - 2).attr("font-family", FONT_FAMILY).attr("font-size", LEGEND_PILL_FONT_SIZE5).attr("font-weight", "500").attr("fill", isExpanded ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(pillLabel);
|
|
16617
|
-
if (isExpanded) {
|
|
16618
|
-
let entryX = pillXOff + pillWidth + 8;
|
|
16619
|
-
const entryY = LEGEND_HEIGHT8 / 2 + LEGEND_ENTRY_FONT_SIZE6 / 2 - 1;
|
|
16620
|
-
const ppLabel = playback.paused ? "\u25B6" : "\u23F8";
|
|
16621
|
-
pbG.append("text").attr("x", entryX).attr("y", entryY).attr("font-family", FONT_FAMILY).attr("font-size", LEGEND_PILL_FONT_SIZE5).attr("fill", palette.textMuted).attr("data-playback-action", "toggle-pause").style("cursor", "pointer").text(ppLabel);
|
|
16622
|
-
entryX += LEGEND_PILL_FONT_SIZE5 * 0.8 + 6;
|
|
16623
|
-
for (const s of playback.speedOptions) {
|
|
16624
|
-
const label = `${s}x`;
|
|
16625
|
-
const isActive = playback.speed === s;
|
|
16626
|
-
const slotW = label.length * LEGEND_ENTRY_FONT_W7 + SPEED_BADGE_H_PAD * 2;
|
|
16627
|
-
const badgeH = LEGEND_ENTRY_FONT_SIZE6 + SPEED_BADGE_V_PAD * 2;
|
|
16628
|
-
const badgeY = (LEGEND_HEIGHT8 - badgeH) / 2;
|
|
16629
|
-
const speedG = pbG.append("g").attr("data-playback-action", "set-speed").attr("data-playback-value", String(s)).style("cursor", "pointer");
|
|
16630
|
-
speedG.append("rect").attr("x", entryX).attr("y", badgeY).attr("width", slotW).attr("height", badgeH).attr("rx", badgeH / 2).attr("fill", isActive ? palette.primary : "transparent");
|
|
16631
|
-
speedG.append("text").attr("x", entryX + slotW / 2).attr("y", entryY).attr("font-family", FONT_FAMILY).attr("font-size", LEGEND_ENTRY_FONT_SIZE6).attr("font-weight", isActive ? "600" : "400").attr("fill", isActive ? palette.bg : palette.textMuted).attr("text-anchor", "middle").text(label);
|
|
16632
|
-
entryX += slotW + SPEED_BADGE_GAP;
|
|
16633
|
-
}
|
|
16634
|
-
}
|
|
16635
|
-
cursorX += fullW + LEGEND_GROUP_GAP5;
|
|
16636
|
-
}
|
|
16637
|
-
}
|
|
16638
|
-
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
17149
|
+
}
|
|
17150
|
+
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, _playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
16639
17151
|
d3Selection9.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
16640
17152
|
const legendGroups = computeInfraLegendGroups(layout.nodes, tagGroups ?? [], palette, layout.edges);
|
|
16641
|
-
const hasLegend = legendGroups.length > 0
|
|
17153
|
+
const hasLegend = legendGroups.length > 0;
|
|
16642
17154
|
const fixedLegend = !exportMode && hasLegend;
|
|
16643
|
-
const legendOffset = hasLegend && !fixedLegend ?
|
|
17155
|
+
const legendOffset = hasLegend && !fixedLegend ? LEGEND_HEIGHT : 0;
|
|
16644
17156
|
const titleOffset = title ? 40 : 0;
|
|
16645
17157
|
const totalWidth = layout.width;
|
|
16646
17158
|
const totalHeight = layout.height + titleOffset + legendOffset;
|
|
16647
17159
|
const shouldAnimate = animate !== false;
|
|
16648
|
-
const rootSvg = d3Selection9.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", "100%").attr("height", fixedLegend ? `calc(100% - ${
|
|
17160
|
+
const rootSvg = d3Selection9.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", "100%").attr("height", fixedLegend ? `calc(100% - ${LEGEND_HEIGHT + LEGEND_FIXED_GAP3}px)` : "100%").attr("viewBox", `0 0 ${totalWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMid meet");
|
|
16649
17161
|
if (shouldAnimate) {
|
|
16650
17162
|
rootSvg.append("style").text(`
|
|
16651
17163
|
@keyframes infra-pulse-warning {
|
|
@@ -16693,7 +17205,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
16693
17205
|
rootSvg.append("text").attr("class", "chart-title").attr("x", totalWidth / 2).attr("y", 28).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", 18).attr("font-weight", "700").attr("fill", palette.text).attr("data-line-number", titleLineNumber != null ? titleLineNumber : "").text(title);
|
|
16694
17206
|
}
|
|
16695
17207
|
renderGroups(svg, layout.groups, palette, isDark);
|
|
16696
|
-
renderEdgePaths(svg, layout.edges, layout.nodes, palette, isDark, shouldAnimate);
|
|
17208
|
+
renderEdgePaths(svg, layout.edges, layout.nodes, layout.groups, palette, isDark, shouldAnimate, layout.direction);
|
|
16697
17209
|
const fanoutSourceIds = collectFanoutSourceIds(layout.edges);
|
|
16698
17210
|
const scaledGroupIds = new Set(
|
|
16699
17211
|
layout.groups.filter((g) => {
|
|
@@ -16705,14 +17217,14 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
16705
17217
|
if (shouldAnimate) {
|
|
16706
17218
|
renderRejectParticles(svg, layout.nodes);
|
|
16707
17219
|
}
|
|
16708
|
-
renderEdgeLabels(svg, layout.edges, palette, isDark, shouldAnimate);
|
|
17220
|
+
renderEdgeLabels(svg, layout.edges, layout.nodes, layout.groups, palette, isDark, shouldAnimate, layout.direction);
|
|
16709
17221
|
if (hasLegend) {
|
|
16710
17222
|
if (fixedLegend) {
|
|
16711
17223
|
const containerWidth = container.clientWidth || totalWidth;
|
|
16712
|
-
const legendSvg = d3Selection9.select(container).append("svg").attr("class", "infra-legend-fixed").attr("width", "100%").attr("height",
|
|
16713
|
-
renderLegend3(legendSvg, legendGroups, containerWidth, LEGEND_FIXED_GAP3 / 2, palette, isDark, activeGroup ?? null
|
|
17224
|
+
const legendSvg = d3Selection9.select(container).append("svg").attr("class", "infra-legend-fixed").attr("width", "100%").attr("height", LEGEND_HEIGHT + LEGEND_FIXED_GAP3).attr("viewBox", `0 0 ${containerWidth} ${LEGEND_HEIGHT + LEGEND_FIXED_GAP3}`).attr("preserveAspectRatio", "xMidYMid meet").style("display", "block");
|
|
17225
|
+
renderLegend3(legendSvg, legendGroups, containerWidth, LEGEND_FIXED_GAP3 / 2, palette, isDark, activeGroup ?? null);
|
|
16714
17226
|
} else {
|
|
16715
|
-
renderLegend3(rootSvg, legendGroups, totalWidth, titleOffset + layout.height + 4, palette, isDark, activeGroup ?? null
|
|
17227
|
+
renderLegend3(rootSvg, legendGroups, totalWidth, titleOffset + layout.height + 4, palette, isDark, activeGroup ?? null);
|
|
16716
17228
|
}
|
|
16717
17229
|
}
|
|
16718
17230
|
}
|
|
@@ -16723,7 +17235,7 @@ function parseAndLayoutInfra(content) {
|
|
|
16723
17235
|
const layout = layoutInfra(computed);
|
|
16724
17236
|
return { parsed, computed, layout };
|
|
16725
17237
|
}
|
|
16726
|
-
var d3Selection9, d3Shape7, NODE_FONT_SIZE4, META_FONT_SIZE4, META_LINE_HEIGHT8, EDGE_LABEL_FONT_SIZE7, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, EDGE_STROKE_WIDTH8, NODE_STROKE_WIDTH8, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, COLLAPSE_BAR_HEIGHT5, COLLAPSE_BAR_INSET2,
|
|
17238
|
+
var d3Selection9, d3Shape7, NODE_FONT_SIZE4, META_FONT_SIZE4, META_LINE_HEIGHT8, EDGE_LABEL_FONT_SIZE7, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, EDGE_STROKE_WIDTH8, NODE_STROKE_WIDTH8, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, COLLAPSE_BAR_HEIGHT5, COLLAPSE_BAR_INSET2, LEGEND_FIXED_GAP3, 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_PARTICLE_R, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
|
|
16727
17239
|
var init_renderer8 = __esm({
|
|
16728
17240
|
"src/infra/renderer.ts"() {
|
|
16729
17241
|
"use strict";
|
|
@@ -16736,6 +17248,7 @@ var init_renderer8 = __esm({
|
|
|
16736
17248
|
init_parser9();
|
|
16737
17249
|
init_compute();
|
|
16738
17250
|
init_layout8();
|
|
17251
|
+
init_legend_constants();
|
|
16739
17252
|
NODE_FONT_SIZE4 = 13;
|
|
16740
17253
|
META_FONT_SIZE4 = 10;
|
|
16741
17254
|
META_LINE_HEIGHT8 = 14;
|
|
@@ -16751,21 +17264,7 @@ var init_renderer8 = __esm({
|
|
|
16751
17264
|
NODE_PAD_BOTTOM2 = 10;
|
|
16752
17265
|
COLLAPSE_BAR_HEIGHT5 = 6;
|
|
16753
17266
|
COLLAPSE_BAR_INSET2 = 0;
|
|
16754
|
-
LEGEND_HEIGHT8 = 28;
|
|
16755
|
-
LEGEND_PILL_PAD7 = 16;
|
|
16756
|
-
LEGEND_PILL_FONT_SIZE5 = 11;
|
|
16757
|
-
LEGEND_PILL_FONT_W7 = LEGEND_PILL_FONT_SIZE5 * 0.6;
|
|
16758
|
-
LEGEND_CAPSULE_PAD7 = 4;
|
|
16759
|
-
LEGEND_DOT_R8 = 4;
|
|
16760
|
-
LEGEND_ENTRY_FONT_SIZE6 = 10;
|
|
16761
|
-
LEGEND_ENTRY_FONT_W7 = LEGEND_ENTRY_FONT_SIZE6 * 0.6;
|
|
16762
|
-
LEGEND_ENTRY_DOT_GAP7 = 4;
|
|
16763
|
-
LEGEND_ENTRY_TRAIL7 = 8;
|
|
16764
|
-
LEGEND_GROUP_GAP5 = 12;
|
|
16765
17267
|
LEGEND_FIXED_GAP3 = 16;
|
|
16766
|
-
SPEED_BADGE_H_PAD = 5;
|
|
16767
|
-
SPEED_BADGE_V_PAD = 3;
|
|
16768
|
-
SPEED_BADGE_GAP = 6;
|
|
16769
17268
|
COLOR_HEALTHY = "#22c55e";
|
|
16770
17269
|
COLOR_WARNING = "#eab308";
|
|
16771
17270
|
COLOR_OVERLOADED = "#ef4444";
|
|
@@ -16782,7 +17281,6 @@ var init_renderer8 = __esm({
|
|
|
16782
17281
|
REJECT_DURATION_MAX = 3;
|
|
16783
17282
|
REJECT_COUNT_MIN = 1;
|
|
16784
17283
|
REJECT_COUNT_MAX = 3;
|
|
16785
|
-
lineGenerator7 = d3Shape7.line().x((d) => d.x).y((d) => d.y).curve(d3Shape7.curveBasis);
|
|
16786
17284
|
PROP_DISPLAY = {
|
|
16787
17285
|
"cache-hit": "cache hit",
|
|
16788
17286
|
"firewall-block": "firewall block",
|
|
@@ -16965,7 +17463,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
|
|
|
16965
17463
|
}
|
|
16966
17464
|
}
|
|
16967
17465
|
} else if (edge.points.length >= 2) {
|
|
16968
|
-
const pathD =
|
|
17466
|
+
const pathD = lineGenerator7(edge.points);
|
|
16969
17467
|
if (pathD) {
|
|
16970
17468
|
edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor2).attr("stroke-width", EDGE_STROKE_WIDTH9).attr("marker-end", `url(#${markerId})`).attr("class", "st-edge");
|
|
16971
17469
|
}
|
|
@@ -17029,7 +17527,7 @@ function renderStateForExport(content, theme, palette) {
|
|
|
17029
17527
|
document.body.removeChild(container);
|
|
17030
17528
|
}
|
|
17031
17529
|
}
|
|
17032
|
-
var d3Selection10, d3Shape8, DIAGRAM_PADDING9, MAX_SCALE8, NODE_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, GROUP_LABEL_FONT_SIZE3, EDGE_STROKE_WIDTH9, NODE_STROKE_WIDTH9, ARROWHEAD_W4, ARROWHEAD_H4, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING2,
|
|
17530
|
+
var d3Selection10, d3Shape8, DIAGRAM_PADDING9, MAX_SCALE8, NODE_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, GROUP_LABEL_FONT_SIZE3, EDGE_STROKE_WIDTH9, NODE_STROKE_WIDTH9, ARROWHEAD_W4, ARROWHEAD_H4, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING2, lineGenerator7;
|
|
17033
17531
|
var init_state_renderer = __esm({
|
|
17034
17532
|
"src/graph/state-renderer.ts"() {
|
|
17035
17533
|
"use strict";
|
|
@@ -17051,7 +17549,7 @@ var init_state_renderer = __esm({
|
|
|
17051
17549
|
PSEUDOSTATE_RADIUS = 10;
|
|
17052
17550
|
STATE_CORNER_RADIUS = 10;
|
|
17053
17551
|
GROUP_EXTRA_PADDING2 = 12;
|
|
17054
|
-
|
|
17552
|
+
lineGenerator7 = d3Shape8.line().x((d) => d.x).y((d) => d.y).curve(d3Shape8.curveBasis);
|
|
17055
17553
|
}
|
|
17056
17554
|
});
|
|
17057
17555
|
|
|
@@ -17785,9 +18283,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17785
18283
|
const GROUP_PADDING_BOTTOM = 8;
|
|
17786
18284
|
const GROUP_LABEL_SIZE = 11;
|
|
17787
18285
|
const titleOffset = title ? TITLE_HEIGHT5 : 0;
|
|
17788
|
-
const legendOffset = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT9 + LEGEND_BOTTOM_GAP : 0;
|
|
17789
18286
|
const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
|
|
17790
|
-
const participantStartY = TOP_MARGIN + titleOffset +
|
|
18287
|
+
const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
|
|
17791
18288
|
const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
|
|
17792
18289
|
const hasActors = participants.some((p) => p.type === "actor");
|
|
17793
18290
|
const messageStartOffset = MESSAGE_START_OFFSET + (hasActors ? 20 : 0);
|
|
@@ -17869,7 +18366,9 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17869
18366
|
participants.length * PARTICIPANT_GAP,
|
|
17870
18367
|
PARTICIPANT_BOX_WIDTH + 40
|
|
17871
18368
|
);
|
|
17872
|
-
const
|
|
18369
|
+
const contentHeight = participantStartY + PARTICIPANT_BOX_HEIGHT + Math.max(lifelineLength, 40) + 40;
|
|
18370
|
+
const legendSpace = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT : 0;
|
|
18371
|
+
const totalHeight = contentHeight + legendSpace;
|
|
17873
18372
|
const containerWidth = options?.exportWidth ?? container.getBoundingClientRect().width;
|
|
17874
18373
|
const svgWidth = Math.max(totalWidth, containerWidth);
|
|
17875
18374
|
const diagramWidth = participants.length * PARTICIPANT_GAP;
|
|
@@ -17927,13 +18426,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17927
18426
|
}
|
|
17928
18427
|
}
|
|
17929
18428
|
if (parsed.tagGroups.length > 0) {
|
|
17930
|
-
const legendY =
|
|
18429
|
+
const legendY = contentHeight;
|
|
17931
18430
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
17932
18431
|
const legendItems = [];
|
|
17933
18432
|
for (const tg of parsed.tagGroups) {
|
|
17934
18433
|
if (tg.entries.length === 0) continue;
|
|
17935
18434
|
const isActive = !!activeTagGroup && tg.name.toLowerCase() === activeTagGroup.toLowerCase();
|
|
17936
|
-
const pillWidth = tg.name.length *
|
|
18435
|
+
const pillWidth = tg.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
17937
18436
|
const entries = tg.entries.map((e) => ({
|
|
17938
18437
|
value: e.value,
|
|
17939
18438
|
color: resolveColor(e.color)
|
|
@@ -17942,38 +18441,42 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17942
18441
|
if (isActive) {
|
|
17943
18442
|
let entriesWidth = 0;
|
|
17944
18443
|
for (const entry of entries) {
|
|
17945
|
-
entriesWidth +=
|
|
18444
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
17946
18445
|
}
|
|
17947
|
-
totalWidth2 =
|
|
18446
|
+
totalWidth2 = LEGEND_CAPSULE_PAD * 2 + pillWidth + 4 + entriesWidth;
|
|
17948
18447
|
}
|
|
17949
18448
|
legendItems.push({ group: tg, isActive, pillWidth, totalWidth: totalWidth2, entries });
|
|
17950
18449
|
}
|
|
17951
|
-
const totalLegendWidth = legendItems.reduce((s, item) => s + item.totalWidth, 0) + (legendItems.length - 1) *
|
|
18450
|
+
const totalLegendWidth = legendItems.reduce((s, item) => s + item.totalWidth, 0) + (legendItems.length - 1) * LEGEND_GROUP_GAP;
|
|
17952
18451
|
let legendX = (svgWidth - totalLegendWidth) / 2;
|
|
18452
|
+
const legendContainer = svg.append("g").attr("class", "sequence-legend");
|
|
18453
|
+
if (activeTagGroup) {
|
|
18454
|
+
legendContainer.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
18455
|
+
}
|
|
17953
18456
|
for (const item of legendItems) {
|
|
17954
|
-
const gEl =
|
|
18457
|
+
const gEl = legendContainer.append("g").attr("transform", `translate(${legendX}, ${legendY})`).attr("class", "sequence-legend-group").attr("data-legend-group", item.group.name.toLowerCase()).style("cursor", "pointer");
|
|
17955
18458
|
if (item.isActive) {
|
|
17956
|
-
gEl.append("rect").attr("width", item.totalWidth).attr("height",
|
|
18459
|
+
gEl.append("rect").attr("width", item.totalWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
17957
18460
|
}
|
|
17958
|
-
const pillXOff = item.isActive ?
|
|
17959
|
-
const pillYOff = item.isActive ?
|
|
17960
|
-
const pillH =
|
|
18461
|
+
const pillXOff = item.isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
18462
|
+
const pillYOff = item.isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
18463
|
+
const pillH = LEGEND_HEIGHT - (item.isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
17961
18464
|
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", item.pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", item.isActive ? palette.bg : groupBg);
|
|
17962
18465
|
if (item.isActive) {
|
|
17963
18466
|
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", item.pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
|
|
17964
18467
|
}
|
|
17965
|
-
gEl.append("text").attr("x", pillXOff + item.pillWidth / 2).attr("y",
|
|
18468
|
+
gEl.append("text").attr("x", pillXOff + item.pillWidth / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", item.isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").text(item.group.name);
|
|
17966
18469
|
if (item.isActive) {
|
|
17967
18470
|
let entryX = pillXOff + item.pillWidth + 4;
|
|
17968
18471
|
for (const entry of item.entries) {
|
|
17969
18472
|
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
17970
|
-
entryG.append("circle").attr("cx", entryX +
|
|
17971
|
-
const textX = entryX +
|
|
17972
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
17973
|
-
entryX = textX + entry.value.length *
|
|
18473
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
18474
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
18475
|
+
entryG.append("text").attr("x", textX).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).text(entry.value);
|
|
18476
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
17974
18477
|
}
|
|
17975
18478
|
}
|
|
17976
|
-
legendX += item.totalWidth +
|
|
18479
|
+
legendX += item.totalWidth + LEGEND_GROUP_GAP;
|
|
17977
18480
|
}
|
|
17978
18481
|
}
|
|
17979
18482
|
for (const group of groups) {
|
|
@@ -18494,7 +18997,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
|
|
|
18494
18997
|
});
|
|
18495
18998
|
}
|
|
18496
18999
|
}
|
|
18497
|
-
var d3Selection11, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT5, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, COLLAPSED_NOTE_H, COLLAPSED_NOTE_W,
|
|
19000
|
+
var d3Selection11, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT5, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, COLLAPSED_NOTE_H, COLLAPSED_NOTE_W, LABEL_CHAR_WIDTH, LABEL_MAX_CHARS, fill, stroke, SW, W, H;
|
|
18498
19001
|
var init_renderer9 = __esm({
|
|
18499
19002
|
"src/sequence/renderer.ts"() {
|
|
18500
19003
|
"use strict";
|
|
@@ -18505,6 +19008,7 @@ var init_renderer9 = __esm({
|
|
|
18505
19008
|
init_colors();
|
|
18506
19009
|
init_parser();
|
|
18507
19010
|
init_tag_resolution();
|
|
19011
|
+
init_legend_constants();
|
|
18508
19012
|
PARTICIPANT_GAP = 160;
|
|
18509
19013
|
PARTICIPANT_BOX_WIDTH = 120;
|
|
18510
19014
|
PARTICIPANT_BOX_HEIGHT = 50;
|
|
@@ -18526,18 +19030,6 @@ var init_renderer9 = __esm({
|
|
|
18526
19030
|
NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
|
|
18527
19031
|
COLLAPSED_NOTE_H = 20;
|
|
18528
19032
|
COLLAPSED_NOTE_W = 40;
|
|
18529
|
-
LEGEND_HEIGHT9 = 28;
|
|
18530
|
-
LEGEND_PILL_PAD8 = 16;
|
|
18531
|
-
LEGEND_PILL_FONT_SIZE6 = 11;
|
|
18532
|
-
LEGEND_PILL_FONT_W8 = LEGEND_PILL_FONT_SIZE6 * 0.6;
|
|
18533
|
-
LEGEND_CAPSULE_PAD8 = 4;
|
|
18534
|
-
LEGEND_DOT_R9 = 4;
|
|
18535
|
-
LEGEND_ENTRY_FONT_SIZE7 = 10;
|
|
18536
|
-
LEGEND_ENTRY_FONT_W8 = LEGEND_ENTRY_FONT_SIZE7 * 0.6;
|
|
18537
|
-
LEGEND_ENTRY_DOT_GAP8 = 4;
|
|
18538
|
-
LEGEND_ENTRY_TRAIL8 = 8;
|
|
18539
|
-
LEGEND_GROUP_GAP6 = 12;
|
|
18540
|
-
LEGEND_BOTTOM_GAP = 8;
|
|
18541
19033
|
LABEL_CHAR_WIDTH = 7.5;
|
|
18542
19034
|
LABEL_MAX_CHARS = Math.floor((PARTICIPANT_BOX_WIDTH - 10) / LABEL_CHAR_WIDTH);
|
|
18543
19035
|
fill = (palette, isDark, color) => color ? mix(color, isDark ? palette.surface : palette.bg, isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
|
|
@@ -20145,9 +20637,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20145
20637
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
20146
20638
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
20147
20639
|
const margin = {
|
|
20148
|
-
top: 104 + markerMargin
|
|
20640
|
+
top: 104 + markerMargin,
|
|
20149
20641
|
right: 40 + scaleMargin,
|
|
20150
|
-
bottom: 40,
|
|
20642
|
+
bottom: 40 + tagLegendReserve,
|
|
20151
20643
|
left: 60 + scaleMargin
|
|
20152
20644
|
};
|
|
20153
20645
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20254,9 +20746,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20254
20746
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
20255
20747
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
20256
20748
|
const margin = {
|
|
20257
|
-
top: 104 + markerMargin
|
|
20749
|
+
top: 104 + markerMargin,
|
|
20258
20750
|
right: 200,
|
|
20259
|
-
bottom: 40,
|
|
20751
|
+
bottom: 40 + tagLegendReserve,
|
|
20260
20752
|
left: 60 + scaleMargin
|
|
20261
20753
|
};
|
|
20262
20754
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20393,9 +20885,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20393
20885
|
const dynamicLeftMargin = Math.max(120, maxGroupNameLen * 7 + 30);
|
|
20394
20886
|
const baseTopMargin = title ? 50 : 20;
|
|
20395
20887
|
const margin = {
|
|
20396
|
-
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin
|
|
20888
|
+
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin,
|
|
20397
20889
|
right: 40,
|
|
20398
|
-
bottom: 40 + scaleMargin,
|
|
20890
|
+
bottom: 40 + scaleMargin + tagLegendReserve,
|
|
20399
20891
|
left: dynamicLeftMargin
|
|
20400
20892
|
};
|
|
20401
20893
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20539,9 +21031,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20539
21031
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
20540
21032
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
20541
21033
|
const margin = {
|
|
20542
|
-
top: 104 + (timelineScale ? 40 : 0) + markerMargin
|
|
21034
|
+
top: 104 + (timelineScale ? 40 : 0) + markerMargin,
|
|
20543
21035
|
right: 40,
|
|
20544
|
-
bottom: 40 + scaleMargin,
|
|
21036
|
+
bottom: 40 + scaleMargin + tagLegendReserve,
|
|
20545
21037
|
left: 60
|
|
20546
21038
|
};
|
|
20547
21039
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20672,17 +21164,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20672
21164
|
});
|
|
20673
21165
|
}
|
|
20674
21166
|
if (parsed.timelineTagGroups.length > 0) {
|
|
20675
|
-
const LG_HEIGHT =
|
|
20676
|
-
const LG_PILL_PAD =
|
|
20677
|
-
const LG_PILL_FONT_SIZE =
|
|
20678
|
-
const LG_PILL_FONT_W =
|
|
20679
|
-
const LG_CAPSULE_PAD =
|
|
20680
|
-
const LG_DOT_R =
|
|
20681
|
-
const LG_ENTRY_FONT_SIZE =
|
|
20682
|
-
const LG_ENTRY_FONT_W =
|
|
20683
|
-
const LG_ENTRY_DOT_GAP =
|
|
20684
|
-
const LG_ENTRY_TRAIL =
|
|
20685
|
-
const LG_GROUP_GAP =
|
|
21167
|
+
const LG_HEIGHT = LEGEND_HEIGHT;
|
|
21168
|
+
const LG_PILL_PAD = LEGEND_PILL_PAD;
|
|
21169
|
+
const LG_PILL_FONT_SIZE = LEGEND_PILL_FONT_SIZE;
|
|
21170
|
+
const LG_PILL_FONT_W = LEGEND_PILL_FONT_W;
|
|
21171
|
+
const LG_CAPSULE_PAD = LEGEND_CAPSULE_PAD;
|
|
21172
|
+
const LG_DOT_R = LEGEND_DOT_R;
|
|
21173
|
+
const LG_ENTRY_FONT_SIZE = LEGEND_ENTRY_FONT_SIZE;
|
|
21174
|
+
const LG_ENTRY_FONT_W = LEGEND_ENTRY_FONT_W;
|
|
21175
|
+
const LG_ENTRY_DOT_GAP = LEGEND_ENTRY_DOT_GAP;
|
|
21176
|
+
const LG_ENTRY_TRAIL = LEGEND_ENTRY_TRAIL;
|
|
21177
|
+
const LG_GROUP_GAP = LEGEND_GROUP_GAP;
|
|
20686
21178
|
const LG_ICON_W = 20;
|
|
20687
21179
|
const mainSvg = d3Selection12.select(container).select("svg");
|
|
20688
21180
|
const mainG = mainSvg.select("g");
|
|
@@ -20715,6 +21207,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20715
21207
|
);
|
|
20716
21208
|
}, drawLegend2 = function() {
|
|
20717
21209
|
mainSvg.selectAll(".tl-tag-legend-group").remove();
|
|
21210
|
+
mainSvg.selectAll(".tl-tag-legend-container").remove();
|
|
20718
21211
|
const effectiveColorKey = (currentActiveGroup ?? currentSwimlaneGroup)?.toLowerCase() ?? null;
|
|
20719
21212
|
const visibleGroups = viewMode ? legendGroups.filter(
|
|
20720
21213
|
(lg) => effectiveColorKey != null && lg.group.name.toLowerCase() === effectiveColorKey
|
|
@@ -20725,13 +21218,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20725
21218
|
return s + (isActive ? lg.expandedWidth : lg.minifiedWidth);
|
|
20726
21219
|
}, 0) + (visibleGroups.length - 1) * LG_GROUP_GAP;
|
|
20727
21220
|
let cx = (width - totalW) / 2;
|
|
21221
|
+
const legendContainer = mainSvg.append("g").attr("class", "tl-tag-legend-container");
|
|
21222
|
+
if (currentActiveGroup) {
|
|
21223
|
+
legendContainer.attr("data-legend-active", currentActiveGroup.toLowerCase());
|
|
21224
|
+
}
|
|
20728
21225
|
for (const lg of visibleGroups) {
|
|
20729
21226
|
const groupKey = lg.group.name.toLowerCase();
|
|
20730
21227
|
const isActive = viewMode || currentActiveGroup != null && currentActiveGroup.toLowerCase() === groupKey;
|
|
20731
21228
|
const isSwimActive = currentSwimlaneGroup != null && currentSwimlaneGroup.toLowerCase() === groupKey;
|
|
20732
21229
|
const pillLabel = lg.group.name;
|
|
20733
21230
|
const pillWidth = pillLabel.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20734
|
-
const gEl =
|
|
21231
|
+
const gEl = legendContainer.append("g").attr("transform", `translate(${cx}, ${legendY})`).attr("class", "tl-tag-legend-group tl-tag-legend-entry").attr("data-legend-group", groupKey).attr("data-tag-group", groupKey).attr("data-legend-entry", "__group__");
|
|
20735
21232
|
if (!viewMode) {
|
|
20736
21233
|
gEl.style("cursor", "pointer").on("click", () => {
|
|
20737
21234
|
currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
|
|
@@ -20821,7 +21318,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20821
21318
|
});
|
|
20822
21319
|
};
|
|
20823
21320
|
var drawSwimlaneIcon = drawSwimlaneIcon2, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
|
|
20824
|
-
const legendY =
|
|
21321
|
+
const legendY = height - LG_HEIGHT - 4;
|
|
20825
21322
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
20826
21323
|
const legendGroups = parsed.timelineTagGroups.map((g) => {
|
|
20827
21324
|
const pillW = g.name.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
@@ -21592,7 +22089,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21592
22089
|
const orgParsed = parseOrg2(content, effectivePalette2);
|
|
21593
22090
|
if (orgParsed.error) return "";
|
|
21594
22091
|
const collapsedNodes = orgExportState?.collapsedNodes;
|
|
21595
|
-
const activeTagGroup = orgExportState?.activeTagGroup ?? null;
|
|
22092
|
+
const activeTagGroup = orgExportState?.activeTagGroup ?? options?.tagGroup ?? null;
|
|
21596
22093
|
const hiddenAttributes = orgExportState?.hiddenAttributes;
|
|
21597
22094
|
const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
21598
22095
|
const orgLayout = layoutOrg2(
|
|
@@ -21621,7 +22118,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21621
22118
|
const sitemapParsed = parseSitemap2(content, effectivePalette2);
|
|
21622
22119
|
if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
|
|
21623
22120
|
const collapsedNodes = orgExportState?.collapsedNodes;
|
|
21624
|
-
const activeTagGroup = orgExportState?.activeTagGroup ?? null;
|
|
22121
|
+
const activeTagGroup = orgExportState?.activeTagGroup ?? options?.tagGroup ?? null;
|
|
21625
22122
|
const hiddenAttributes = orgExportState?.hiddenAttributes;
|
|
21626
22123
|
const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
21627
22124
|
const sitemapLayout = layoutSitemap2(
|
|
@@ -21649,7 +22146,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21649
22146
|
container2.style.position = "absolute";
|
|
21650
22147
|
container2.style.left = "-9999px";
|
|
21651
22148
|
document.body.appendChild(container2);
|
|
21652
|
-
renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark");
|
|
22149
|
+
renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark", void 0, void 0, options?.tagGroup);
|
|
21653
22150
|
return finalizeSvgExport(container2, theme, effectivePalette2, options);
|
|
21654
22151
|
}
|
|
21655
22152
|
if (detectedType === "class") {
|
|
@@ -21681,7 +22178,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21681
22178
|
const exportWidth = erLayout.width + PADDING * 2;
|
|
21682
22179
|
const exportHeight = erLayout.height + PADDING * 2 + titleOffset;
|
|
21683
22180
|
const container2 = createExportContainer(exportWidth, exportHeight);
|
|
21684
|
-
renderERDiagram2(container2, erParsed, erLayout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight });
|
|
22181
|
+
renderERDiagram2(container2, erParsed, erLayout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight }, options?.tagGroup);
|
|
21685
22182
|
return finalizeSvgExport(container2, theme, effectivePalette2, options);
|
|
21686
22183
|
}
|
|
21687
22184
|
if (detectedType === "initiative-status") {
|
|
@@ -21718,7 +22215,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21718
22215
|
const exportHeight = c4Layout.height + PADDING * 2 + titleOffset;
|
|
21719
22216
|
const container2 = createExportContainer(exportWidth, exportHeight);
|
|
21720
22217
|
const renderFn = c4Level === "deployment" || c4Level === "components" && c4System && c4Container || c4Level === "containers" && c4System ? renderC4Containers2 : renderC4Context2;
|
|
21721
|
-
renderFn(container2, c4Parsed, c4Layout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight });
|
|
22218
|
+
renderFn(container2, c4Parsed, c4Layout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight }, options?.tagGroup);
|
|
21722
22219
|
return finalizeSvgExport(container2, theme, effectivePalette2, options);
|
|
21723
22220
|
}
|
|
21724
22221
|
if (detectedType === "flowchart") {
|
|
@@ -21741,16 +22238,16 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21741
22238
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
21742
22239
|
const infraParsed = parseInfra2(content);
|
|
21743
22240
|
if (infraParsed.error || infraParsed.nodes.length === 0) return "";
|
|
21744
|
-
const
|
|
21745
|
-
const infraComputed = computeInfra2(infraParsed, selectedScenario ? { scenario: selectedScenario } : {});
|
|
22241
|
+
const infraComputed = computeInfra2(infraParsed);
|
|
21746
22242
|
const infraLayout = layoutInfra2(infraComputed);
|
|
22243
|
+
const activeTagGroup = options?.tagGroup ?? null;
|
|
21747
22244
|
const titleOffset = infraParsed.title ? 40 : 0;
|
|
21748
22245
|
const legendGroups = computeInfraLegendGroups2(infraLayout.nodes, infraParsed.tagGroups, effectivePalette2);
|
|
21749
22246
|
const legendOffset = legendGroups.length > 0 ? 28 : 0;
|
|
21750
22247
|
const exportWidth = infraLayout.width;
|
|
21751
22248
|
const exportHeight = infraLayout.height + titleOffset + legendOffset;
|
|
21752
22249
|
const container2 = createExportContainer(exportWidth, exportHeight);
|
|
21753
|
-
renderInfra2(container2, infraLayout, effectivePalette2, theme === "dark", infraParsed.title, infraParsed.titleLineNumber, infraParsed.tagGroups,
|
|
22250
|
+
renderInfra2(container2, infraLayout, effectivePalette2, theme === "dark", infraParsed.title, infraParsed.titleLineNumber, infraParsed.tagGroups, activeTagGroup, false, null, null, true);
|
|
21754
22251
|
const infraSvg = container2.querySelector("svg");
|
|
21755
22252
|
if (infraSvg) {
|
|
21756
22253
|
infraSvg.setAttribute("width", String(exportWidth));
|
|
@@ -21794,7 +22291,8 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21794
22291
|
const seqParsed = parseSequenceDgmo2(content);
|
|
21795
22292
|
if (seqParsed.error || seqParsed.participants.length === 0) return "";
|
|
21796
22293
|
renderSequenceDiagram2(container, seqParsed, effectivePalette, isDark, void 0, {
|
|
21797
|
-
exportWidth: EXPORT_WIDTH
|
|
22294
|
+
exportWidth: EXPORT_WIDTH,
|
|
22295
|
+
activeTagGroup: options?.tagGroup
|
|
21798
22296
|
});
|
|
21799
22297
|
} else if (parsed.type === "wordcloud") {
|
|
21800
22298
|
await renderWordCloudAsync(container, parsed, effectivePalette, isDark, dims);
|
|
@@ -21808,7 +22306,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21808
22306
|
isDark,
|
|
21809
22307
|
void 0,
|
|
21810
22308
|
dims,
|
|
21811
|
-
orgExportState?.activeTagGroup,
|
|
22309
|
+
orgExportState?.activeTagGroup ?? options?.tagGroup,
|
|
21812
22310
|
orgExportState?.swimlaneTagGroup
|
|
21813
22311
|
);
|
|
21814
22312
|
} else if (parsed.type === "venn") {
|
|
@@ -21837,6 +22335,7 @@ var init_d3 = __esm({
|
|
|
21837
22335
|
init_diagnostics();
|
|
21838
22336
|
init_parsing();
|
|
21839
22337
|
init_tag_groups();
|
|
22338
|
+
init_legend_constants();
|
|
21840
22339
|
DEFAULT_CLOUD_OPTIONS = {
|
|
21841
22340
|
rotate: "none",
|
|
21842
22341
|
max: 0,
|
|
@@ -22154,7 +22653,7 @@ async function render(content, options) {
|
|
|
22154
22653
|
c4Level: options?.c4Level,
|
|
22155
22654
|
c4System: options?.c4System,
|
|
22156
22655
|
c4Container: options?.c4Container,
|
|
22157
|
-
|
|
22656
|
+
tagGroup: options?.tagGroup
|
|
22158
22657
|
});
|
|
22159
22658
|
}
|
|
22160
22659
|
|