@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.js
CHANGED
|
@@ -1445,6 +1445,29 @@ var init_tag_groups = __esm({
|
|
|
1445
1445
|
}
|
|
1446
1446
|
});
|
|
1447
1447
|
|
|
1448
|
+
// src/utils/legend-constants.ts
|
|
1449
|
+
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;
|
|
1450
|
+
var init_legend_constants = __esm({
|
|
1451
|
+
"src/utils/legend-constants.ts"() {
|
|
1452
|
+
"use strict";
|
|
1453
|
+
LEGEND_HEIGHT = 28;
|
|
1454
|
+
LEGEND_PILL_PAD = 16;
|
|
1455
|
+
LEGEND_PILL_FONT_SIZE = 11;
|
|
1456
|
+
LEGEND_PILL_FONT_W = LEGEND_PILL_FONT_SIZE * 0.6;
|
|
1457
|
+
LEGEND_CAPSULE_PAD = 4;
|
|
1458
|
+
LEGEND_DOT_R = 4;
|
|
1459
|
+
LEGEND_ENTRY_FONT_SIZE = 10;
|
|
1460
|
+
LEGEND_ENTRY_FONT_W = LEGEND_ENTRY_FONT_SIZE * 0.6;
|
|
1461
|
+
LEGEND_ENTRY_DOT_GAP = 4;
|
|
1462
|
+
LEGEND_ENTRY_TRAIL = 8;
|
|
1463
|
+
LEGEND_GROUP_GAP = 12;
|
|
1464
|
+
LEGEND_EYE_SIZE = 14;
|
|
1465
|
+
LEGEND_EYE_GAP = 6;
|
|
1466
|
+
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";
|
|
1467
|
+
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";
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1448
1471
|
// src/sequence/participant-inference.ts
|
|
1449
1472
|
function inferParticipantType(name) {
|
|
1450
1473
|
for (const rule of PARTICIPANT_RULES) {
|
|
@@ -4756,10 +4779,12 @@ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacit
|
|
|
4756
4779
|
if (type === "category" && data && data.length > 0) {
|
|
4757
4780
|
const maxLabelLen = Math.max(...data.map((l) => l.length));
|
|
4758
4781
|
const count = data.length;
|
|
4759
|
-
|
|
4760
|
-
|
|
4782
|
+
const step = intervalOverride != null && intervalOverride > 0 ? intervalOverride + 1 : 1;
|
|
4783
|
+
const visibleCount = Math.ceil(count / step);
|
|
4784
|
+
if (visibleCount > 10 || maxLabelLen > 20) catFontSize = 10;
|
|
4785
|
+
else if (visibleCount > 5 || maxLabelLen > 14) catFontSize = 11;
|
|
4761
4786
|
else if (maxLabelLen > 8) catFontSize = 12;
|
|
4762
|
-
if (chartWidthHint && count > 0) {
|
|
4787
|
+
if ((intervalOverride == null || intervalOverride === 0) && chartWidthHint && count > 0) {
|
|
4763
4788
|
const availPerLabel = Math.floor(chartWidthHint * 0.85 / count);
|
|
4764
4789
|
catLabelExtras = {
|
|
4765
4790
|
width: availPerLabel,
|
|
@@ -4777,6 +4802,9 @@ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacit
|
|
|
4777
4802
|
fontFamily: FONT_FAMILY,
|
|
4778
4803
|
...type === "category" && {
|
|
4779
4804
|
interval: intervalOverride ?? 0,
|
|
4805
|
+
// Prevent ECharts auto-rotation: it measures raw slot width (chartWidth/N),
|
|
4806
|
+
// which is too narrow when an interval skips most labels, and rotates to 90°.
|
|
4807
|
+
rotate: 0,
|
|
4780
4808
|
formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2"),
|
|
4781
4809
|
...catLabelExtras
|
|
4782
4810
|
}
|
|
@@ -4852,21 +4880,13 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
|
|
|
4852
4880
|
]
|
|
4853
4881
|
};
|
|
4854
4882
|
}
|
|
4855
|
-
function
|
|
4883
|
+
function buildIntervalStep(labels) {
|
|
4856
4884
|
const count = labels.length;
|
|
4857
|
-
if (count <=
|
|
4858
|
-
const snapSteps = [1, 2,
|
|
4859
|
-
const raw = Math.ceil(count /
|
|
4885
|
+
if (count <= 6) return 0;
|
|
4886
|
+
const snapSteps = [1, 2, 5, 10, 25, 50, 100];
|
|
4887
|
+
const raw = Math.ceil(count / 5);
|
|
4860
4888
|
const N = [...snapSteps].reverse().find((s) => s <= raw) ?? 1;
|
|
4861
|
-
|
|
4862
|
-
for (let i = 0; i < count; i += N) pinned.add(i);
|
|
4863
|
-
for (const era of eras) {
|
|
4864
|
-
const si = labels.indexOf(era.start);
|
|
4865
|
-
const ei = labels.indexOf(era.end);
|
|
4866
|
-
if (si >= 0) pinned.add(si);
|
|
4867
|
-
if (ei >= 0) pinned.add(ei);
|
|
4868
|
-
}
|
|
4869
|
-
return (index) => pinned.has(index);
|
|
4889
|
+
return N - 1;
|
|
4870
4890
|
}
|
|
4871
4891
|
function buildMarkArea(eras, labels, textColor, defaultColor) {
|
|
4872
4892
|
if (eras.length === 0) return void 0;
|
|
@@ -4901,7 +4921,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4901
4921
|
const labels = parsed.data.map((d) => d.label);
|
|
4902
4922
|
const values = parsed.data.map((d) => d.value);
|
|
4903
4923
|
const eras = parsed.eras ?? [];
|
|
4904
|
-
const interval =
|
|
4924
|
+
const interval = buildIntervalStep(labels);
|
|
4905
4925
|
const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
|
|
4906
4926
|
return {
|
|
4907
4927
|
...CHART_BASE,
|
|
@@ -4933,7 +4953,7 @@ function buildMultiLineOption(parsed, palette, textColor, axisLineColor, splitLi
|
|
|
4933
4953
|
const seriesNames = parsed.seriesNames ?? [];
|
|
4934
4954
|
const labels = parsed.data.map((d) => d.label);
|
|
4935
4955
|
const eras = parsed.eras ?? [];
|
|
4936
|
-
const interval =
|
|
4956
|
+
const interval = buildIntervalStep(labels);
|
|
4937
4957
|
const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
|
|
4938
4958
|
const series = seriesNames.map((name, idx) => {
|
|
4939
4959
|
const color = parsed.seriesNameColors?.[idx] ?? colors[idx % colors.length];
|
|
@@ -4977,7 +4997,7 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4977
4997
|
const labels = parsed.data.map((d) => d.label);
|
|
4978
4998
|
const values = parsed.data.map((d) => d.value);
|
|
4979
4999
|
const eras = parsed.eras ?? [];
|
|
4980
|
-
const interval =
|
|
5000
|
+
const interval = buildIntervalStep(labels);
|
|
4981
5001
|
const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
|
|
4982
5002
|
return {
|
|
4983
5003
|
...CHART_BASE,
|
|
@@ -6990,7 +7010,6 @@ function parseInfra(content) {
|
|
|
6990
7010
|
edges: [],
|
|
6991
7011
|
groups: [],
|
|
6992
7012
|
tagGroups: [],
|
|
6993
|
-
scenarios: [],
|
|
6994
7013
|
options: {},
|
|
6995
7014
|
diagnostics: [],
|
|
6996
7015
|
error: null
|
|
@@ -7093,16 +7112,7 @@ function parseInfra(content) {
|
|
|
7093
7112
|
continue;
|
|
7094
7113
|
}
|
|
7095
7114
|
if (/^scenario\s*:/i.test(trimmed)) {
|
|
7096
|
-
|
|
7097
|
-
finishCurrentTagGroup();
|
|
7098
|
-
currentGroup = null;
|
|
7099
|
-
const scenarioName = trimmed.replace(/^scenario\s*:\s*/i, "").trim();
|
|
7100
|
-
const scenario = {
|
|
7101
|
-
name: scenarioName,
|
|
7102
|
-
overrides: {},
|
|
7103
|
-
lineNumber
|
|
7104
|
-
};
|
|
7105
|
-
let scenarioNodeId = null;
|
|
7115
|
+
console.warn("[dgmo warn] scenario syntax is deprecated and will be ignored");
|
|
7106
7116
|
let si = i + 1;
|
|
7107
7117
|
while (si < lines.length) {
|
|
7108
7118
|
const sLine = lines[si];
|
|
@@ -7113,23 +7123,9 @@ function parseInfra(content) {
|
|
|
7113
7123
|
}
|
|
7114
7124
|
const sIndent = sLine.length - sLine.trimStart().length;
|
|
7115
7125
|
if (sIndent === 0) break;
|
|
7116
|
-
if (sIndent <= 2) {
|
|
7117
|
-
scenarioNodeId = nodeId2(sTrimmed.replace(/\|.*$/, "").trim());
|
|
7118
|
-
if (!scenario.overrides[scenarioNodeId]) {
|
|
7119
|
-
scenario.overrides[scenarioNodeId] = {};
|
|
7120
|
-
}
|
|
7121
|
-
} else if (scenarioNodeId) {
|
|
7122
|
-
const pm = sTrimmed.match(PROPERTY_RE);
|
|
7123
|
-
if (pm) {
|
|
7124
|
-
const key = pm[1].toLowerCase();
|
|
7125
|
-
const val = parsePropertyValue(pm[2].trim());
|
|
7126
|
-
scenario.overrides[scenarioNodeId][key] = val;
|
|
7127
|
-
}
|
|
7128
|
-
}
|
|
7129
7126
|
si++;
|
|
7130
7127
|
}
|
|
7131
7128
|
i = si - 1;
|
|
7132
|
-
result.scenarios.push(scenario);
|
|
7133
7129
|
continue;
|
|
7134
7130
|
}
|
|
7135
7131
|
const tagMatch = trimmed.match(TAG_GROUP_RE);
|
|
@@ -7662,14 +7658,14 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
|
|
|
7662
7658
|
const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
|
|
7663
7659
|
const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
|
|
7664
7660
|
if (visibleEntries.length === 0) continue;
|
|
7665
|
-
const pillWidth = group.name.length *
|
|
7661
|
+
const pillWidth = group.name.length * LEGEND_PILL_FONT_W2 + LEGEND_PILL_PAD2;
|
|
7666
7662
|
const minPillWidth = pillWidth;
|
|
7667
7663
|
let entriesWidth = 0;
|
|
7668
7664
|
for (const entry of visibleEntries) {
|
|
7669
|
-
entriesWidth +=
|
|
7665
|
+
entriesWidth += LEGEND_DOT_R2 * 2 + LEGEND_ENTRY_DOT_GAP2 + entry.value.length * LEGEND_ENTRY_FONT_W2 + LEGEND_ENTRY_TRAIL2;
|
|
7670
7666
|
}
|
|
7671
|
-
const eyeSpace = showEyeIcons ?
|
|
7672
|
-
const capsuleWidth =
|
|
7667
|
+
const eyeSpace = showEyeIcons ? LEGEND_EYE_SIZE2 + LEGEND_EYE_GAP2 : 0;
|
|
7668
|
+
const capsuleWidth = LEGEND_CAPSULE_PAD2 * 2 + pillWidth + 4 + eyeSpace + entriesWidth;
|
|
7673
7669
|
groups.push({
|
|
7674
7670
|
name: group.name,
|
|
7675
7671
|
alias: group.alias,
|
|
@@ -7680,9 +7676,9 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
|
|
|
7680
7676
|
x: 0,
|
|
7681
7677
|
y: 0,
|
|
7682
7678
|
width: capsuleWidth,
|
|
7683
|
-
height:
|
|
7679
|
+
height: LEGEND_HEIGHT2,
|
|
7684
7680
|
minifiedWidth: minPillWidth,
|
|
7685
|
-
minifiedHeight:
|
|
7681
|
+
minifiedHeight: LEGEND_HEIGHT2
|
|
7686
7682
|
});
|
|
7687
7683
|
}
|
|
7688
7684
|
return groups;
|
|
@@ -7712,7 +7708,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
7712
7708
|
for (const g of legendGroups2) {
|
|
7713
7709
|
g.x = MARGIN;
|
|
7714
7710
|
g.y = cy;
|
|
7715
|
-
cy +=
|
|
7711
|
+
cy += LEGEND_HEIGHT2 + LEGEND_GROUP_GAP2;
|
|
7716
7712
|
if (g.width > maxWidth2) maxWidth2 = g.width;
|
|
7717
7713
|
}
|
|
7718
7714
|
return {
|
|
@@ -7721,7 +7717,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
7721
7717
|
containers: [],
|
|
7722
7718
|
legend: legendGroups2,
|
|
7723
7719
|
width: maxWidth2 + MARGIN * 2,
|
|
7724
|
-
height: cy -
|
|
7720
|
+
height: cy - LEGEND_GROUP_GAP2 + MARGIN
|
|
7725
7721
|
};
|
|
7726
7722
|
}
|
|
7727
7723
|
injectDefaultMetadata(parsed.roots, parsed.tagGroups);
|
|
@@ -8251,7 +8247,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
8251
8247
|
const effectiveH = (g) => activeTagGroup != null || allExpanded ? g.height : g.minifiedHeight;
|
|
8252
8248
|
if (visibleGroups.length > 0) {
|
|
8253
8249
|
if (legendPosition === "bottom") {
|
|
8254
|
-
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
8250
|
+
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP2;
|
|
8255
8251
|
const neededWidth = totalGroupsWidth + MARGIN * 2;
|
|
8256
8252
|
if (neededWidth > totalWidth) {
|
|
8257
8253
|
finalWidth = neededWidth;
|
|
@@ -8269,22 +8265,22 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
8269
8265
|
for (const g of visibleGroups) {
|
|
8270
8266
|
g.x = cx;
|
|
8271
8267
|
g.y = legendY;
|
|
8272
|
-
cx += effectiveW(g) +
|
|
8268
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP2;
|
|
8273
8269
|
}
|
|
8274
|
-
finalHeight = totalHeight + LEGEND_GAP +
|
|
8270
|
+
finalHeight = totalHeight + LEGEND_GAP + LEGEND_HEIGHT2;
|
|
8275
8271
|
} else {
|
|
8276
|
-
const legendShift =
|
|
8272
|
+
const legendShift = LEGEND_HEIGHT2 + LEGEND_GROUP_GAP2;
|
|
8277
8273
|
for (const n of layoutNodes) n.y += legendShift;
|
|
8278
8274
|
for (const c of containers) c.y += legendShift;
|
|
8279
8275
|
for (const e of layoutEdges) {
|
|
8280
8276
|
for (const p of e.points) p.y += legendShift;
|
|
8281
8277
|
}
|
|
8282
|
-
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
8278
|
+
const totalGroupsWidth = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP2;
|
|
8283
8279
|
let cx = MARGIN;
|
|
8284
8280
|
for (const g of visibleGroups) {
|
|
8285
8281
|
g.x = cx;
|
|
8286
8282
|
g.y = MARGIN;
|
|
8287
|
-
cx += effectiveW(g) +
|
|
8283
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP2;
|
|
8288
8284
|
}
|
|
8289
8285
|
finalHeight += legendShift;
|
|
8290
8286
|
const neededWidth = totalGroupsWidth + MARGIN * 2;
|
|
@@ -8302,7 +8298,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
|
|
|
8302
8298
|
height: finalHeight
|
|
8303
8299
|
};
|
|
8304
8300
|
}
|
|
8305
|
-
var 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,
|
|
8301
|
+
var 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;
|
|
8306
8302
|
var init_layout = __esm({
|
|
8307
8303
|
"src/org/layout.ts"() {
|
|
8308
8304
|
"use strict";
|
|
@@ -8323,17 +8319,17 @@ var init_layout = __esm({
|
|
|
8323
8319
|
CONTAINER_META_LINE_HEIGHT = 16;
|
|
8324
8320
|
STACK_V_GAP = 20;
|
|
8325
8321
|
LEGEND_GAP = 30;
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8322
|
+
LEGEND_HEIGHT2 = 28;
|
|
8323
|
+
LEGEND_PILL_PAD2 = 16;
|
|
8324
|
+
LEGEND_PILL_FONT_W2 = 11 * 0.6;
|
|
8325
|
+
LEGEND_CAPSULE_PAD2 = 4;
|
|
8326
|
+
LEGEND_DOT_R2 = 4;
|
|
8327
|
+
LEGEND_ENTRY_FONT_W2 = 10 * 0.6;
|
|
8328
|
+
LEGEND_ENTRY_DOT_GAP2 = 4;
|
|
8329
|
+
LEGEND_ENTRY_TRAIL2 = 8;
|
|
8330
|
+
LEGEND_GROUP_GAP2 = 12;
|
|
8331
|
+
LEGEND_EYE_SIZE2 = 14;
|
|
8332
|
+
LEGEND_EYE_GAP2 = 6;
|
|
8337
8333
|
}
|
|
8338
8334
|
});
|
|
8339
8335
|
|
|
@@ -8431,11 +8427,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
|
|
|
8431
8427
|
if (width <= 0 || height <= 0) return;
|
|
8432
8428
|
const titleOffset = parsed.title ? TITLE_HEIGHT : 0;
|
|
8433
8429
|
const legendOnly = layout.nodes.length === 0;
|
|
8434
|
-
const legendPosition = parsed.options?.["legend-position"] ?? "
|
|
8430
|
+
const legendPosition = parsed.options?.["legend-position"] ?? "bottom";
|
|
8435
8431
|
const hasLegend = layout.legend.length > 0;
|
|
8436
|
-
const layoutLegendShift =
|
|
8432
|
+
const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
8437
8433
|
const fixedLegend = !exportDims && hasLegend && !legendOnly;
|
|
8438
|
-
const legendReserve = fixedLegend ?
|
|
8434
|
+
const legendReserve = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
|
|
8439
8435
|
const fixedTitle = !exportDims && !!parsed.title;
|
|
8440
8436
|
const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
|
|
8441
8437
|
const diagramW = layout.width;
|
|
@@ -8590,56 +8586,60 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
|
|
|
8590
8586
|
if (fixedLegend && visibleGroups.length > 0) {
|
|
8591
8587
|
fixedPositions = /* @__PURE__ */ new Map();
|
|
8592
8588
|
const effectiveW = (g) => activeTagGroup != null ? g.width : g.minifiedWidth;
|
|
8593
|
-
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
8589
|
+
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
8594
8590
|
let cx = (width - totalW) / 2;
|
|
8595
8591
|
for (const g of visibleGroups) {
|
|
8596
8592
|
fixedPositions.set(g.name, cx);
|
|
8597
|
-
cx += effectiveW(g) +
|
|
8593
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
8598
8594
|
}
|
|
8599
8595
|
}
|
|
8600
|
-
const
|
|
8596
|
+
const legendParentBase = fixedLegend ? svg.append("g").attr("class", "org-legend-fixed").attr(
|
|
8601
8597
|
"transform",
|
|
8602
|
-
legendPosition === "bottom" ? `translate(0, ${height - DIAGRAM_PADDING -
|
|
8598
|
+
legendPosition === "bottom" ? `translate(0, ${height - DIAGRAM_PADDING - LEGEND_HEIGHT})` : `translate(0, ${DIAGRAM_PADDING + titleReserve})`
|
|
8603
8599
|
) : contentG;
|
|
8600
|
+
const legendParent = legendParentBase;
|
|
8601
|
+
if (fixedLegend && activeTagGroup) {
|
|
8602
|
+
legendParentBase.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
8603
|
+
}
|
|
8604
8604
|
for (const group of visibleGroups) {
|
|
8605
8605
|
const isActive = legendOnly || activeTagGroup != null && group.name.toLowerCase() === activeTagGroup.toLowerCase();
|
|
8606
8606
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
8607
8607
|
const pillLabel = group.name;
|
|
8608
|
-
const pillWidth = pillLabel.length *
|
|
8608
|
+
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
8609
8609
|
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
8610
8610
|
const gY = fixedPositions ? 0 : group.y;
|
|
8611
8611
|
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");
|
|
8612
8612
|
if (isActive) {
|
|
8613
|
-
gEl.append("rect").attr("width", group.width).attr("height",
|
|
8613
|
+
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
8614
8614
|
}
|
|
8615
|
-
const pillXOff = isActive ?
|
|
8616
|
-
const pillYOff = isActive ?
|
|
8617
|
-
const pillH =
|
|
8615
|
+
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
8616
|
+
const pillYOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
8617
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
8618
8618
|
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);
|
|
8619
8619
|
if (isActive) {
|
|
8620
8620
|
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);
|
|
8621
8621
|
}
|
|
8622
|
-
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y",
|
|
8622
|
+
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);
|
|
8623
8623
|
if (isActive && fixedLegend) {
|
|
8624
8624
|
const groupKey = group.name.toLowerCase();
|
|
8625
8625
|
const isHidden = hiddenAttributes?.has(groupKey) ?? false;
|
|
8626
|
-
const eyeX = pillXOff + pillWidth +
|
|
8627
|
-
const eyeY = (
|
|
8626
|
+
const eyeX = pillXOff + pillWidth + LEGEND_EYE_GAP;
|
|
8627
|
+
const eyeY = (LEGEND_HEIGHT - LEGEND_EYE_SIZE) / 2;
|
|
8628
8628
|
const hitPad = 6;
|
|
8629
8629
|
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);
|
|
8630
|
-
eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width",
|
|
8630
|
+
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");
|
|
8631
8631
|
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");
|
|
8632
8632
|
}
|
|
8633
8633
|
if (isActive) {
|
|
8634
|
-
const eyeShift = fixedLegend ?
|
|
8634
|
+
const eyeShift = fixedLegend ? LEGEND_EYE_SIZE + LEGEND_EYE_GAP : 0;
|
|
8635
8635
|
let entryX = pillXOff + pillWidth + 4 + eyeShift;
|
|
8636
8636
|
for (const entry of group.entries) {
|
|
8637
8637
|
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
8638
|
-
entryG.append("circle").attr("cx", entryX +
|
|
8639
|
-
const textX = entryX +
|
|
8638
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
8639
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
8640
8640
|
const entryLabel = entry.value;
|
|
8641
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
8642
|
-
entryX = textX + entryLabel.length *
|
|
8641
|
+
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);
|
|
8642
|
+
entryX = textX + entryLabel.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
8643
8643
|
}
|
|
8644
8644
|
}
|
|
8645
8645
|
}
|
|
@@ -8678,7 +8678,7 @@ function renderOrgForExport(content, theme, palette) {
|
|
|
8678
8678
|
document.body.removeChild(container);
|
|
8679
8679
|
}
|
|
8680
8680
|
}
|
|
8681
|
-
var 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,
|
|
8681
|
+
var 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;
|
|
8682
8682
|
var init_renderer = __esm({
|
|
8683
8683
|
"src/org/renderer.ts"() {
|
|
8684
8684
|
"use strict";
|
|
@@ -8686,6 +8686,7 @@ var init_renderer = __esm({
|
|
|
8686
8686
|
init_color_utils();
|
|
8687
8687
|
init_parser4();
|
|
8688
8688
|
init_layout();
|
|
8689
|
+
init_legend_constants();
|
|
8689
8690
|
DIAGRAM_PADDING = 20;
|
|
8690
8691
|
MAX_SCALE = 3;
|
|
8691
8692
|
TITLE_HEIGHT = 30;
|
|
@@ -8705,22 +8706,7 @@ var init_renderer = __esm({
|
|
|
8705
8706
|
CONTAINER_HEADER_HEIGHT = 28;
|
|
8706
8707
|
COLLAPSE_BAR_HEIGHT = 6;
|
|
8707
8708
|
COLLAPSE_BAR_INSET = 0;
|
|
8708
|
-
LEGEND_HEIGHT2 = 28;
|
|
8709
|
-
LEGEND_PILL_PAD2 = 16;
|
|
8710
|
-
LEGEND_PILL_FONT_SIZE = 11;
|
|
8711
|
-
LEGEND_PILL_FONT_W2 = LEGEND_PILL_FONT_SIZE * 0.6;
|
|
8712
|
-
LEGEND_CAPSULE_PAD2 = 4;
|
|
8713
|
-
LEGEND_DOT_R2 = 4;
|
|
8714
|
-
LEGEND_ENTRY_FONT_SIZE = 10;
|
|
8715
|
-
LEGEND_ENTRY_FONT_W2 = LEGEND_ENTRY_FONT_SIZE * 0.6;
|
|
8716
|
-
LEGEND_ENTRY_DOT_GAP2 = 4;
|
|
8717
|
-
LEGEND_ENTRY_TRAIL2 = 8;
|
|
8718
|
-
LEGEND_GROUP_GAP2 = 12;
|
|
8719
|
-
LEGEND_EYE_SIZE2 = 14;
|
|
8720
|
-
LEGEND_EYE_GAP2 = 6;
|
|
8721
8709
|
LEGEND_FIXED_GAP = 8;
|
|
8722
|
-
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";
|
|
8723
|
-
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";
|
|
8724
8710
|
}
|
|
8725
8711
|
});
|
|
8726
8712
|
|
|
@@ -9145,23 +9131,17 @@ function layoutSitemap(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, e
|
|
|
9145
9131
|
const effectiveW = (g2) => activeTagGroup != null || allExpanded ? g2.width : g2.minifiedWidth;
|
|
9146
9132
|
if (visibleGroups.length > 0) {
|
|
9147
9133
|
const legendShift = LEGEND_HEIGHT3 + LEGEND_GROUP_GAP3;
|
|
9148
|
-
for (const n of layoutNodes) n.y += legendShift;
|
|
9149
|
-
for (const c of layoutContainers) c.y += legendShift;
|
|
9150
|
-
for (const e of layoutEdges) {
|
|
9151
|
-
for (const p of e.points) p.y += legendShift;
|
|
9152
|
-
}
|
|
9153
9134
|
const totalGroupsWidth = visibleGroups.reduce((s, g2) => s + effectiveW(g2), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP3;
|
|
9154
|
-
|
|
9135
|
+
const neededWidth = totalGroupsWidth + MARGIN2 * 2;
|
|
9136
|
+
if (neededWidth > totalWidth) totalWidth = neededWidth;
|
|
9137
|
+
let cx = (totalWidth - totalGroupsWidth) / 2;
|
|
9138
|
+
const legendY = totalHeight + LEGEND_GROUP_GAP3;
|
|
9155
9139
|
for (const g2 of visibleGroups) {
|
|
9156
9140
|
g2.x = cx;
|
|
9157
|
-
g2.y =
|
|
9141
|
+
g2.y = legendY;
|
|
9158
9142
|
cx += effectiveW(g2) + LEGEND_GROUP_GAP3;
|
|
9159
9143
|
}
|
|
9160
9144
|
totalHeight += legendShift;
|
|
9161
|
-
const neededWidth = totalGroupsWidth + MARGIN2 * 2;
|
|
9162
|
-
if (neededWidth > totalWidth) {
|
|
9163
|
-
totalWidth = neededWidth;
|
|
9164
|
-
}
|
|
9165
9145
|
}
|
|
9166
9146
|
return {
|
|
9167
9147
|
nodes: layoutNodes,
|
|
@@ -9356,25 +9336,26 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
9356
9336
|
const height = exportDims?.height ?? container.clientHeight;
|
|
9357
9337
|
if (width <= 0 || height <= 0) return;
|
|
9358
9338
|
const hasLegend = layout.legend.length > 0;
|
|
9359
|
-
const layoutLegendShift =
|
|
9339
|
+
const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
9360
9340
|
const fixedLegend = !exportDims && hasLegend;
|
|
9361
9341
|
const fixedTitle = fixedLegend && !!parsed.title;
|
|
9362
9342
|
const fixedTitleH = fixedTitle ? TITLE_HEIGHT2 : 0;
|
|
9363
|
-
const legendReserveH = fixedLegend ?
|
|
9364
|
-
const
|
|
9343
|
+
const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP2 : 0;
|
|
9344
|
+
const fixedReserveTop = fixedTitleH;
|
|
9345
|
+
const fixedReserveBottom = legendReserveH;
|
|
9365
9346
|
const titleOffset = !fixedTitle && parsed.title ? TITLE_HEIGHT2 : 0;
|
|
9366
9347
|
const diagramW = layout.width;
|
|
9367
9348
|
let diagramH = layout.height + titleOffset;
|
|
9368
9349
|
if (fixedLegend) {
|
|
9369
9350
|
diagramH -= layoutLegendShift;
|
|
9370
9351
|
}
|
|
9371
|
-
const availH = height - DIAGRAM_PADDING2 * 2 -
|
|
9352
|
+
const availH = height - DIAGRAM_PADDING2 * 2 - fixedReserveTop - fixedReserveBottom;
|
|
9372
9353
|
const scaleX = (width - DIAGRAM_PADDING2 * 2) / diagramW;
|
|
9373
9354
|
const scaleY = availH / diagramH;
|
|
9374
9355
|
const scale = Math.min(MAX_SCALE2, scaleX, scaleY);
|
|
9375
9356
|
const scaledW = diagramW * scale;
|
|
9376
9357
|
const offsetX = (width - scaledW) / 2;
|
|
9377
|
-
const offsetY = DIAGRAM_PADDING2 +
|
|
9358
|
+
const offsetY = DIAGRAM_PADDING2 + fixedReserveTop;
|
|
9378
9359
|
const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("font-family", FONT_FAMILY);
|
|
9379
9360
|
const defs = svg.append("defs");
|
|
9380
9361
|
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);
|
|
@@ -9515,7 +9496,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
9515
9496
|
titleEl.text(parsed.title);
|
|
9516
9497
|
}
|
|
9517
9498
|
if (fixedLegend) {
|
|
9518
|
-
const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr("transform", `translate(0, ${DIAGRAM_PADDING2
|
|
9499
|
+
const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr("transform", `translate(0, ${height - DIAGRAM_PADDING2 - LEGEND_HEIGHT})`);
|
|
9500
|
+
if (activeTagGroup) {
|
|
9501
|
+
legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
9502
|
+
}
|
|
9519
9503
|
renderLegend(legendParent, layout.legend, palette, isDark, activeTagGroup, width, hiddenAttributes);
|
|
9520
9504
|
}
|
|
9521
9505
|
}
|
|
@@ -9527,49 +9511,49 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
|
|
|
9527
9511
|
if (fixedWidth != null && visibleGroups.length > 0) {
|
|
9528
9512
|
fixedPositions = /* @__PURE__ */ new Map();
|
|
9529
9513
|
const effectiveW = (g) => activeTagGroup != null ? g.width : g.minifiedWidth;
|
|
9530
|
-
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) *
|
|
9514
|
+
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
9531
9515
|
let cx = (fixedWidth - totalW) / 2;
|
|
9532
9516
|
for (const g of visibleGroups) {
|
|
9533
9517
|
fixedPositions.set(g.name, cx);
|
|
9534
|
-
cx += effectiveW(g) +
|
|
9518
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
9535
9519
|
}
|
|
9536
9520
|
}
|
|
9537
9521
|
for (const group of visibleGroups) {
|
|
9538
9522
|
const isActive = activeTagGroup != null;
|
|
9539
|
-
const pillW = group.name.length *
|
|
9523
|
+
const pillW = group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
9540
9524
|
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
9541
9525
|
const gY = fixedPositions ? 0 : group.y;
|
|
9542
9526
|
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");
|
|
9543
9527
|
if (isActive) {
|
|
9544
|
-
legendG.append("rect").attr("width", group.width).attr("height",
|
|
9528
|
+
legendG.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
9545
9529
|
}
|
|
9546
|
-
const pillXOff = isActive ?
|
|
9547
|
-
const pillYOff = isActive ?
|
|
9548
|
-
const pillH =
|
|
9530
|
+
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
9531
|
+
const pillYOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
9532
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
9549
9533
|
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);
|
|
9550
9534
|
if (isActive) {
|
|
9551
9535
|
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);
|
|
9552
9536
|
}
|
|
9553
|
-
legendG.append("text").attr("x", pillXOff + pillW / 2).attr("y",
|
|
9537
|
+
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);
|
|
9554
9538
|
if (isActive && fixedWidth != null) {
|
|
9555
9539
|
const groupKey = group.name.toLowerCase();
|
|
9556
9540
|
const isHidden = hiddenAttributes?.has(groupKey) ?? false;
|
|
9557
|
-
const eyeX = pillXOff + pillW +
|
|
9558
|
-
const eyeY = (
|
|
9541
|
+
const eyeX = pillXOff + pillW + LEGEND_EYE_GAP;
|
|
9542
|
+
const eyeY = (LEGEND_HEIGHT - LEGEND_EYE_SIZE) / 2;
|
|
9559
9543
|
const hitPad = 6;
|
|
9560
9544
|
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);
|
|
9561
|
-
eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width",
|
|
9562
|
-
eyeG.append("path").attr("d", isHidden ?
|
|
9545
|
+
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");
|
|
9546
|
+
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");
|
|
9563
9547
|
}
|
|
9564
9548
|
if (isActive) {
|
|
9565
|
-
const eyeShift = fixedWidth != null ?
|
|
9549
|
+
const eyeShift = fixedWidth != null ? LEGEND_EYE_SIZE + LEGEND_EYE_GAP : 0;
|
|
9566
9550
|
let entryX = pillXOff + pillW + 4 + eyeShift;
|
|
9567
9551
|
for (const entry of group.entries) {
|
|
9568
9552
|
const entryG = legendG.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
9569
|
-
entryG.append("circle").attr("cx", entryX +
|
|
9570
|
-
const textX = entryX +
|
|
9571
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
9572
|
-
entryX = textX + entry.value.length *
|
|
9553
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
9554
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
9555
|
+
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);
|
|
9556
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
9573
9557
|
}
|
|
9574
9558
|
}
|
|
9575
9559
|
}
|
|
@@ -9615,12 +9599,13 @@ async function renderSitemapForExport(content, theme, palette) {
|
|
|
9615
9599
|
const brandColor = theme === "transparent" ? "#888" : effectivePalette.textMuted;
|
|
9616
9600
|
return injectBranding2(svgHtml, brandColor);
|
|
9617
9601
|
}
|
|
9618
|
-
var 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,
|
|
9602
|
+
var 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;
|
|
9619
9603
|
var init_renderer2 = __esm({
|
|
9620
9604
|
"src/sitemap/renderer.ts"() {
|
|
9621
9605
|
"use strict";
|
|
9622
9606
|
init_fonts();
|
|
9623
9607
|
init_color_utils();
|
|
9608
|
+
init_legend_constants();
|
|
9624
9609
|
DIAGRAM_PADDING2 = 20;
|
|
9625
9610
|
MAX_SCALE2 = 3;
|
|
9626
9611
|
TITLE_HEIGHT2 = 30;
|
|
@@ -9642,23 +9627,8 @@ var init_renderer2 = __esm({
|
|
|
9642
9627
|
ARROWHEAD_H = 7;
|
|
9643
9628
|
EDGE_LABEL_FONT_SIZE = 11;
|
|
9644
9629
|
COLLAPSE_BAR_HEIGHT2 = 6;
|
|
9645
|
-
LEGEND_HEIGHT4 = 28;
|
|
9646
9630
|
LEGEND_FIXED_GAP2 = 8;
|
|
9647
|
-
LEGEND_PILL_PAD4 = 16;
|
|
9648
|
-
LEGEND_PILL_FONT_SIZE2 = 11;
|
|
9649
|
-
LEGEND_PILL_FONT_W4 = LEGEND_PILL_FONT_SIZE2 * 0.6;
|
|
9650
|
-
LEGEND_CAPSULE_PAD4 = 4;
|
|
9651
|
-
LEGEND_DOT_R4 = 4;
|
|
9652
|
-
LEGEND_ENTRY_FONT_SIZE2 = 10;
|
|
9653
|
-
LEGEND_ENTRY_FONT_W4 = LEGEND_ENTRY_FONT_SIZE2 * 0.6;
|
|
9654
|
-
LEGEND_ENTRY_DOT_GAP4 = 4;
|
|
9655
|
-
LEGEND_ENTRY_TRAIL4 = 8;
|
|
9656
|
-
LEGEND_GROUP_GAP4 = 12;
|
|
9657
|
-
LEGEND_EYE_SIZE4 = 14;
|
|
9658
|
-
LEGEND_EYE_GAP4 = 6;
|
|
9659
9631
|
lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
|
|
9660
|
-
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";
|
|
9661
|
-
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";
|
|
9662
9632
|
}
|
|
9663
9633
|
});
|
|
9664
9634
|
|
|
@@ -9861,8 +9831,7 @@ function resolveCardTagColor(card, tagGroups, activeTagGroup) {
|
|
|
9861
9831
|
return entry?.color;
|
|
9862
9832
|
}
|
|
9863
9833
|
function computeLayout(parsed, _palette) {
|
|
9864
|
-
const
|
|
9865
|
-
const headerHeight = hasHeader ? Math.max(TITLE_HEIGHT3, LEGEND_HEIGHT5) + 8 : 0;
|
|
9834
|
+
const headerHeight = parsed.title ? TITLE_HEIGHT3 + 8 : 0;
|
|
9866
9835
|
const startY = DIAGRAM_PADDING3 + headerHeight;
|
|
9867
9836
|
const charWidth = CARD_TITLE_FONT_SIZE * 0.6;
|
|
9868
9837
|
const columnLayouts = [];
|
|
@@ -9919,7 +9888,8 @@ function computeLayout(parsed, _palette) {
|
|
|
9919
9888
|
currentX += cl.width + COLUMN_GAP;
|
|
9920
9889
|
}
|
|
9921
9890
|
const totalWidth = currentX - COLUMN_GAP + DIAGRAM_PADDING3;
|
|
9922
|
-
const
|
|
9891
|
+
const legendSpace = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT : 0;
|
|
9892
|
+
const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3 + legendSpace;
|
|
9923
9893
|
return { columns: columnLayouts, totalWidth, totalHeight };
|
|
9924
9894
|
}
|
|
9925
9895
|
function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exportDims, activeTagGroup) {
|
|
@@ -9932,42 +9902,45 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
|
|
|
9932
9902
|
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);
|
|
9933
9903
|
}
|
|
9934
9904
|
if (parsed.tagGroups.length > 0) {
|
|
9935
|
-
const legendY =
|
|
9936
|
-
|
|
9937
|
-
let legendX = DIAGRAM_PADDING3 + titleTextWidth;
|
|
9905
|
+
const legendY = height - LEGEND_HEIGHT;
|
|
9906
|
+
let legendX = DIAGRAM_PADDING3;
|
|
9938
9907
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
9939
|
-
const capsulePad =
|
|
9908
|
+
const capsulePad = LEGEND_CAPSULE_PAD;
|
|
9909
|
+
const legendContainer = svg.append("g").attr("class", "kanban-legend");
|
|
9910
|
+
if (activeTagGroup) {
|
|
9911
|
+
legendContainer.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
9912
|
+
}
|
|
9940
9913
|
for (const group of parsed.tagGroups) {
|
|
9941
9914
|
const isActive = activeTagGroup?.toLowerCase() === group.name.toLowerCase();
|
|
9942
9915
|
if (activeTagGroup != null && !isActive) continue;
|
|
9943
|
-
const pillTextWidth = group.name.length *
|
|
9916
|
+
const pillTextWidth = group.name.length * LEGEND_PILL_FONT_SIZE * 0.6;
|
|
9944
9917
|
const pillWidth = pillTextWidth + 16;
|
|
9945
9918
|
let capsuleContentWidth = pillWidth;
|
|
9946
9919
|
if (isActive) {
|
|
9947
9920
|
capsuleContentWidth += 4;
|
|
9948
9921
|
for (const entry of group.entries) {
|
|
9949
|
-
capsuleContentWidth +=
|
|
9922
|
+
capsuleContentWidth += LEGEND_DOT_R * 2 + 4 + entry.value.length * LEGEND_ENTRY_FONT_SIZE * 0.6 + 8;
|
|
9950
9923
|
}
|
|
9951
9924
|
}
|
|
9952
9925
|
const capsuleWidth = capsuleContentWidth + capsulePad * 2;
|
|
9953
9926
|
if (isActive) {
|
|
9954
|
-
|
|
9927
|
+
legendContainer.append("rect").attr("x", legendX).attr("y", legendY).attr("width", capsuleWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
9955
9928
|
}
|
|
9956
9929
|
const pillX = legendX + (isActive ? capsulePad : 0);
|
|
9957
9930
|
const pillBg = isActive ? palette.bg : groupBg;
|
|
9958
|
-
|
|
9931
|
+
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());
|
|
9959
9932
|
if (isActive) {
|
|
9960
|
-
|
|
9933
|
+
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);
|
|
9961
9934
|
}
|
|
9962
|
-
|
|
9935
|
+
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);
|
|
9963
9936
|
if (isActive) {
|
|
9964
9937
|
let entryX = pillX + pillWidth + 4;
|
|
9965
9938
|
for (const entry of group.entries) {
|
|
9966
|
-
const entryG =
|
|
9967
|
-
entryG.append("circle").attr("cx", entryX +
|
|
9968
|
-
const entryTextX = entryX +
|
|
9969
|
-
entryG.append("text").attr("x", entryTextX).attr("y", legendY +
|
|
9970
|
-
entryX = entryTextX + entry.value.length *
|
|
9939
|
+
const entryG = legendContainer.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
9940
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", legendY + LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
9941
|
+
const entryTextX = entryX + LEGEND_DOT_R * 2 + 4;
|
|
9942
|
+
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);
|
|
9943
|
+
entryX = entryTextX + entry.value.length * LEGEND_ENTRY_FONT_SIZE * 0.6 + 8;
|
|
9971
9944
|
}
|
|
9972
9945
|
legendX += capsuleWidth + 12;
|
|
9973
9946
|
} else {
|
|
@@ -10054,7 +10027,7 @@ function renderKanbanForExport(content, theme, palette) {
|
|
|
10054
10027
|
const svgEl = container.querySelector("svg");
|
|
10055
10028
|
return svgEl?.outerHTML ?? "";
|
|
10056
10029
|
}
|
|
10057
|
-
var 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
|
|
10030
|
+
var 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;
|
|
10058
10031
|
var init_renderer3 = __esm({
|
|
10059
10032
|
"src/kanban/renderer.ts"() {
|
|
10060
10033
|
"use strict";
|
|
@@ -10063,6 +10036,7 @@ var init_renderer3 = __esm({
|
|
|
10063
10036
|
init_inline_markdown();
|
|
10064
10037
|
init_parser5();
|
|
10065
10038
|
init_mutations();
|
|
10039
|
+
init_legend_constants();
|
|
10066
10040
|
DIAGRAM_PADDING3 = 20;
|
|
10067
10041
|
COLUMN_GAP = 16;
|
|
10068
10042
|
COLUMN_HEADER_HEIGHT = 36;
|
|
@@ -10084,10 +10058,6 @@ var init_renderer3 = __esm({
|
|
|
10084
10058
|
WIP_FONT_SIZE = 10;
|
|
10085
10059
|
COLUMN_RADIUS = 8;
|
|
10086
10060
|
COLUMN_HEADER_RADIUS = 8;
|
|
10087
|
-
LEGEND_HEIGHT5 = 28;
|
|
10088
|
-
LEGEND_FONT_SIZE = 11;
|
|
10089
|
-
LEGEND_DOT_R5 = 4;
|
|
10090
|
-
LEGEND_ENTRY_FONT_SIZE3 = 10;
|
|
10091
10061
|
}
|
|
10092
10062
|
});
|
|
10093
10063
|
|
|
@@ -10506,110 +10476,211 @@ function computeNodeDimensions2(table) {
|
|
|
10506
10476
|
const height = headerHeight + columnsHeight + (columnsHeight === 0 ? 4 : 0);
|
|
10507
10477
|
return { width, height, headerHeight, columnsHeight };
|
|
10508
10478
|
}
|
|
10479
|
+
function findConnectedComponents(tableIds, relationships) {
|
|
10480
|
+
const adj = /* @__PURE__ */ new Map();
|
|
10481
|
+
for (const id of tableIds) adj.set(id, /* @__PURE__ */ new Set());
|
|
10482
|
+
for (const rel of relationships) {
|
|
10483
|
+
adj.get(rel.source)?.add(rel.target);
|
|
10484
|
+
adj.get(rel.target)?.add(rel.source);
|
|
10485
|
+
}
|
|
10486
|
+
const visited = /* @__PURE__ */ new Set();
|
|
10487
|
+
const components = [];
|
|
10488
|
+
for (const id of tableIds) {
|
|
10489
|
+
if (visited.has(id)) continue;
|
|
10490
|
+
const comp = [];
|
|
10491
|
+
const queue = [id];
|
|
10492
|
+
while (queue.length > 0) {
|
|
10493
|
+
const cur = queue.shift();
|
|
10494
|
+
if (visited.has(cur)) continue;
|
|
10495
|
+
visited.add(cur);
|
|
10496
|
+
comp.push(cur);
|
|
10497
|
+
for (const nb of adj.get(cur) ?? []) {
|
|
10498
|
+
if (!visited.has(nb)) queue.push(nb);
|
|
10499
|
+
}
|
|
10500
|
+
}
|
|
10501
|
+
components.push(comp);
|
|
10502
|
+
}
|
|
10503
|
+
return components;
|
|
10504
|
+
}
|
|
10505
|
+
function layoutComponent(tables, rels, dimMap) {
|
|
10506
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
10507
|
+
const edgePoints = /* @__PURE__ */ new Map();
|
|
10508
|
+
if (tables.length === 1) {
|
|
10509
|
+
const dims = dimMap.get(tables[0].id);
|
|
10510
|
+
nodePositions.set(tables[0].id, { x: dims.width / 2, y: dims.height / 2, ...dims });
|
|
10511
|
+
return { nodePositions, edgePoints, width: dims.width, height: dims.height };
|
|
10512
|
+
}
|
|
10513
|
+
const g = new dagre3.graphlib.Graph({ multigraph: true });
|
|
10514
|
+
g.setGraph({ rankdir: "LR", nodesep: 40, ranksep: 80, edgesep: 20 });
|
|
10515
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
10516
|
+
for (const table of tables) {
|
|
10517
|
+
const dims = dimMap.get(table.id);
|
|
10518
|
+
g.setNode(table.id, { width: dims.width, height: dims.height });
|
|
10519
|
+
}
|
|
10520
|
+
for (const rel of rels) {
|
|
10521
|
+
g.setEdge(rel.source, rel.target, { label: rel.label ?? "" }, String(rel.lineNumber));
|
|
10522
|
+
}
|
|
10523
|
+
dagre3.layout(g);
|
|
10524
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
10525
|
+
for (const table of tables) {
|
|
10526
|
+
const pos = g.node(table.id);
|
|
10527
|
+
const dims = dimMap.get(table.id);
|
|
10528
|
+
minX = Math.min(minX, pos.x - dims.width / 2);
|
|
10529
|
+
minY = Math.min(minY, pos.y - dims.height / 2);
|
|
10530
|
+
maxX = Math.max(maxX, pos.x + dims.width / 2);
|
|
10531
|
+
maxY = Math.max(maxY, pos.y + dims.height / 2);
|
|
10532
|
+
}
|
|
10533
|
+
for (const rel of rels) {
|
|
10534
|
+
const ed = g.edge(rel.source, rel.target, String(rel.lineNumber));
|
|
10535
|
+
for (const pt of ed?.points ?? []) {
|
|
10536
|
+
minX = Math.min(minX, pt.x);
|
|
10537
|
+
minY = Math.min(minY, pt.y);
|
|
10538
|
+
maxX = Math.max(maxX, pt.x);
|
|
10539
|
+
maxY = Math.max(maxY, pt.y);
|
|
10540
|
+
}
|
|
10541
|
+
if (rel.label && (ed?.points ?? []).length > 0) {
|
|
10542
|
+
const pts = ed.points;
|
|
10543
|
+
const mid = pts[Math.floor(pts.length / 2)];
|
|
10544
|
+
const hw = (rel.label.length * 7 + 8) / 2;
|
|
10545
|
+
minX = Math.min(minX, mid.x - hw);
|
|
10546
|
+
maxX = Math.max(maxX, mid.x + hw);
|
|
10547
|
+
}
|
|
10548
|
+
}
|
|
10549
|
+
for (const table of tables) {
|
|
10550
|
+
const pos = g.node(table.id);
|
|
10551
|
+
const dims = dimMap.get(table.id);
|
|
10552
|
+
nodePositions.set(table.id, {
|
|
10553
|
+
x: pos.x - minX,
|
|
10554
|
+
y: pos.y - minY,
|
|
10555
|
+
...dims
|
|
10556
|
+
});
|
|
10557
|
+
}
|
|
10558
|
+
for (const rel of rels) {
|
|
10559
|
+
const ed = g.edge(rel.source, rel.target, String(rel.lineNumber));
|
|
10560
|
+
edgePoints.set(
|
|
10561
|
+
rel.lineNumber,
|
|
10562
|
+
(ed?.points ?? []).map((pt) => ({ x: pt.x - minX, y: pt.y - minY }))
|
|
10563
|
+
);
|
|
10564
|
+
}
|
|
10565
|
+
return {
|
|
10566
|
+
nodePositions,
|
|
10567
|
+
edgePoints,
|
|
10568
|
+
width: Math.max(0, maxX - minX),
|
|
10569
|
+
height: Math.max(0, maxY - minY)
|
|
10570
|
+
};
|
|
10571
|
+
}
|
|
10572
|
+
function packComponents(items) {
|
|
10573
|
+
if (items.length === 0) return [];
|
|
10574
|
+
const sorted = [...items].sort((a, b) => {
|
|
10575
|
+
const aConnected = a.compIds.length > 1 ? 1 : 0;
|
|
10576
|
+
const bConnected = b.compIds.length > 1 ? 1 : 0;
|
|
10577
|
+
if (aConnected !== bConnected) return bConnected - aConnected;
|
|
10578
|
+
return b.compLayout.height - a.compLayout.height;
|
|
10579
|
+
});
|
|
10580
|
+
const totalArea = items.reduce(
|
|
10581
|
+
(s, c) => s + (c.compLayout.width || MIN_WIDTH2) * (c.compLayout.height || HEADER_BASE2),
|
|
10582
|
+
0
|
|
10583
|
+
);
|
|
10584
|
+
const targetW = Math.max(
|
|
10585
|
+
Math.sqrt(totalArea) * 1.5,
|
|
10586
|
+
sorted[0].compLayout.width
|
|
10587
|
+
// at least as wide as the widest component
|
|
10588
|
+
);
|
|
10589
|
+
const placements = [];
|
|
10590
|
+
let curX = 0;
|
|
10591
|
+
let curY = 0;
|
|
10592
|
+
let rowH = 0;
|
|
10593
|
+
for (const item of sorted) {
|
|
10594
|
+
const w = item.compLayout.width || MIN_WIDTH2;
|
|
10595
|
+
const h = item.compLayout.height || HEADER_BASE2;
|
|
10596
|
+
if (curX > 0 && curX + w > targetW) {
|
|
10597
|
+
curY += rowH + COMP_GAP;
|
|
10598
|
+
curX = 0;
|
|
10599
|
+
rowH = 0;
|
|
10600
|
+
}
|
|
10601
|
+
placements.push({ compIds: item.compIds, compLayout: item.compLayout, offsetX: curX, offsetY: curY });
|
|
10602
|
+
curX += w + COMP_GAP;
|
|
10603
|
+
rowH = Math.max(rowH, h);
|
|
10604
|
+
}
|
|
10605
|
+
return placements;
|
|
10606
|
+
}
|
|
10509
10607
|
function layoutERDiagram(parsed) {
|
|
10510
10608
|
if (parsed.tables.length === 0) {
|
|
10511
10609
|
return { nodes: [], edges: [], width: 0, height: 0 };
|
|
10512
10610
|
}
|
|
10513
|
-
const g = new dagre3.graphlib.Graph();
|
|
10514
|
-
g.setGraph({
|
|
10515
|
-
rankdir: "TB",
|
|
10516
|
-
nodesep: 60,
|
|
10517
|
-
ranksep: 80,
|
|
10518
|
-
edgesep: 20
|
|
10519
|
-
});
|
|
10520
|
-
g.setDefaultEdgeLabel(() => ({}));
|
|
10521
10611
|
const dimMap = /* @__PURE__ */ new Map();
|
|
10522
10612
|
for (const table of parsed.tables) {
|
|
10523
|
-
|
|
10524
|
-
dimMap.set(table.id, dims);
|
|
10525
|
-
g.setNode(table.id, {
|
|
10526
|
-
label: table.name,
|
|
10527
|
-
width: dims.width,
|
|
10528
|
-
height: dims.height
|
|
10529
|
-
});
|
|
10613
|
+
dimMap.set(table.id, computeNodeDimensions2(table));
|
|
10530
10614
|
}
|
|
10531
|
-
|
|
10532
|
-
|
|
10615
|
+
const compIdSets = findConnectedComponents(
|
|
10616
|
+
parsed.tables.map((t) => t.id),
|
|
10617
|
+
parsed.relationships
|
|
10618
|
+
);
|
|
10619
|
+
const tableById = new Map(parsed.tables.map((t) => [t.id, t]));
|
|
10620
|
+
const componentItems = compIdSets.map((ids) => {
|
|
10621
|
+
const tables = ids.map((id) => tableById.get(id));
|
|
10622
|
+
const rels = parsed.relationships.filter((r) => ids.includes(r.source));
|
|
10623
|
+
return { compIds: ids, compLayout: layoutComponent(tables, rels, dimMap) };
|
|
10624
|
+
});
|
|
10625
|
+
const packed = packComponents(componentItems);
|
|
10626
|
+
const placementByTableId = /* @__PURE__ */ new Map();
|
|
10627
|
+
for (const p of packed) {
|
|
10628
|
+
for (const id of p.compIds) placementByTableId.set(id, p);
|
|
10629
|
+
}
|
|
10630
|
+
const placementByRelLine = /* @__PURE__ */ new Map();
|
|
10631
|
+
for (const p of packed) {
|
|
10632
|
+
for (const lineNum of p.compLayout.edgePoints.keys()) {
|
|
10633
|
+
placementByRelLine.set(lineNum, p);
|
|
10634
|
+
}
|
|
10533
10635
|
}
|
|
10534
|
-
dagre3.layout(g);
|
|
10535
10636
|
const layoutNodes = parsed.tables.map((table) => {
|
|
10536
|
-
const
|
|
10537
|
-
const
|
|
10637
|
+
const p = placementByTableId.get(table.id);
|
|
10638
|
+
const pos = p.compLayout.nodePositions.get(table.id);
|
|
10538
10639
|
return {
|
|
10539
10640
|
...table,
|
|
10540
|
-
x: pos.x,
|
|
10541
|
-
y: pos.y,
|
|
10542
|
-
width:
|
|
10543
|
-
height:
|
|
10544
|
-
headerHeight:
|
|
10545
|
-
columnsHeight:
|
|
10641
|
+
x: pos.x + p.offsetX + HALF_MARGIN,
|
|
10642
|
+
y: pos.y + p.offsetY + HALF_MARGIN,
|
|
10643
|
+
width: pos.width,
|
|
10644
|
+
height: pos.height,
|
|
10645
|
+
headerHeight: pos.headerHeight,
|
|
10646
|
+
columnsHeight: pos.columnsHeight
|
|
10546
10647
|
};
|
|
10547
10648
|
});
|
|
10548
10649
|
const layoutEdges = parsed.relationships.map((rel) => {
|
|
10549
|
-
const
|
|
10650
|
+
const p = placementByRelLine.get(rel.lineNumber);
|
|
10651
|
+
const pts = p?.compLayout.edgePoints.get(rel.lineNumber) ?? [];
|
|
10550
10652
|
return {
|
|
10551
10653
|
source: rel.source,
|
|
10552
10654
|
target: rel.target,
|
|
10553
10655
|
cardinality: rel.cardinality,
|
|
10554
|
-
points:
|
|
10656
|
+
points: pts.map((pt) => ({
|
|
10657
|
+
x: pt.x + (p?.offsetX ?? 0) + HALF_MARGIN,
|
|
10658
|
+
y: pt.y + (p?.offsetY ?? 0) + HALF_MARGIN
|
|
10659
|
+
})),
|
|
10555
10660
|
label: rel.label,
|
|
10556
10661
|
lineNumber: rel.lineNumber
|
|
10557
10662
|
};
|
|
10558
10663
|
});
|
|
10559
|
-
let minX = Infinity;
|
|
10560
|
-
let minY = Infinity;
|
|
10561
10664
|
let maxX = 0;
|
|
10562
10665
|
let maxY = 0;
|
|
10563
10666
|
for (const node of layoutNodes) {
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
const top = node.y - node.height / 2;
|
|
10567
|
-
const bottom = node.y + node.height / 2;
|
|
10568
|
-
if (left < minX) minX = left;
|
|
10569
|
-
if (right > maxX) maxX = right;
|
|
10570
|
-
if (top < minY) minY = top;
|
|
10571
|
-
if (bottom > maxY) maxY = bottom;
|
|
10667
|
+
maxX = Math.max(maxX, node.x + node.width / 2);
|
|
10668
|
+
maxY = Math.max(maxY, node.y + node.height / 2);
|
|
10572
10669
|
}
|
|
10573
10670
|
for (const edge of layoutEdges) {
|
|
10574
10671
|
for (const pt of edge.points) {
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
if (pt.y < minY) minY = pt.y;
|
|
10578
|
-
if (pt.y > maxY) maxY = pt.y;
|
|
10579
|
-
}
|
|
10580
|
-
if (edge.label && edge.points.length > 0) {
|
|
10581
|
-
const midPt = edge.points[Math.floor(edge.points.length / 2)];
|
|
10582
|
-
const labelHalfW = (edge.label.length * 7 + 8) / 2;
|
|
10583
|
-
if (midPt.x + labelHalfW > maxX) maxX = midPt.x + labelHalfW;
|
|
10584
|
-
if (midPt.x - labelHalfW < minX) minX = midPt.x - labelHalfW;
|
|
10672
|
+
maxX = Math.max(maxX, pt.x);
|
|
10673
|
+
maxY = Math.max(maxY, pt.y);
|
|
10585
10674
|
}
|
|
10586
10675
|
}
|
|
10587
|
-
const EDGE_MARGIN2 = 60;
|
|
10588
|
-
const HALF_MARGIN = EDGE_MARGIN2 / 2;
|
|
10589
|
-
const shiftX = -minX + HALF_MARGIN;
|
|
10590
|
-
const shiftY = -minY + HALF_MARGIN;
|
|
10591
|
-
for (const node of layoutNodes) {
|
|
10592
|
-
node.x += shiftX;
|
|
10593
|
-
node.y += shiftY;
|
|
10594
|
-
}
|
|
10595
|
-
for (const edge of layoutEdges) {
|
|
10596
|
-
for (const pt of edge.points) {
|
|
10597
|
-
pt.x += shiftX;
|
|
10598
|
-
pt.y += shiftY;
|
|
10599
|
-
}
|
|
10600
|
-
}
|
|
10601
|
-
maxX += shiftX;
|
|
10602
|
-
maxY += shiftY;
|
|
10603
|
-
const totalWidth = maxX + HALF_MARGIN;
|
|
10604
|
-
const totalHeight = maxY + HALF_MARGIN;
|
|
10605
10676
|
return {
|
|
10606
10677
|
nodes: layoutNodes,
|
|
10607
10678
|
edges: layoutEdges,
|
|
10608
|
-
width:
|
|
10609
|
-
height:
|
|
10679
|
+
width: maxX + HALF_MARGIN,
|
|
10680
|
+
height: maxY + HALF_MARGIN
|
|
10610
10681
|
};
|
|
10611
10682
|
}
|
|
10612
|
-
var MIN_WIDTH2, CHAR_WIDTH4, PADDING_X2, HEADER_BASE2, MEMBER_LINE_HEIGHT3, COMPARTMENT_PADDING_Y3, SEPARATOR_HEIGHT2;
|
|
10683
|
+
var MIN_WIDTH2, CHAR_WIDTH4, PADDING_X2, HEADER_BASE2, MEMBER_LINE_HEIGHT3, COMPARTMENT_PADDING_Y3, SEPARATOR_HEIGHT2, HALF_MARGIN, COMP_GAP;
|
|
10613
10684
|
var init_layout4 = __esm({
|
|
10614
10685
|
"src/er/layout.ts"() {
|
|
10615
10686
|
"use strict";
|
|
@@ -10620,6 +10691,135 @@ var init_layout4 = __esm({
|
|
|
10620
10691
|
MEMBER_LINE_HEIGHT3 = 18;
|
|
10621
10692
|
COMPARTMENT_PADDING_Y3 = 8;
|
|
10622
10693
|
SEPARATOR_HEIGHT2 = 1;
|
|
10694
|
+
HALF_MARGIN = 30;
|
|
10695
|
+
COMP_GAP = 60;
|
|
10696
|
+
}
|
|
10697
|
+
});
|
|
10698
|
+
|
|
10699
|
+
// src/er/classify.ts
|
|
10700
|
+
function classifyEREntities(tables, relationships) {
|
|
10701
|
+
const result = /* @__PURE__ */ new Map();
|
|
10702
|
+
if (tables.length === 0) return result;
|
|
10703
|
+
const indegreeMap = {};
|
|
10704
|
+
for (const t of tables) indegreeMap[t.id] = 0;
|
|
10705
|
+
for (const rel of relationships) {
|
|
10706
|
+
if (rel.source === rel.target) continue;
|
|
10707
|
+
if (rel.cardinality.from === "1" && rel.cardinality.to !== "1") {
|
|
10708
|
+
indegreeMap[rel.source] = (indegreeMap[rel.source] ?? 0) + 1;
|
|
10709
|
+
}
|
|
10710
|
+
if (rel.cardinality.to === "1" && rel.cardinality.from !== "1") {
|
|
10711
|
+
indegreeMap[rel.target] = (indegreeMap[rel.target] ?? 0) + 1;
|
|
10712
|
+
}
|
|
10713
|
+
}
|
|
10714
|
+
const tableStarNeighbors = /* @__PURE__ */ new Map();
|
|
10715
|
+
for (const rel of relationships) {
|
|
10716
|
+
if (rel.source === rel.target) continue;
|
|
10717
|
+
if (rel.cardinality.from === "*") {
|
|
10718
|
+
if (!tableStarNeighbors.has(rel.source)) tableStarNeighbors.set(rel.source, /* @__PURE__ */ new Set());
|
|
10719
|
+
tableStarNeighbors.get(rel.source).add(rel.target);
|
|
10720
|
+
}
|
|
10721
|
+
if (rel.cardinality.to === "*") {
|
|
10722
|
+
if (!tableStarNeighbors.has(rel.target)) tableStarNeighbors.set(rel.target, /* @__PURE__ */ new Set());
|
|
10723
|
+
tableStarNeighbors.get(rel.target).add(rel.source);
|
|
10724
|
+
}
|
|
10725
|
+
}
|
|
10726
|
+
const mmParticipants = /* @__PURE__ */ new Set();
|
|
10727
|
+
for (const [id, neighbors] of tableStarNeighbors) {
|
|
10728
|
+
if (neighbors.size >= 2) mmParticipants.add(id);
|
|
10729
|
+
}
|
|
10730
|
+
const indegreeValues = Object.values(indegreeMap);
|
|
10731
|
+
const mean = indegreeValues.reduce((a, b) => a + b, 0) / indegreeValues.length;
|
|
10732
|
+
const variance = indegreeValues.reduce((a, b) => a + (b - mean) ** 2, 0) / indegreeValues.length;
|
|
10733
|
+
const stddev = Math.sqrt(variance);
|
|
10734
|
+
const sorted = [...indegreeValues].sort((a, b) => a - b);
|
|
10735
|
+
const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
|
|
10736
|
+
for (const table of tables) {
|
|
10737
|
+
const id = table.id;
|
|
10738
|
+
const cols = table.columns;
|
|
10739
|
+
const fkCols = cols.filter((c) => c.constraints.includes("fk"));
|
|
10740
|
+
const pkFkCols = cols.filter(
|
|
10741
|
+
(c) => c.constraints.includes("pk") && c.constraints.includes("fk")
|
|
10742
|
+
);
|
|
10743
|
+
const fkCount = fkCols.length;
|
|
10744
|
+
const fkRatio = cols.length === 0 ? 0 : fkCount / cols.length;
|
|
10745
|
+
const indegree = indegreeMap[id] ?? 0;
|
|
10746
|
+
const nameLower = table.name.toLowerCase();
|
|
10747
|
+
const externalRels = relationships.filter(
|
|
10748
|
+
(r) => (r.source === id || r.target === id) && r.source !== r.target
|
|
10749
|
+
);
|
|
10750
|
+
const hasSelfRef = relationships.some((r) => r.source === id && r.target === id);
|
|
10751
|
+
const externalTargets = /* @__PURE__ */ new Set();
|
|
10752
|
+
for (const rel of externalRels) {
|
|
10753
|
+
externalTargets.add(rel.source === id ? rel.target : rel.source);
|
|
10754
|
+
}
|
|
10755
|
+
if (hasSelfRef && externalRels.length === 0) {
|
|
10756
|
+
result.set(id, "self-referential");
|
|
10757
|
+
continue;
|
|
10758
|
+
}
|
|
10759
|
+
const isInheritancePattern = pkFkCols.length >= 2 && externalTargets.size === 1;
|
|
10760
|
+
const junctionByRatio = fkRatio >= 0.6 && !isInheritancePattern;
|
|
10761
|
+
const junctionByCompositePk = pkFkCols.length >= 2 && externalTargets.size >= 2;
|
|
10762
|
+
const junctionByMm = mmParticipants.has(id);
|
|
10763
|
+
if (junctionByRatio || junctionByCompositePk || junctionByMm) {
|
|
10764
|
+
result.set(id, "junction");
|
|
10765
|
+
continue;
|
|
10766
|
+
}
|
|
10767
|
+
if (fkRatio >= 0.4 && fkRatio < 0.6 && pkFkCols.length < 2 && !mmParticipants.has(id)) {
|
|
10768
|
+
result.set(id, "ambiguous");
|
|
10769
|
+
continue;
|
|
10770
|
+
}
|
|
10771
|
+
const nameMatchesLookup = LOOKUP_NAME_SUFFIXES.some((s) => nameLower.endsWith(s));
|
|
10772
|
+
if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median) {
|
|
10773
|
+
result.set(id, "lookup");
|
|
10774
|
+
continue;
|
|
10775
|
+
}
|
|
10776
|
+
if (tables.length >= 6 && indegree > 0 && indegree > mean + 1.5 * stddev && indegree >= 2 * mean) {
|
|
10777
|
+
result.set(id, "hub");
|
|
10778
|
+
continue;
|
|
10779
|
+
}
|
|
10780
|
+
if (fkCount > 0) {
|
|
10781
|
+
result.set(id, "dependent");
|
|
10782
|
+
continue;
|
|
10783
|
+
}
|
|
10784
|
+
result.set(id, "core");
|
|
10785
|
+
}
|
|
10786
|
+
return result;
|
|
10787
|
+
}
|
|
10788
|
+
var ROLE_COLORS, ROLE_LABELS, ROLE_ORDER, LOOKUP_NAME_SUFFIXES;
|
|
10789
|
+
var init_classify = __esm({
|
|
10790
|
+
"src/er/classify.ts"() {
|
|
10791
|
+
"use strict";
|
|
10792
|
+
ROLE_COLORS = {
|
|
10793
|
+
core: "green",
|
|
10794
|
+
dependent: "blue",
|
|
10795
|
+
junction: "red",
|
|
10796
|
+
ambiguous: "purple",
|
|
10797
|
+
lookup: "yellow",
|
|
10798
|
+
hub: "orange",
|
|
10799
|
+
"self-referential": "teal",
|
|
10800
|
+
unclassified: "gray"
|
|
10801
|
+
};
|
|
10802
|
+
ROLE_LABELS = {
|
|
10803
|
+
core: "Core entity",
|
|
10804
|
+
dependent: "Dependent",
|
|
10805
|
+
junction: "Junction / M:M",
|
|
10806
|
+
ambiguous: "Bridge",
|
|
10807
|
+
lookup: "Lookup / Reference",
|
|
10808
|
+
hub: "Hub",
|
|
10809
|
+
"self-referential": "Self-referential",
|
|
10810
|
+
unclassified: "Unclassified"
|
|
10811
|
+
};
|
|
10812
|
+
ROLE_ORDER = [
|
|
10813
|
+
"core",
|
|
10814
|
+
"dependent",
|
|
10815
|
+
"junction",
|
|
10816
|
+
"ambiguous",
|
|
10817
|
+
"lookup",
|
|
10818
|
+
"hub",
|
|
10819
|
+
"self-referential",
|
|
10820
|
+
"unclassified"
|
|
10821
|
+
];
|
|
10822
|
+
LOOKUP_NAME_SUFFIXES = ["_type", "_status", "_code", "_category"];
|
|
10623
10823
|
}
|
|
10624
10824
|
});
|
|
10625
10825
|
|
|
@@ -10691,25 +10891,41 @@ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels) {
|
|
|
10691
10891
|
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);
|
|
10692
10892
|
}
|
|
10693
10893
|
}
|
|
10694
|
-
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup) {
|
|
10894
|
+
function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, semanticColorsActive) {
|
|
10695
10895
|
d3Selection5.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
10696
|
-
const
|
|
10697
|
-
const
|
|
10698
|
-
if (width <= 0 || height <= 0) return;
|
|
10896
|
+
const useSemanticColors = parsed.tagGroups.length === 0 && layout.nodes.every((n) => !n.color);
|
|
10897
|
+
const legendReserveH = useSemanticColors ? LEGEND_HEIGHT + DIAGRAM_PADDING5 : 0;
|
|
10699
10898
|
const titleHeight = parsed.title ? 40 : 0;
|
|
10700
10899
|
const diagramW = layout.width;
|
|
10701
10900
|
const diagramH = layout.height;
|
|
10702
|
-
const
|
|
10703
|
-
const
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10901
|
+
const naturalW = diagramW + DIAGRAM_PADDING5 * 2;
|
|
10902
|
+
const naturalH = diagramH + titleHeight + legendReserveH + DIAGRAM_PADDING5 * 2;
|
|
10903
|
+
let viewW;
|
|
10904
|
+
let viewH;
|
|
10905
|
+
let scale;
|
|
10906
|
+
let offsetX;
|
|
10907
|
+
let offsetY;
|
|
10908
|
+
if (exportDims) {
|
|
10909
|
+
viewW = exportDims.width ?? naturalW;
|
|
10910
|
+
viewH = exportDims.height ?? naturalH;
|
|
10911
|
+
const availH = viewH - titleHeight - legendReserveH;
|
|
10912
|
+
const scaleX = (viewW - DIAGRAM_PADDING5 * 2) / diagramW;
|
|
10913
|
+
const scaleY = (availH - DIAGRAM_PADDING5 * 2) / diagramH;
|
|
10914
|
+
scale = Math.min(MAX_SCALE4, scaleX, scaleY);
|
|
10915
|
+
const scaledW = diagramW * scale;
|
|
10916
|
+
offsetX = (viewW - scaledW) / 2;
|
|
10917
|
+
offsetY = titleHeight + DIAGRAM_PADDING5;
|
|
10918
|
+
} else {
|
|
10919
|
+
viewW = naturalW;
|
|
10920
|
+
viewH = naturalH;
|
|
10921
|
+
scale = 1;
|
|
10922
|
+
offsetX = DIAGRAM_PADDING5;
|
|
10923
|
+
offsetY = titleHeight + DIAGRAM_PADDING5;
|
|
10924
|
+
}
|
|
10925
|
+
if (viewW <= 0 || viewH <= 0) return;
|
|
10926
|
+
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);
|
|
10711
10927
|
if (parsed.title) {
|
|
10712
|
-
const titleEl = svg.append("text").attr("class", "chart-title").attr("x",
|
|
10928
|
+
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);
|
|
10713
10929
|
if (parsed.titleLineNumber) {
|
|
10714
10930
|
titleEl.attr("data-line-number", parsed.titleLineNumber);
|
|
10715
10931
|
if (onClickItem) {
|
|
@@ -10723,6 +10939,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10723
10939
|
}
|
|
10724
10940
|
const contentG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
|
|
10725
10941
|
const seriesColors2 = getSeriesColors(palette);
|
|
10942
|
+
const semanticRoles = useSemanticColors ? classifyEREntities(parsed.tables, parsed.relationships) : null;
|
|
10943
|
+
const semanticActive = semanticRoles !== null && (semanticColorsActive ?? true);
|
|
10726
10944
|
const useLabels = parsed.options.notation === "labels";
|
|
10727
10945
|
for (const edge of layout.edges) {
|
|
10728
10946
|
if (edge.points.length < 2) continue;
|
|
@@ -10762,7 +10980,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10762
10980
|
for (let ni = 0; ni < layout.nodes.length; ni++) {
|
|
10763
10981
|
const node = layout.nodes[ni];
|
|
10764
10982
|
const tagColor = resolveTagColor(node.metadata, parsed.tagGroups, activeTagGroup ?? null);
|
|
10765
|
-
const
|
|
10983
|
+
const semanticColor = semanticActive ? palette.colors[ROLE_COLORS[semanticRoles.get(node.id) ?? "unclassified"]] : semanticRoles ? palette.primary : void 0;
|
|
10984
|
+
const nodeColor2 = node.color ?? tagColor ?? semanticColor ?? seriesColors2[ni % seriesColors2.length];
|
|
10766
10985
|
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);
|
|
10767
10986
|
if (activeTagGroup) {
|
|
10768
10987
|
const tagKey = activeTagGroup.toLowerCase();
|
|
@@ -10771,6 +10990,10 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10771
10990
|
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
10772
10991
|
}
|
|
10773
10992
|
}
|
|
10993
|
+
if (semanticRoles) {
|
|
10994
|
+
const role = semanticRoles.get(node.id);
|
|
10995
|
+
if (role) nodeG.attr("data-er-role", role);
|
|
10996
|
+
}
|
|
10774
10997
|
if (onClickItem) {
|
|
10775
10998
|
nodeG.style("cursor", "pointer").on("click", () => {
|
|
10776
10999
|
onClickItem(node.lineNumber);
|
|
@@ -10803,32 +11026,87 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
|
|
|
10803
11026
|
}
|
|
10804
11027
|
}
|
|
10805
11028
|
if (parsed.tagGroups.length > 0) {
|
|
10806
|
-
const
|
|
10807
|
-
const
|
|
10808
|
-
const LEGEND_PILL_RX = 11;
|
|
10809
|
-
const LEGEND_PILL_PAD9 = 10;
|
|
11029
|
+
const LEGEND_PILL_H = LEGEND_HEIGHT - 6;
|
|
11030
|
+
const LEGEND_PILL_RX = Math.floor(LEGEND_PILL_H / 2);
|
|
10810
11031
|
const LEGEND_GAP2 = 8;
|
|
10811
|
-
const LEGEND_FONT_SIZE2 = 11;
|
|
10812
|
-
const LEGEND_GROUP_GAP7 = 16;
|
|
10813
11032
|
const legendG = svg.append("g").attr("class", "er-tag-legend");
|
|
11033
|
+
if (activeTagGroup) {
|
|
11034
|
+
legendG.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
11035
|
+
}
|
|
10814
11036
|
let legendX = DIAGRAM_PADDING5;
|
|
10815
|
-
let legendY =
|
|
11037
|
+
let legendY = viewH - DIAGRAM_PADDING5;
|
|
10816
11038
|
for (const group of parsed.tagGroups) {
|
|
10817
11039
|
const groupG = legendG.append("g").attr("data-legend-group", group.name.toLowerCase());
|
|
10818
|
-
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",
|
|
11040
|
+
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}:`);
|
|
10819
11041
|
const labelWidth = (labelText.node()?.getComputedTextLength?.() ?? group.name.length * 7) + 6;
|
|
10820
11042
|
legendX += labelWidth;
|
|
10821
11043
|
for (const entry of group.entries) {
|
|
10822
11044
|
const pillG = groupG.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
10823
|
-
const tmpText = legendG.append("text").attr("font-size",
|
|
11045
|
+
const tmpText = legendG.append("text").attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-family", FONT_FAMILY).text(entry.value);
|
|
10824
11046
|
const textW = tmpText.node()?.getComputedTextLength?.() ?? entry.value.length * 7;
|
|
10825
11047
|
tmpText.remove();
|
|
10826
|
-
const pillW = textW +
|
|
11048
|
+
const pillW = textW + LEGEND_PILL_PAD * 2;
|
|
10827
11049
|
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);
|
|
10828
|
-
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",
|
|
11050
|
+
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);
|
|
10829
11051
|
legendX += pillW + LEGEND_GAP2;
|
|
10830
11052
|
}
|
|
10831
|
-
legendX +=
|
|
11053
|
+
legendX += LEGEND_GROUP_GAP;
|
|
11054
|
+
}
|
|
11055
|
+
}
|
|
11056
|
+
if (semanticRoles) {
|
|
11057
|
+
const presentRoles = ROLE_ORDER.filter((role) => {
|
|
11058
|
+
for (const r of semanticRoles.values()) {
|
|
11059
|
+
if (r === role) return true;
|
|
11060
|
+
}
|
|
11061
|
+
return false;
|
|
11062
|
+
});
|
|
11063
|
+
if (presentRoles.length > 0) {
|
|
11064
|
+
const measureLabelW = (text, fontSize) => {
|
|
11065
|
+
const dummy = svg.append("text").attr("font-size", fontSize).attr("font-family", FONT_FAMILY).attr("visibility", "hidden").text(text);
|
|
11066
|
+
const measured = dummy.node()?.getComputedTextLength?.() ?? 0;
|
|
11067
|
+
dummy.remove();
|
|
11068
|
+
return measured > 0 ? measured : text.length * fontSize * 0.6;
|
|
11069
|
+
};
|
|
11070
|
+
const labelWidths = /* @__PURE__ */ new Map();
|
|
11071
|
+
for (const role of presentRoles) {
|
|
11072
|
+
labelWidths.set(role, measureLabelW(ROLE_LABELS[role], LEGEND_ENTRY_FONT_SIZE));
|
|
11073
|
+
}
|
|
11074
|
+
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
11075
|
+
const groupName = "Role";
|
|
11076
|
+
const pillWidth = groupName.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
11077
|
+
const pillH = LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2;
|
|
11078
|
+
let totalWidth;
|
|
11079
|
+
let entriesWidth = 0;
|
|
11080
|
+
if (semanticActive) {
|
|
11081
|
+
for (const role of presentRoles) {
|
|
11082
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + labelWidths.get(role) + LEGEND_ENTRY_TRAIL;
|
|
11083
|
+
}
|
|
11084
|
+
totalWidth = LEGEND_CAPSULE_PAD * 2 + pillWidth + LEGEND_ENTRY_TRAIL + entriesWidth;
|
|
11085
|
+
} else {
|
|
11086
|
+
totalWidth = pillWidth;
|
|
11087
|
+
}
|
|
11088
|
+
const legendX = (viewW - totalWidth) / 2;
|
|
11089
|
+
const legendY = viewH - DIAGRAM_PADDING5 - LEGEND_HEIGHT;
|
|
11090
|
+
const semanticLegendG = svg.append("g").attr("class", "er-semantic-legend").attr("data-legend-group", "role").attr("transform", `translate(${legendX}, ${legendY})`).style("cursor", "pointer");
|
|
11091
|
+
if (semanticActive) {
|
|
11092
|
+
semanticLegendG.append("rect").attr("width", totalWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
11093
|
+
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);
|
|
11094
|
+
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);
|
|
11095
|
+
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);
|
|
11096
|
+
let entryX = LEGEND_CAPSULE_PAD + pillWidth + LEGEND_ENTRY_TRAIL;
|
|
11097
|
+
for (const role of presentRoles) {
|
|
11098
|
+
const label = ROLE_LABELS[role];
|
|
11099
|
+
const roleColor = palette.colors[ROLE_COLORS[role]];
|
|
11100
|
+
const entryG = semanticLegendG.append("g").attr("data-legend-entry", role);
|
|
11101
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", roleColor);
|
|
11102
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
11103
|
+
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);
|
|
11104
|
+
entryX = textX + labelWidths.get(role) + LEGEND_ENTRY_TRAIL;
|
|
11105
|
+
}
|
|
11106
|
+
} else {
|
|
11107
|
+
semanticLegendG.append("rect").attr("width", pillWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
11108
|
+
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);
|
|
11109
|
+
}
|
|
10832
11110
|
}
|
|
10833
11111
|
}
|
|
10834
11112
|
}
|
|
@@ -10875,8 +11153,10 @@ var init_renderer5 = __esm({
|
|
|
10875
11153
|
init_color_utils();
|
|
10876
11154
|
init_palettes();
|
|
10877
11155
|
init_tag_groups();
|
|
11156
|
+
init_legend_constants();
|
|
10878
11157
|
init_parser3();
|
|
10879
11158
|
init_layout4();
|
|
11159
|
+
init_classify();
|
|
10880
11160
|
DIAGRAM_PADDING5 = 20;
|
|
10881
11161
|
MAX_SCALE4 = 3;
|
|
10882
11162
|
TABLE_FONT_SIZE = 13;
|
|
@@ -11005,9 +11285,10 @@ function layoutInitiativeStatus(parsed, collapseResult) {
|
|
|
11005
11285
|
const dagreEdge = g.edge(edge.source, edge.target, `e${i}`);
|
|
11006
11286
|
const dagrePoints = dagreEdge?.points ?? [];
|
|
11007
11287
|
const hasIntermediateRank = allNodeX.some((x) => x > src.x + 20 && x < tgt.x - 20);
|
|
11008
|
-
const step = Math.min((enterX - exitX) * 0.15, 20);
|
|
11288
|
+
const step = Math.max(0, Math.min((enterX - exitX) * 0.15, 20));
|
|
11009
11289
|
const isBackEdge = tgt.x < src.x - 5;
|
|
11010
|
-
const
|
|
11290
|
+
const isTopExit = !isBackEdge && tgt.x > src.x && !hasIntermediateRank && tgt.y < src.y - NODESEP;
|
|
11291
|
+
const isBottomExit = !isBackEdge && tgt.x > src.x && !hasIntermediateRank && tgt.y > src.y + NODESEP;
|
|
11011
11292
|
let points;
|
|
11012
11293
|
if (isBackEdge) {
|
|
11013
11294
|
const routeAbove = Math.min(src.y, tgt.y) > avgNodeY;
|
|
@@ -11017,28 +11298,43 @@ function layoutInitiativeStatus(parsed, collapseResult) {
|
|
|
11017
11298
|
const spreadDir = avgNodeX < rawMidX ? 1 : -1;
|
|
11018
11299
|
const unclamped = Math.abs(src.x - tgt.x) < NODE_WIDTH ? rawMidX + spreadDir * BACK_EDGE_MIN_SPREAD : rawMidX;
|
|
11019
11300
|
const midX = Math.min(src.x, Math.max(tgt.x, unclamped));
|
|
11301
|
+
const srcDepart = Math.max(midX + 1, src.x - TOP_EXIT_STEP);
|
|
11302
|
+
const tgtApproach = Math.min(midX - 1, tgt.x + TOP_EXIT_STEP);
|
|
11020
11303
|
if (routeAbove) {
|
|
11021
11304
|
const arcY = Math.min(src.y - srcHalfH, tgt.y - tgtHalfH) - BACK_EDGE_MARGIN;
|
|
11022
11305
|
points = [
|
|
11023
11306
|
{ x: src.x, y: src.y - srcHalfH },
|
|
11307
|
+
{ x: srcDepart, y: src.y - srcHalfH - TOP_EXIT_STEP },
|
|
11024
11308
|
{ x: midX, y: arcY },
|
|
11309
|
+
{ x: tgtApproach, y: tgt.y - tgtHalfH - TOP_EXIT_STEP },
|
|
11025
11310
|
{ x: tgt.x, y: tgt.y - tgtHalfH }
|
|
11026
11311
|
];
|
|
11027
11312
|
} else {
|
|
11028
11313
|
const arcY = Math.max(src.y + srcHalfH, tgt.y + tgtHalfH) + BACK_EDGE_MARGIN;
|
|
11029
11314
|
points = [
|
|
11030
11315
|
{ x: src.x, y: src.y + srcHalfH },
|
|
11316
|
+
{ x: srcDepart, y: src.y + srcHalfH + TOP_EXIT_STEP },
|
|
11031
11317
|
{ x: midX, y: arcY },
|
|
11318
|
+
{ x: tgtApproach, y: tgt.y + tgtHalfH + TOP_EXIT_STEP },
|
|
11032
11319
|
{ x: tgt.x, y: tgt.y + tgtHalfH }
|
|
11033
11320
|
];
|
|
11034
11321
|
}
|
|
11035
|
-
} else if (
|
|
11036
|
-
const exitY =
|
|
11037
|
-
const
|
|
11038
|
-
const midY = (exitY + tgt.y) / 2;
|
|
11322
|
+
} else if (isTopExit) {
|
|
11323
|
+
const exitY = src.y - src.height / 2;
|
|
11324
|
+
const p1x = Math.min(Math.max(src.x, src.x + yOffset + TOP_EXIT_STEP), (src.x + enterX) / 2 - 1);
|
|
11039
11325
|
points = [
|
|
11040
11326
|
{ x: src.x, y: exitY },
|
|
11041
|
-
{ x:
|
|
11327
|
+
{ x: p1x, y: exitY - TOP_EXIT_STEP },
|
|
11328
|
+
{ x: enterX - step, y: tgt.y + yOffset },
|
|
11329
|
+
{ x: enterX, y: tgt.y }
|
|
11330
|
+
];
|
|
11331
|
+
} else if (isBottomExit) {
|
|
11332
|
+
const exitY = src.y + src.height / 2;
|
|
11333
|
+
const p1x = Math.min(Math.max(src.x, src.x + yOffset + TOP_EXIT_STEP), (src.x + enterX) / 2 - 1);
|
|
11334
|
+
points = [
|
|
11335
|
+
{ x: src.x, y: exitY },
|
|
11336
|
+
{ x: p1x, y: exitY + TOP_EXIT_STEP },
|
|
11337
|
+
{ x: enterX - step, y: tgt.y + yOffset },
|
|
11042
11338
|
{ x: enterX, y: tgt.y }
|
|
11043
11339
|
];
|
|
11044
11340
|
} else if (tgt.x > src.x && !hasIntermediateRank) {
|
|
@@ -11136,7 +11432,7 @@ function layoutInitiativeStatus(parsed, collapseResult) {
|
|
|
11136
11432
|
totalHeight += 40;
|
|
11137
11433
|
return { nodes: layoutNodes, edges: layoutEdges, groups: layoutGroups, width: totalWidth, height: totalHeight };
|
|
11138
11434
|
}
|
|
11139
|
-
var 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;
|
|
11435
|
+
var 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;
|
|
11140
11436
|
var init_layout5 = __esm({
|
|
11141
11437
|
"src/initiative-status/layout.ts"() {
|
|
11142
11438
|
"use strict";
|
|
@@ -11152,6 +11448,7 @@ var init_layout5 = __esm({
|
|
|
11152
11448
|
MAX_PARALLEL_EDGES = 5;
|
|
11153
11449
|
BACK_EDGE_MARGIN = 40;
|
|
11154
11450
|
BACK_EDGE_MIN_SPREAD = Math.round(NODE_WIDTH * 0.75);
|
|
11451
|
+
TOP_EXIT_STEP = 10;
|
|
11155
11452
|
CHAR_WIDTH_RATIO = 0.6;
|
|
11156
11453
|
NODE_FONT_SIZE = 13;
|
|
11157
11454
|
NODE_TEXT_PADDING = 12;
|
|
@@ -11651,7 +11948,7 @@ __export(layout_exports6, {
|
|
|
11651
11948
|
rollUpContextRelationships: () => rollUpContextRelationships
|
|
11652
11949
|
});
|
|
11653
11950
|
import dagre5 from "@dagrejs/dagre";
|
|
11654
|
-
function computeEdgePenalty(edgeList, nodePositions, degrees) {
|
|
11951
|
+
function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
|
|
11655
11952
|
let penalty = 0;
|
|
11656
11953
|
for (const edge of edgeList) {
|
|
11657
11954
|
const sx = nodePositions.get(edge.source);
|
|
@@ -11661,6 +11958,32 @@ function computeEdgePenalty(edgeList, nodePositions, degrees) {
|
|
|
11661
11958
|
const weight = Math.min(degrees.get(edge.source) ?? 1, degrees.get(edge.target) ?? 1);
|
|
11662
11959
|
penalty += dist * weight;
|
|
11663
11960
|
}
|
|
11961
|
+
if (nodeGeometry) {
|
|
11962
|
+
for (const edge of edgeList) {
|
|
11963
|
+
const geomA = nodeGeometry.get(edge.source);
|
|
11964
|
+
const geomB = nodeGeometry.get(edge.target);
|
|
11965
|
+
if (!geomA || !geomB) continue;
|
|
11966
|
+
const ax = nodePositions.get(edge.source) ?? 0;
|
|
11967
|
+
const bx = nodePositions.get(edge.target) ?? 0;
|
|
11968
|
+
const ay = geomA.y;
|
|
11969
|
+
const by = geomB.y;
|
|
11970
|
+
if (ay === by) continue;
|
|
11971
|
+
const edgeMinX = Math.min(ax, bx);
|
|
11972
|
+
const edgeMaxX = Math.max(ax, bx);
|
|
11973
|
+
const edgeMinY = Math.min(ay, by);
|
|
11974
|
+
const edgeMaxY = Math.max(ay, by);
|
|
11975
|
+
for (const [name, geomC] of nodeGeometry) {
|
|
11976
|
+
if (name === edge.source || name === edge.target) continue;
|
|
11977
|
+
const cx = nodePositions.get(name) ?? 0;
|
|
11978
|
+
const cy = geomC.y;
|
|
11979
|
+
const hw = geomC.width / 2;
|
|
11980
|
+
const hh = geomC.height / 2;
|
|
11981
|
+
if (cx + hw > edgeMinX && cx - hw < edgeMaxX && cy + hh > edgeMinY && cy - hh < edgeMaxY) {
|
|
11982
|
+
penalty += EDGE_NODE_COLLISION_WEIGHT;
|
|
11983
|
+
}
|
|
11984
|
+
}
|
|
11985
|
+
}
|
|
11986
|
+
}
|
|
11664
11987
|
return penalty;
|
|
11665
11988
|
}
|
|
11666
11989
|
function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
@@ -11670,6 +11993,11 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11670
11993
|
degrees.set(edge.source, (degrees.get(edge.source) ?? 0) + 1);
|
|
11671
11994
|
degrees.set(edge.target, (degrees.get(edge.target) ?? 0) + 1);
|
|
11672
11995
|
}
|
|
11996
|
+
const nodeGeometry = /* @__PURE__ */ new Map();
|
|
11997
|
+
for (const name of g.nodes()) {
|
|
11998
|
+
const pos = g.node(name);
|
|
11999
|
+
if (pos) nodeGeometry.set(name, { y: pos.y, width: pos.width, height: pos.height });
|
|
12000
|
+
}
|
|
11673
12001
|
const rankMap = /* @__PURE__ */ new Map();
|
|
11674
12002
|
for (const name of g.nodes()) {
|
|
11675
12003
|
const pos = g.node(name);
|
|
@@ -11712,7 +12040,7 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11712
12040
|
const pos = g.node(name);
|
|
11713
12041
|
if (pos) basePositions.set(name, pos.x);
|
|
11714
12042
|
}
|
|
11715
|
-
const currentPenalty = computeEdgePenalty(edgeList, basePositions, degrees);
|
|
12043
|
+
const currentPenalty = computeEdgePenalty(edgeList, basePositions, degrees, nodeGeometry);
|
|
11716
12044
|
let bestPerm = [...partition];
|
|
11717
12045
|
let bestPenalty = currentPenalty;
|
|
11718
12046
|
if (partition.length <= 8) {
|
|
@@ -11722,7 +12050,7 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11722
12050
|
for (let i = 0; i < perm.length; i++) {
|
|
11723
12051
|
testPositions.set(perm[i], xSlots[i]);
|
|
11724
12052
|
}
|
|
11725
|
-
const penalty = computeEdgePenalty(edgeList, testPositions, degrees);
|
|
12053
|
+
const penalty = computeEdgePenalty(edgeList, testPositions, degrees, nodeGeometry);
|
|
11726
12054
|
if (penalty < bestPenalty) {
|
|
11727
12055
|
bestPenalty = penalty;
|
|
11728
12056
|
bestPerm = [...perm];
|
|
@@ -11740,13 +12068,13 @@ function reduceCrossings(g, edgeList, nodeGroupMap) {
|
|
|
11740
12068
|
for (let k = 0; k < workingOrder.length; k++) {
|
|
11741
12069
|
testPositions.set(workingOrder[k], xSlots[k]);
|
|
11742
12070
|
}
|
|
11743
|
-
const before = computeEdgePenalty(edgeList, testPositions, degrees);
|
|
12071
|
+
const before = computeEdgePenalty(edgeList, testPositions, degrees, nodeGeometry);
|
|
11744
12072
|
[workingOrder[i], workingOrder[i + 1]] = [workingOrder[i + 1], workingOrder[i]];
|
|
11745
12073
|
const testPositions2 = new Map(basePositions);
|
|
11746
12074
|
for (let k = 0; k < workingOrder.length; k++) {
|
|
11747
12075
|
testPositions2.set(workingOrder[k], xSlots[k]);
|
|
11748
12076
|
}
|
|
11749
|
-
const after = computeEdgePenalty(edgeList, testPositions2, degrees);
|
|
12077
|
+
const after = computeEdgePenalty(edgeList, testPositions2, degrees, nodeGeometry);
|
|
11750
12078
|
if (after < before) {
|
|
11751
12079
|
improved = true;
|
|
11752
12080
|
if (after < bestPenalty) {
|
|
@@ -11974,31 +12302,27 @@ function computeC4NodeDimensions(el, options) {
|
|
|
11974
12302
|
height += CARD_V_PAD3;
|
|
11975
12303
|
return { width, height };
|
|
11976
12304
|
}
|
|
11977
|
-
function computeLegendGroups3(tagGroups
|
|
12305
|
+
function computeLegendGroups3(tagGroups) {
|
|
11978
12306
|
const result = [];
|
|
11979
12307
|
for (const group of tagGroups) {
|
|
11980
12308
|
const entries = [];
|
|
11981
12309
|
for (const entry of group.entries) {
|
|
11982
|
-
if (usedValuesByGroup) {
|
|
11983
|
-
const used = usedValuesByGroup.get(group.name.toLowerCase());
|
|
11984
|
-
if (!used?.has(entry.value.toLowerCase())) continue;
|
|
11985
|
-
}
|
|
11986
12310
|
entries.push({ value: entry.value, color: entry.color });
|
|
11987
12311
|
}
|
|
11988
12312
|
if (entries.length === 0) continue;
|
|
11989
|
-
const nameW = group.name.length *
|
|
11990
|
-
let capsuleW =
|
|
12313
|
+
const nameW = group.name.length * LEGEND_PILL_FONT_W4 + LEGEND_PILL_PAD4 * 2;
|
|
12314
|
+
let capsuleW = LEGEND_CAPSULE_PAD4;
|
|
11991
12315
|
for (const e of entries) {
|
|
11992
|
-
capsuleW +=
|
|
12316
|
+
capsuleW += LEGEND_DOT_R4 * 2 + LEGEND_ENTRY_DOT_GAP4 + e.value.length * LEGEND_ENTRY_FONT_W5 + LEGEND_ENTRY_TRAIL4;
|
|
11993
12317
|
}
|
|
11994
|
-
capsuleW +=
|
|
12318
|
+
capsuleW += LEGEND_CAPSULE_PAD4;
|
|
11995
12319
|
result.push({
|
|
11996
12320
|
name: group.name,
|
|
11997
12321
|
entries,
|
|
11998
12322
|
x: 0,
|
|
11999
12323
|
y: 0,
|
|
12000
12324
|
width: nameW + capsuleW,
|
|
12001
|
-
height:
|
|
12325
|
+
height: LEGEND_HEIGHT4
|
|
12002
12326
|
});
|
|
12003
12327
|
}
|
|
12004
12328
|
return result;
|
|
@@ -12118,18 +12442,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
12118
12442
|
}
|
|
12119
12443
|
let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN3 * 2 : 0;
|
|
12120
12444
|
let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN3 * 2 : 0;
|
|
12121
|
-
const
|
|
12122
|
-
for (const el of contextElements) {
|
|
12123
|
-
for (const group of parsed.tagGroups) {
|
|
12124
|
-
const key = group.name.toLowerCase();
|
|
12125
|
-
const val = el.metadata[key];
|
|
12126
|
-
if (val) {
|
|
12127
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
12128
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
12129
|
-
}
|
|
12130
|
-
}
|
|
12131
|
-
}
|
|
12132
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
12445
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
12133
12446
|
if (legendGroups.length > 0) {
|
|
12134
12447
|
const legendY = totalHeight + MARGIN3;
|
|
12135
12448
|
let legendX = MARGIN3;
|
|
@@ -12139,7 +12452,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
12139
12452
|
legendX += lg.width + 12;
|
|
12140
12453
|
}
|
|
12141
12454
|
const legendRight = legendX;
|
|
12142
|
-
const legendBottom = legendY +
|
|
12455
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
12143
12456
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
12144
12457
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
12145
12458
|
}
|
|
@@ -12445,18 +12758,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
12445
12758
|
}
|
|
12446
12759
|
let totalWidth = maxX - minX + MARGIN3 * 2;
|
|
12447
12760
|
let totalHeight = maxY - minY + MARGIN3 * 2;
|
|
12448
|
-
const
|
|
12449
|
-
for (const el of [...containers, ...externals]) {
|
|
12450
|
-
for (const group of parsed.tagGroups) {
|
|
12451
|
-
const key = group.name.toLowerCase();
|
|
12452
|
-
const val = el.metadata[key];
|
|
12453
|
-
if (val) {
|
|
12454
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
12455
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
12456
|
-
}
|
|
12457
|
-
}
|
|
12458
|
-
}
|
|
12459
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
12761
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
12460
12762
|
if (legendGroups.length > 0) {
|
|
12461
12763
|
const legendY = totalHeight + MARGIN3;
|
|
12462
12764
|
let legendX = MARGIN3;
|
|
@@ -12466,7 +12768,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
12466
12768
|
legendX += lg.width + 12;
|
|
12467
12769
|
}
|
|
12468
12770
|
const legendRight = legendX;
|
|
12469
|
-
const legendBottom = legendY +
|
|
12771
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
12470
12772
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
12471
12773
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
12472
12774
|
}
|
|
@@ -12821,21 +13123,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
12821
13123
|
}
|
|
12822
13124
|
let totalWidth = maxX - minX + MARGIN3 * 2;
|
|
12823
13125
|
let totalHeight = maxY - minY + MARGIN3 * 2;
|
|
12824
|
-
const
|
|
12825
|
-
for (const el of [...components, ...externals]) {
|
|
12826
|
-
for (const group of parsed.tagGroups) {
|
|
12827
|
-
const key = group.name.toLowerCase();
|
|
12828
|
-
let val = el.metadata[key];
|
|
12829
|
-
if (!val && components.includes(el)) {
|
|
12830
|
-
val = targetContainer.metadata[key] ?? system.metadata[key];
|
|
12831
|
-
}
|
|
12832
|
-
if (val) {
|
|
12833
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
12834
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
12835
|
-
}
|
|
12836
|
-
}
|
|
12837
|
-
}
|
|
12838
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
13126
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
12839
13127
|
if (legendGroups.length > 0) {
|
|
12840
13128
|
const legendY = totalHeight + MARGIN3;
|
|
12841
13129
|
let legendX = MARGIN3;
|
|
@@ -12845,7 +13133,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
12845
13133
|
legendX += lg.width + 12;
|
|
12846
13134
|
}
|
|
12847
13135
|
const legendRight = legendX;
|
|
12848
|
-
const legendBottom = legendY +
|
|
13136
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
12849
13137
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
12850
13138
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
12851
13139
|
}
|
|
@@ -13090,18 +13378,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
13090
13378
|
}
|
|
13091
13379
|
let totalWidth = maxX - minX + MARGIN3 * 2;
|
|
13092
13380
|
let totalHeight = maxY - minY + MARGIN3 * 2;
|
|
13093
|
-
const
|
|
13094
|
-
for (const r of refEntries) {
|
|
13095
|
-
for (const group of parsed.tagGroups) {
|
|
13096
|
-
const key = group.name.toLowerCase();
|
|
13097
|
-
const val = r.element.metadata[key];
|
|
13098
|
-
if (val) {
|
|
13099
|
-
if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, /* @__PURE__ */ new Set());
|
|
13100
|
-
usedValuesByGroup.get(key).add(val.toLowerCase());
|
|
13101
|
-
}
|
|
13102
|
-
}
|
|
13103
|
-
}
|
|
13104
|
-
const legendGroups = computeLegendGroups3(parsed.tagGroups, usedValuesByGroup);
|
|
13381
|
+
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
13105
13382
|
if (legendGroups.length > 0) {
|
|
13106
13383
|
const legendY = totalHeight + MARGIN3;
|
|
13107
13384
|
let legendX = MARGIN3;
|
|
@@ -13111,13 +13388,13 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
13111
13388
|
legendX += lg.width + 12;
|
|
13112
13389
|
}
|
|
13113
13390
|
const legendRight = legendX;
|
|
13114
|
-
const legendBottom = legendY +
|
|
13391
|
+
const legendBottom = legendY + LEGEND_HEIGHT4;
|
|
13115
13392
|
if (legendRight > totalWidth) totalWidth = legendRight;
|
|
13116
13393
|
if (legendBottom > totalHeight) totalHeight = legendBottom;
|
|
13117
13394
|
}
|
|
13118
13395
|
return { nodes, edges, legend: legendGroups, groupBoundaries, width: totalWidth, height: totalHeight };
|
|
13119
13396
|
}
|
|
13120
|
-
var 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,
|
|
13397
|
+
var 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;
|
|
13121
13398
|
var init_layout6 = __esm({
|
|
13122
13399
|
"src/c4/layout.ts"() {
|
|
13123
13400
|
"use strict";
|
|
@@ -13136,16 +13413,17 @@ var init_layout6 = __esm({
|
|
|
13136
13413
|
MARGIN3 = 40;
|
|
13137
13414
|
BOUNDARY_PAD = 40;
|
|
13138
13415
|
GROUP_BOUNDARY_PAD = 24;
|
|
13139
|
-
|
|
13140
|
-
|
|
13141
|
-
|
|
13142
|
-
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
LEGEND_ENTRY_FONT_W5 =
|
|
13146
|
-
|
|
13147
|
-
|
|
13148
|
-
|
|
13416
|
+
LEGEND_HEIGHT4 = 28;
|
|
13417
|
+
LEGEND_PILL_FONT_SIZE2 = 11;
|
|
13418
|
+
LEGEND_PILL_FONT_W4 = LEGEND_PILL_FONT_SIZE2 * 0.6;
|
|
13419
|
+
LEGEND_PILL_PAD4 = 16;
|
|
13420
|
+
LEGEND_DOT_R4 = 4;
|
|
13421
|
+
LEGEND_ENTRY_FONT_SIZE2 = 10;
|
|
13422
|
+
LEGEND_ENTRY_FONT_W5 = LEGEND_ENTRY_FONT_SIZE2 * 0.6;
|
|
13423
|
+
LEGEND_ENTRY_DOT_GAP4 = 4;
|
|
13424
|
+
LEGEND_ENTRY_TRAIL4 = 8;
|
|
13425
|
+
LEGEND_CAPSULE_PAD4 = 4;
|
|
13426
|
+
EDGE_NODE_COLLISION_WEIGHT = 5e3;
|
|
13149
13427
|
META_EXCLUDE_KEYS = /* @__PURE__ */ new Set(["description", "tech", "technology", "is a"]);
|
|
13150
13428
|
}
|
|
13151
13429
|
});
|
|
@@ -13223,8 +13501,14 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
13223
13501
|
if (width <= 0 || height <= 0) return;
|
|
13224
13502
|
const titleHeight = parsed.title ? TITLE_HEIGHT4 + 10 : 0;
|
|
13225
13503
|
const diagramW = layout.width;
|
|
13226
|
-
const
|
|
13227
|
-
const
|
|
13504
|
+
const hasLegend = layout.legend.length > 0;
|
|
13505
|
+
const C4_LAYOUT_MARGIN = 40;
|
|
13506
|
+
const LEGEND_FIXED_GAP4 = 8;
|
|
13507
|
+
const fixedLegend = !exportDims && hasLegend;
|
|
13508
|
+
const legendLayoutSpace = C4_LAYOUT_MARGIN + LEGEND_HEIGHT;
|
|
13509
|
+
const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : 0;
|
|
13510
|
+
const diagramH = fixedLegend ? layout.height - legendLayoutSpace : layout.height;
|
|
13511
|
+
const availH = height - titleHeight - legendReserveH;
|
|
13228
13512
|
const scaleX = (width - DIAGRAM_PADDING7 * 2) / diagramW;
|
|
13229
13513
|
const scaleY = (availH - DIAGRAM_PADDING7 * 2) / diagramH;
|
|
13230
13514
|
const scale = Math.min(MAX_SCALE6, scaleX, scaleY);
|
|
@@ -13296,6 +13580,20 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
13296
13580
|
}
|
|
13297
13581
|
for (const node of layout.nodes) {
|
|
13298
13582
|
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);
|
|
13583
|
+
if (activeTagGroup) {
|
|
13584
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
13585
|
+
const tagValue = node.metadata[tagKey];
|
|
13586
|
+
if (tagValue) {
|
|
13587
|
+
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
13588
|
+
} else {
|
|
13589
|
+
const tagGroup = parsed.tagGroups.find(
|
|
13590
|
+
(g) => g.name.toLowerCase() === tagKey || g.alias?.toLowerCase() === tagKey
|
|
13591
|
+
);
|
|
13592
|
+
if (tagGroup?.defaultValue) {
|
|
13593
|
+
nodeG.attr(`data-tag-${tagKey}`, tagGroup.defaultValue.toLowerCase());
|
|
13594
|
+
}
|
|
13595
|
+
}
|
|
13596
|
+
}
|
|
13299
13597
|
if (node.importPath) {
|
|
13300
13598
|
nodeG.attr("data-import-path", node.importPath);
|
|
13301
13599
|
}
|
|
@@ -13348,36 +13646,12 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
|
|
|
13348
13646
|
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");
|
|
13349
13647
|
}
|
|
13350
13648
|
}
|
|
13351
|
-
if (
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
13356
|
-
const pillLabel = group.name;
|
|
13357
|
-
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W6 + LEGEND_PILL_PAD6;
|
|
13358
|
-
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");
|
|
13359
|
-
if (isActive) {
|
|
13360
|
-
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT7).attr("rx", LEGEND_HEIGHT7 / 2).attr("fill", groupBg);
|
|
13361
|
-
}
|
|
13362
|
-
const pillX = isActive ? LEGEND_CAPSULE_PAD6 : 0;
|
|
13363
|
-
const pillY = isActive ? LEGEND_CAPSULE_PAD6 : 0;
|
|
13364
|
-
const pillH = LEGEND_HEIGHT7 - (isActive ? LEGEND_CAPSULE_PAD6 * 2 : 0);
|
|
13365
|
-
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);
|
|
13366
|
-
if (isActive) {
|
|
13367
|
-
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);
|
|
13368
|
-
}
|
|
13369
|
-
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);
|
|
13370
|
-
if (isActive) {
|
|
13371
|
-
let entryX = pillX + pillWidth + 4;
|
|
13372
|
-
for (const entry of group.entries) {
|
|
13373
|
-
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
13374
|
-
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R7).attr("cy", LEGEND_HEIGHT7 / 2).attr("r", LEGEND_DOT_R7).attr("fill", entry.color);
|
|
13375
|
-
const textX = entryX + LEGEND_DOT_R7 * 2 + LEGEND_ENTRY_DOT_GAP6;
|
|
13376
|
-
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);
|
|
13377
|
-
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W6 + LEGEND_ENTRY_TRAIL6;
|
|
13378
|
-
}
|
|
13379
|
-
}
|
|
13649
|
+
if (hasLegend) {
|
|
13650
|
+
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");
|
|
13651
|
+
if (activeTagGroup) {
|
|
13652
|
+
legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
13380
13653
|
}
|
|
13654
|
+
renderLegend2(legendParent, layout, palette, isDark, activeTagGroup, fixedLegend ? width : null);
|
|
13381
13655
|
}
|
|
13382
13656
|
}
|
|
13383
13657
|
function renderC4ContextForExport(content, theme, palette) {
|
|
@@ -13665,33 +13939,47 @@ function placeEdgeLabels(labels, edges, obstacleRects) {
|
|
|
13665
13939
|
placedRects.push({ x: lbl.x, y: lbl.y, w: lbl.bgW, h: lbl.bgH });
|
|
13666
13940
|
}
|
|
13667
13941
|
}
|
|
13668
|
-
function renderLegend2(
|
|
13669
|
-
|
|
13942
|
+
function renderLegend2(parent, layout, palette, isDark, activeTagGroup, fixedWidth) {
|
|
13943
|
+
const visibleGroups = activeTagGroup != null ? layout.legend.filter((g) => g.name.toLowerCase() === (activeTagGroup ?? "").toLowerCase()) : layout.legend;
|
|
13944
|
+
const pillWidthOf = (g) => g.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
13945
|
+
const effectiveW = (g) => activeTagGroup != null ? g.width : pillWidthOf(g);
|
|
13946
|
+
let fixedPositions = null;
|
|
13947
|
+
if (fixedWidth != null && visibleGroups.length > 0) {
|
|
13948
|
+
fixedPositions = /* @__PURE__ */ new Map();
|
|
13949
|
+
const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0) + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
13950
|
+
let cx = Math.max(DIAGRAM_PADDING7, (fixedWidth - totalW) / 2);
|
|
13951
|
+
for (const g of visibleGroups) {
|
|
13952
|
+
fixedPositions.set(g.name, cx);
|
|
13953
|
+
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
13954
|
+
}
|
|
13955
|
+
}
|
|
13956
|
+
for (const group of visibleGroups) {
|
|
13670
13957
|
const isActive = activeTagGroup != null && group.name.toLowerCase() === (activeTagGroup ?? "").toLowerCase();
|
|
13671
|
-
if (activeTagGroup != null && !isActive) continue;
|
|
13672
13958
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
13673
13959
|
const pillLabel = group.name;
|
|
13674
|
-
const pillWidth =
|
|
13675
|
-
const
|
|
13960
|
+
const pillWidth = pillWidthOf(group);
|
|
13961
|
+
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
13962
|
+
const gY = fixedPositions != null ? 0 : group.y;
|
|
13963
|
+
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");
|
|
13676
13964
|
if (isActive) {
|
|
13677
|
-
gEl.append("rect").attr("width", group.width).attr("height",
|
|
13965
|
+
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
13678
13966
|
}
|
|
13679
|
-
const pillX = isActive ?
|
|
13680
|
-
const pillY = isActive ?
|
|
13681
|
-
const pillH =
|
|
13967
|
+
const pillX = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
13968
|
+
const pillY = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
13969
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
13682
13970
|
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);
|
|
13683
13971
|
if (isActive) {
|
|
13684
13972
|
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);
|
|
13685
13973
|
}
|
|
13686
|
-
gEl.append("text").attr("x", pillX + pillWidth / 2).attr("y",
|
|
13974
|
+
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);
|
|
13687
13975
|
if (isActive) {
|
|
13688
13976
|
let entryX = pillX + pillWidth + 4;
|
|
13689
13977
|
for (const entry of group.entries) {
|
|
13690
13978
|
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
13691
|
-
entryG.append("circle").attr("cx", entryX +
|
|
13692
|
-
const textX = entryX +
|
|
13693
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
13694
|
-
entryX = textX + entry.value.length *
|
|
13979
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
13980
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
13981
|
+
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);
|
|
13982
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
13695
13983
|
}
|
|
13696
13984
|
}
|
|
13697
13985
|
}
|
|
@@ -13703,8 +13991,14 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
|
|
|
13703
13991
|
if (width <= 0 || height <= 0) return;
|
|
13704
13992
|
const titleHeight = parsed.title ? TITLE_HEIGHT4 + 10 : 0;
|
|
13705
13993
|
const diagramW = layout.width;
|
|
13706
|
-
const
|
|
13707
|
-
const
|
|
13994
|
+
const hasLegend = layout.legend.length > 0;
|
|
13995
|
+
const C4_LAYOUT_MARGIN = 40;
|
|
13996
|
+
const LEGEND_FIXED_GAP4 = 8;
|
|
13997
|
+
const fixedLegend = !exportDims && hasLegend;
|
|
13998
|
+
const legendLayoutSpace = C4_LAYOUT_MARGIN + LEGEND_HEIGHT;
|
|
13999
|
+
const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : 0;
|
|
14000
|
+
const diagramH = fixedLegend ? layout.height - legendLayoutSpace : layout.height;
|
|
14001
|
+
const availH = height - titleHeight - legendReserveH;
|
|
13708
14002
|
const scaleX = (width - DIAGRAM_PADDING7 * 2) / diagramW;
|
|
13709
14003
|
const scaleY = (availH - DIAGRAM_PADDING7 * 2) / diagramH;
|
|
13710
14004
|
const scale = Math.min(MAX_SCALE6, scaleX, scaleY);
|
|
@@ -13775,6 +14069,20 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
|
|
|
13775
14069
|
renderEdges(contentG, layout.edges, palette, onClickItem, boundaryLabelObstacles);
|
|
13776
14070
|
for (const node of layout.nodes) {
|
|
13777
14071
|
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);
|
|
14072
|
+
if (activeTagGroup) {
|
|
14073
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
14074
|
+
const tagValue = node.metadata[tagKey];
|
|
14075
|
+
if (tagValue) {
|
|
14076
|
+
nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
|
|
14077
|
+
} else {
|
|
14078
|
+
const tagGroup = parsed.tagGroups.find(
|
|
14079
|
+
(g) => g.name.toLowerCase() === tagKey || g.alias?.toLowerCase() === tagKey
|
|
14080
|
+
);
|
|
14081
|
+
if (tagGroup?.defaultValue) {
|
|
14082
|
+
nodeG.attr(`data-tag-${tagKey}`, tagGroup.defaultValue.toLowerCase());
|
|
14083
|
+
}
|
|
14084
|
+
}
|
|
14085
|
+
}
|
|
13778
14086
|
if (node.shape) {
|
|
13779
14087
|
nodeG.attr("data-shape", node.shape);
|
|
13780
14088
|
}
|
|
@@ -13860,8 +14168,12 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
|
|
|
13860
14168
|
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");
|
|
13861
14169
|
}
|
|
13862
14170
|
}
|
|
13863
|
-
if (
|
|
13864
|
-
|
|
14171
|
+
if (hasLegend) {
|
|
14172
|
+
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");
|
|
14173
|
+
if (activeTagGroup) {
|
|
14174
|
+
legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
14175
|
+
}
|
|
14176
|
+
renderLegend2(legendParent, layout, palette, isDark, activeTagGroup, fixedLegend ? width : null);
|
|
13865
14177
|
}
|
|
13866
14178
|
}
|
|
13867
14179
|
function renderC4ContainersForExport(content, systemName, theme, palette) {
|
|
@@ -13963,7 +14275,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
|
|
|
13963
14275
|
document.body.removeChild(el);
|
|
13964
14276
|
}
|
|
13965
14277
|
}
|
|
13966
|
-
var 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,
|
|
14278
|
+
var 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;
|
|
13967
14279
|
var init_renderer7 = __esm({
|
|
13968
14280
|
"src/c4/renderer.ts"() {
|
|
13969
14281
|
"use strict";
|
|
@@ -13972,6 +14284,7 @@ var init_renderer7 = __esm({
|
|
|
13972
14284
|
init_inline_markdown();
|
|
13973
14285
|
init_parser6();
|
|
13974
14286
|
init_layout6();
|
|
14287
|
+
init_legend_constants();
|
|
13975
14288
|
DIAGRAM_PADDING7 = 20;
|
|
13976
14289
|
MAX_SCALE6 = 3;
|
|
13977
14290
|
TITLE_HEIGHT4 = 30;
|
|
@@ -14004,16 +14317,6 @@ var init_renderer7 = __esm({
|
|
|
14004
14317
|
PERSON_LEG_SPAN = 7;
|
|
14005
14318
|
PERSON_ICON_W = PERSON_ARM_SPAN * 2;
|
|
14006
14319
|
PERSON_SW = 1.5;
|
|
14007
|
-
LEGEND_HEIGHT7 = 28;
|
|
14008
|
-
LEGEND_PILL_FONT_SIZE4 = 11;
|
|
14009
|
-
LEGEND_PILL_FONT_W6 = LEGEND_PILL_FONT_SIZE4 * 0.6;
|
|
14010
|
-
LEGEND_PILL_PAD6 = 16;
|
|
14011
|
-
LEGEND_DOT_R7 = 4;
|
|
14012
|
-
LEGEND_ENTRY_FONT_SIZE5 = 10;
|
|
14013
|
-
LEGEND_ENTRY_FONT_W6 = LEGEND_ENTRY_FONT_SIZE5 * 0.6;
|
|
14014
|
-
LEGEND_ENTRY_DOT_GAP6 = 4;
|
|
14015
|
-
LEGEND_ENTRY_TRAIL6 = 8;
|
|
14016
|
-
LEGEND_CAPSULE_PAD6 = 4;
|
|
14017
14320
|
lineGenerator5 = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
|
|
14018
14321
|
}
|
|
14019
14322
|
});
|
|
@@ -14869,23 +15172,6 @@ function computeInfra(parsed, params = {}) {
|
|
|
14869
15172
|
const defaultLatencyMs = parseFloat(parsed.options["default-latency-ms"] ?? "") || 0;
|
|
14870
15173
|
const defaultUptime = parseFloat(parsed.options["default-uptime"] ?? "") || 100;
|
|
14871
15174
|
let effectiveNodes = parsed.nodes;
|
|
14872
|
-
if (params.scenario) {
|
|
14873
|
-
const overrides = params.scenario.overrides;
|
|
14874
|
-
effectiveNodes = parsed.nodes.map((node) => {
|
|
14875
|
-
const nodeOverrides = overrides[node.id];
|
|
14876
|
-
if (!nodeOverrides) return node;
|
|
14877
|
-
const props = node.properties.map((p) => {
|
|
14878
|
-
const ov = nodeOverrides[p.key];
|
|
14879
|
-
return ov != null ? { ...p, value: ov } : p;
|
|
14880
|
-
});
|
|
14881
|
-
for (const [key, val] of Object.entries(nodeOverrides)) {
|
|
14882
|
-
if (!props.some((p) => p.key === key)) {
|
|
14883
|
-
props.push({ key, value: val, lineNumber: node.lineNumber });
|
|
14884
|
-
}
|
|
14885
|
-
}
|
|
14886
|
-
return { ...node, properties: props };
|
|
14887
|
-
});
|
|
14888
|
-
}
|
|
14889
15175
|
if (params.propertyOverrides) {
|
|
14890
15176
|
const propOv = params.propertyOverrides;
|
|
14891
15177
|
effectiveNodes = effectiveNodes.map((node) => {
|
|
@@ -15322,6 +15608,7 @@ var init_compute = __esm({
|
|
|
15322
15608
|
// src/infra/layout.ts
|
|
15323
15609
|
var layout_exports8 = {};
|
|
15324
15610
|
__export(layout_exports8, {
|
|
15611
|
+
fixEdgeWaypoints: () => fixEdgeWaypoints,
|
|
15325
15612
|
layoutInfra: () => layoutInfra,
|
|
15326
15613
|
separateGroups: () => separateGroups
|
|
15327
15614
|
});
|
|
@@ -15506,6 +15793,8 @@ function formatUptime(fraction) {
|
|
|
15506
15793
|
return `${pct.toFixed(1)}%`;
|
|
15507
15794
|
}
|
|
15508
15795
|
function separateGroups(groups, nodes, isLR, maxIterations = 20) {
|
|
15796
|
+
const groupDeltas = /* @__PURE__ */ new Map();
|
|
15797
|
+
let converged = false;
|
|
15509
15798
|
for (let iter = 0; iter < maxIterations; iter++) {
|
|
15510
15799
|
let anyOverlap = false;
|
|
15511
15800
|
for (let i = 0; i < groups.length; i++) {
|
|
@@ -15523,6 +15812,9 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
|
|
|
15523
15812
|
const groupToShift = aCenter <= bCenter ? gb : ga;
|
|
15524
15813
|
if (isLR) groupToShift.y += shift;
|
|
15525
15814
|
else groupToShift.x += shift;
|
|
15815
|
+
const prev = groupDeltas.get(groupToShift.id) ?? { dx: 0, dy: 0 };
|
|
15816
|
+
if (isLR) groupDeltas.set(groupToShift.id, { dx: prev.dx, dy: prev.dy + shift });
|
|
15817
|
+
else groupDeltas.set(groupToShift.id, { dx: prev.dx + shift, dy: prev.dy });
|
|
15526
15818
|
for (const node of nodes) {
|
|
15527
15819
|
if (node.groupId === groupToShift.id) {
|
|
15528
15820
|
if (isLR) node.y += shift;
|
|
@@ -15531,19 +15823,48 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
|
|
|
15531
15823
|
}
|
|
15532
15824
|
}
|
|
15533
15825
|
}
|
|
15534
|
-
if (!anyOverlap)
|
|
15826
|
+
if (!anyOverlap) {
|
|
15827
|
+
converged = true;
|
|
15828
|
+
break;
|
|
15829
|
+
}
|
|
15830
|
+
}
|
|
15831
|
+
if (!converged && maxIterations > 0) {
|
|
15832
|
+
console.warn(`separateGroups: hit maxIterations (${maxIterations}) without fully resolving all group overlaps`);
|
|
15833
|
+
}
|
|
15834
|
+
return groupDeltas;
|
|
15835
|
+
}
|
|
15836
|
+
function fixEdgeWaypoints(edges, nodes, groupDeltas) {
|
|
15837
|
+
if (groupDeltas.size === 0) return;
|
|
15838
|
+
const nodeToGroup = /* @__PURE__ */ new Map();
|
|
15839
|
+
for (const node of nodes) nodeToGroup.set(node.id, node.groupId);
|
|
15840
|
+
for (const edge of edges) {
|
|
15841
|
+
const srcGroup = nodeToGroup.get(edge.sourceId) ?? null;
|
|
15842
|
+
const tgtGroup = nodeToGroup.get(edge.targetId) ?? null;
|
|
15843
|
+
const srcDelta = srcGroup ? groupDeltas.get(srcGroup) : void 0;
|
|
15844
|
+
const tgtDelta = tgtGroup ? groupDeltas.get(tgtGroup) : void 0;
|
|
15845
|
+
if (!srcDelta && !tgtDelta) continue;
|
|
15846
|
+
if (srcDelta && tgtDelta && srcGroup !== tgtGroup) {
|
|
15847
|
+
edge.points = [];
|
|
15848
|
+
continue;
|
|
15849
|
+
}
|
|
15850
|
+
const delta = srcDelta ?? tgtDelta;
|
|
15851
|
+
for (const pt of edge.points) {
|
|
15852
|
+
pt.x += delta.dx;
|
|
15853
|
+
pt.y += delta.dy;
|
|
15854
|
+
}
|
|
15535
15855
|
}
|
|
15536
15856
|
}
|
|
15537
15857
|
function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
15538
15858
|
if (computed.nodes.length === 0) {
|
|
15539
|
-
return { nodes: [], edges: [], groups: [], options: {}, width: 0, height: 0 };
|
|
15859
|
+
return { nodes: [], edges: [], groups: [], options: {}, direction: computed.direction, width: 0, height: 0 };
|
|
15540
15860
|
}
|
|
15861
|
+
const isLR = computed.direction !== "TB";
|
|
15541
15862
|
const g = new dagre7.graphlib.Graph();
|
|
15542
15863
|
g.setGraph({
|
|
15543
15864
|
rankdir: computed.direction === "TB" ? "TB" : "LR",
|
|
15544
|
-
nodesep:
|
|
15545
|
-
ranksep:
|
|
15546
|
-
edgesep:
|
|
15865
|
+
nodesep: isLR ? 70 : 60,
|
|
15866
|
+
ranksep: isLR ? 150 : 120,
|
|
15867
|
+
edgesep: 30
|
|
15547
15868
|
});
|
|
15548
15869
|
g.setDefaultEdgeLabel(() => ({}));
|
|
15549
15870
|
const groupedNodeIds = /* @__PURE__ */ new Set();
|
|
@@ -15551,7 +15872,6 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
15551
15872
|
if (node.groupId) groupedNodeIds.add(node.id);
|
|
15552
15873
|
}
|
|
15553
15874
|
const GROUP_INFLATE = GROUP_PADDING3 * 2 + GROUP_HEADER_HEIGHT;
|
|
15554
|
-
const isLR = computed.direction !== "TB";
|
|
15555
15875
|
const widthMap = /* @__PURE__ */ new Map();
|
|
15556
15876
|
const heightMap = /* @__PURE__ */ new Map();
|
|
15557
15877
|
for (const node of computed.nodes) {
|
|
@@ -15686,7 +16006,8 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
15686
16006
|
lineNumber: group.lineNumber
|
|
15687
16007
|
};
|
|
15688
16008
|
});
|
|
15689
|
-
separateGroups(layoutGroups, layoutNodes, isLR);
|
|
16009
|
+
const groupDeltas = separateGroups(layoutGroups, layoutNodes, isLR);
|
|
16010
|
+
fixEdgeWaypoints(layoutEdges, layoutNodes, groupDeltas);
|
|
15690
16011
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
15691
16012
|
for (const node of layoutNodes) {
|
|
15692
16013
|
const left = node.x - node.width / 2;
|
|
@@ -15744,6 +16065,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
15744
16065
|
edges: layoutEdges,
|
|
15745
16066
|
groups: layoutGroups,
|
|
15746
16067
|
options: computed.options,
|
|
16068
|
+
direction: computed.direction,
|
|
15747
16069
|
width: totalWidth,
|
|
15748
16070
|
height: totalHeight
|
|
15749
16071
|
};
|
|
@@ -15785,23 +16107,23 @@ var init_layout8 = __esm({
|
|
|
15785
16107
|
]);
|
|
15786
16108
|
DISPLAY_NAMES = {
|
|
15787
16109
|
"cache-hit": "cache hit",
|
|
15788
|
-
"firewall-block": "
|
|
16110
|
+
"firewall-block": "firewall block",
|
|
15789
16111
|
"ratelimit-rps": "rate limit RPS",
|
|
15790
16112
|
"latency-ms": "latency",
|
|
15791
16113
|
"uptime": "uptime",
|
|
15792
16114
|
"instances": "instances",
|
|
15793
16115
|
"max-rps": "max RPS",
|
|
15794
|
-
"cb-error-threshold": "CB error",
|
|
15795
|
-
"cb-latency-threshold-ms": "CB latency",
|
|
16116
|
+
"cb-error-threshold": "CB error threshold",
|
|
16117
|
+
"cb-latency-threshold-ms": "CB latency threshold",
|
|
15796
16118
|
"concurrency": "concurrency",
|
|
15797
16119
|
"duration-ms": "duration",
|
|
15798
16120
|
"cold-start-ms": "cold start",
|
|
15799
16121
|
"buffer": "buffer",
|
|
15800
|
-
"drain-rate": "drain",
|
|
16122
|
+
"drain-rate": "drain rate",
|
|
15801
16123
|
"retention-hours": "retention",
|
|
15802
16124
|
"partitions": "partitions"
|
|
15803
16125
|
};
|
|
15804
|
-
GROUP_GAP =
|
|
16126
|
+
GROUP_GAP = GROUP_PADDING3 * 2 + GROUP_HEADER_HEIGHT;
|
|
15805
16127
|
}
|
|
15806
16128
|
});
|
|
15807
16129
|
|
|
@@ -15876,6 +16198,236 @@ function resolveNodeSlo(node, diagramOptions) {
|
|
|
15876
16198
|
if (availThreshold == null && latencyP90 == null) return null;
|
|
15877
16199
|
return { availThreshold, latencyP90, warningMargin };
|
|
15878
16200
|
}
|
|
16201
|
+
function buildPathD(pts, direction) {
|
|
16202
|
+
const gen = d3Shape7.line().x((d) => d.x).y((d) => d.y);
|
|
16203
|
+
if (pts.length <= 2) {
|
|
16204
|
+
gen.curve(direction === "TB" ? d3Shape7.curveBumpY : d3Shape7.curveBumpX);
|
|
16205
|
+
} else {
|
|
16206
|
+
gen.curve(d3Shape7.curveCatmullRom.alpha(0.5));
|
|
16207
|
+
}
|
|
16208
|
+
return gen(pts) ?? "";
|
|
16209
|
+
}
|
|
16210
|
+
function computePortPts(edges, nodeMap, direction) {
|
|
16211
|
+
const srcPts = /* @__PURE__ */ new Map();
|
|
16212
|
+
const tgtPts = /* @__PURE__ */ new Map();
|
|
16213
|
+
const PAD = 0.1;
|
|
16214
|
+
const activeEdges = edges.filter((e) => e.points.length > 0);
|
|
16215
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
16216
|
+
for (const e of activeEdges) {
|
|
16217
|
+
if (!bySource.has(e.sourceId)) bySource.set(e.sourceId, []);
|
|
16218
|
+
bySource.get(e.sourceId).push(e);
|
|
16219
|
+
}
|
|
16220
|
+
for (const [sourceId, es] of bySource) {
|
|
16221
|
+
if (es.length < 2) continue;
|
|
16222
|
+
const source = nodeMap.get(sourceId);
|
|
16223
|
+
if (!source) continue;
|
|
16224
|
+
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);
|
|
16225
|
+
const n = sorted.length;
|
|
16226
|
+
for (let i = 0; i < n; i++) {
|
|
16227
|
+
const frac = n === 1 ? 0.5 : PAD + (1 - 2 * PAD) * i / (n - 1);
|
|
16228
|
+
const { e, t } = sorted[i];
|
|
16229
|
+
const isBackward = direction === "LR" ? t.x < source.x : t.y < source.y;
|
|
16230
|
+
if (direction === "LR") {
|
|
16231
|
+
srcPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16232
|
+
x: isBackward ? source.x - source.width / 2 : source.x + source.width / 2,
|
|
16233
|
+
y: source.y - source.height / 2 + frac * source.height
|
|
16234
|
+
});
|
|
16235
|
+
} else {
|
|
16236
|
+
srcPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16237
|
+
x: source.x - source.width / 2 + frac * source.width,
|
|
16238
|
+
y: isBackward ? source.y - source.height / 2 : source.y + source.height / 2
|
|
16239
|
+
});
|
|
16240
|
+
}
|
|
16241
|
+
}
|
|
16242
|
+
}
|
|
16243
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
16244
|
+
for (const e of activeEdges) {
|
|
16245
|
+
if (!byTarget.has(e.targetId)) byTarget.set(e.targetId, []);
|
|
16246
|
+
byTarget.get(e.targetId).push(e);
|
|
16247
|
+
}
|
|
16248
|
+
for (const [targetId, es] of byTarget) {
|
|
16249
|
+
if (es.length < 2) continue;
|
|
16250
|
+
const target = nodeMap.get(targetId);
|
|
16251
|
+
if (!target) continue;
|
|
16252
|
+
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);
|
|
16253
|
+
const n = sorted.length;
|
|
16254
|
+
for (let i = 0; i < n; i++) {
|
|
16255
|
+
const frac = n === 1 ? 0.5 : PAD + (1 - 2 * PAD) * i / (n - 1);
|
|
16256
|
+
const { e, s } = sorted[i];
|
|
16257
|
+
const isBackward = direction === "LR" ? target.x < s.x : target.y < s.y;
|
|
16258
|
+
if (direction === "LR") {
|
|
16259
|
+
tgtPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16260
|
+
x: isBackward ? target.x + target.width / 2 : target.x - target.width / 2,
|
|
16261
|
+
y: target.y - target.height / 2 + frac * target.height
|
|
16262
|
+
});
|
|
16263
|
+
} else {
|
|
16264
|
+
tgtPts.set(`${e.sourceId}:${e.targetId}`, {
|
|
16265
|
+
x: target.x - target.width / 2 + frac * target.width,
|
|
16266
|
+
y: isBackward ? target.y + target.height / 2 : target.y - target.height / 2
|
|
16267
|
+
});
|
|
16268
|
+
}
|
|
16269
|
+
}
|
|
16270
|
+
}
|
|
16271
|
+
return { srcPts, tgtPts };
|
|
16272
|
+
}
|
|
16273
|
+
function findRoutingLane(blocking, targetY, margin) {
|
|
16274
|
+
const MERGE_SLOP = 4;
|
|
16275
|
+
const sorted = [...blocking].sort((a, b) => a.y + a.height / 2 - (b.y + b.height / 2));
|
|
16276
|
+
const merged = [];
|
|
16277
|
+
for (const r of sorted) {
|
|
16278
|
+
const lo = r.y - MERGE_SLOP;
|
|
16279
|
+
const hi = r.y + r.height + MERGE_SLOP;
|
|
16280
|
+
if (merged.length && lo <= merged[merged.length - 1][1]) {
|
|
16281
|
+
merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], hi);
|
|
16282
|
+
} else {
|
|
16283
|
+
merged.push([lo, hi]);
|
|
16284
|
+
}
|
|
16285
|
+
}
|
|
16286
|
+
if (merged.length === 0) return targetY;
|
|
16287
|
+
const MIN_GAP = 10;
|
|
16288
|
+
const candidates = [
|
|
16289
|
+
merged[0][0] - margin,
|
|
16290
|
+
// above all blocking rects
|
|
16291
|
+
merged[merged.length - 1][1] + margin
|
|
16292
|
+
// below all blocking rects
|
|
16293
|
+
];
|
|
16294
|
+
for (let i = 0; i < merged.length - 1; i++) {
|
|
16295
|
+
const gapLo = merged[i][1];
|
|
16296
|
+
const gapHi = merged[i + 1][0];
|
|
16297
|
+
if (gapHi - gapLo >= MIN_GAP) {
|
|
16298
|
+
candidates.push((gapLo + gapHi) / 2);
|
|
16299
|
+
}
|
|
16300
|
+
}
|
|
16301
|
+
return candidates.reduce(
|
|
16302
|
+
(best, c) => Math.abs(c - targetY) < Math.abs(best - targetY) ? c : best,
|
|
16303
|
+
candidates[0]
|
|
16304
|
+
);
|
|
16305
|
+
}
|
|
16306
|
+
function segmentIntersectsRect(p1, p2, rect) {
|
|
16307
|
+
const { x: rx, y: ry, width: rw, height: rh } = rect;
|
|
16308
|
+
const rr = rx + rw;
|
|
16309
|
+
const rb = ry + rh;
|
|
16310
|
+
const inRect = (p) => p.x >= rx && p.x <= rr && p.y >= ry && p.y <= rb;
|
|
16311
|
+
if (inRect(p1) || inRect(p2)) return true;
|
|
16312
|
+
if (Math.max(p1.x, p2.x) < rx || Math.min(p1.x, p2.x) > rr) return false;
|
|
16313
|
+
if (Math.max(p1.y, p2.y) < ry || Math.min(p1.y, p2.y) > rb) return false;
|
|
16314
|
+
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
16315
|
+
const crosses = (a, b) => {
|
|
16316
|
+
const d1 = cross(a, b, p1);
|
|
16317
|
+
const d2 = cross(a, b, p2);
|
|
16318
|
+
const d3 = cross(p1, p2, a);
|
|
16319
|
+
const d4 = cross(p1, p2, b);
|
|
16320
|
+
return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0);
|
|
16321
|
+
};
|
|
16322
|
+
const tl = { x: rx, y: ry };
|
|
16323
|
+
const tr = { x: rr, y: ry };
|
|
16324
|
+
const br = { x: rr, y: rb };
|
|
16325
|
+
const bl = { x: rx, y: rb };
|
|
16326
|
+
return crosses(tl, tr) || crosses(tr, br) || crosses(br, bl) || crosses(bl, tl);
|
|
16327
|
+
}
|
|
16328
|
+
function curveIntersectsRect(sc, tc, rect, direction) {
|
|
16329
|
+
if (direction === "LR") {
|
|
16330
|
+
const midX = (sc.x + tc.x) / 2;
|
|
16331
|
+
const m1 = { x: midX, y: sc.y };
|
|
16332
|
+
const m2 = { x: midX, y: tc.y };
|
|
16333
|
+
return segmentIntersectsRect(sc, m1, rect) || segmentIntersectsRect(m1, m2, rect) || segmentIntersectsRect(m2, tc, rect);
|
|
16334
|
+
} else {
|
|
16335
|
+
const midY = (sc.y + tc.y) / 2;
|
|
16336
|
+
const m1 = { x: sc.x, y: midY };
|
|
16337
|
+
const m2 = { x: tc.x, y: midY };
|
|
16338
|
+
return segmentIntersectsRect(sc, m1, rect) || segmentIntersectsRect(m1, m2, rect) || segmentIntersectsRect(m2, tc, rect);
|
|
16339
|
+
}
|
|
16340
|
+
}
|
|
16341
|
+
function edgeWaypoints(source, target, groups, nodes, direction, margin = 30, srcExitPt, tgtEnterPt) {
|
|
16342
|
+
const sc = { x: source.x, y: source.y };
|
|
16343
|
+
const tc = { x: target.x, y: target.y };
|
|
16344
|
+
const isBackward = direction === "LR" ? tc.x < sc.x : tc.y < sc.y;
|
|
16345
|
+
if (isBackward) {
|
|
16346
|
+
if (direction === "LR") {
|
|
16347
|
+
const xBandObs = [];
|
|
16348
|
+
for (const g of groups) {
|
|
16349
|
+
if (g.x + g.width < tc.x - margin || g.x > sc.x + margin) continue;
|
|
16350
|
+
xBandObs.push({ x: g.x, y: g.y, width: g.width, height: g.height });
|
|
16351
|
+
}
|
|
16352
|
+
for (const n of nodes) {
|
|
16353
|
+
if (n.id === source.id || n.id === target.id) continue;
|
|
16354
|
+
const nLeft = n.x - n.width / 2;
|
|
16355
|
+
const nRight = n.x + n.width / 2;
|
|
16356
|
+
if (nRight < tc.x - margin || nLeft > sc.x + margin) continue;
|
|
16357
|
+
xBandObs.push({ x: nLeft, y: n.y - n.height / 2, width: n.width, height: n.height });
|
|
16358
|
+
}
|
|
16359
|
+
const midY = (sc.y + tc.y) / 2;
|
|
16360
|
+
const routeY2 = xBandObs.length > 0 ? findRoutingLane(xBandObs, midY, margin) : midY;
|
|
16361
|
+
const exitBorder = srcExitPt ?? nodeBorderPoint(source, { x: sc.x, y: routeY2 });
|
|
16362
|
+
const exitPt2 = { x: exitBorder.x, y: routeY2 };
|
|
16363
|
+
const enterPt2 = { x: tc.x, y: routeY2 };
|
|
16364
|
+
const tp2 = tgtEnterPt ?? nodeBorderPoint(target, enterPt2);
|
|
16365
|
+
return srcExitPt ? [srcExitPt, exitPt2, enterPt2, tp2] : [exitBorder, exitPt2, enterPt2, tp2];
|
|
16366
|
+
} else {
|
|
16367
|
+
const yBandObs = [];
|
|
16368
|
+
for (const g of groups) {
|
|
16369
|
+
if (g.y + g.height < tc.y - margin || g.y > sc.y + margin) continue;
|
|
16370
|
+
yBandObs.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 nTop = n.y - n.height / 2;
|
|
16375
|
+
const nBot = n.y + n.height / 2;
|
|
16376
|
+
if (nBot < tc.y - margin || nTop > sc.y + margin) continue;
|
|
16377
|
+
yBandObs.push({ x: n.x - n.width / 2, y: nTop, width: n.width, height: n.height });
|
|
16378
|
+
}
|
|
16379
|
+
const rotated = yBandObs.map((r) => ({ x: r.y, y: r.x, width: r.height, height: r.width }));
|
|
16380
|
+
const midX = (sc.x + tc.x) / 2;
|
|
16381
|
+
const routeX = rotated.length > 0 ? findRoutingLane(rotated, midX, margin) : midX;
|
|
16382
|
+
const exitPt2 = srcExitPt ?? { x: routeX, y: sc.y };
|
|
16383
|
+
const enterPt2 = { x: routeX, y: tc.y };
|
|
16384
|
+
return [
|
|
16385
|
+
srcExitPt ?? nodeBorderPoint(source, exitPt2),
|
|
16386
|
+
exitPt2,
|
|
16387
|
+
enterPt2,
|
|
16388
|
+
tgtEnterPt ?? nodeBorderPoint(target, enterPt2)
|
|
16389
|
+
];
|
|
16390
|
+
}
|
|
16391
|
+
}
|
|
16392
|
+
const blocking = [];
|
|
16393
|
+
const blockingGroupIds = /* @__PURE__ */ new Set();
|
|
16394
|
+
const pathSrc = srcExitPt ?? sc;
|
|
16395
|
+
const pathTgt = tgtEnterPt ?? tc;
|
|
16396
|
+
for (const g of groups) {
|
|
16397
|
+
if (g.id === source.groupId || g.id === target.groupId) continue;
|
|
16398
|
+
const gRect = { x: g.x, y: g.y, width: g.width, height: g.height };
|
|
16399
|
+
if (curveIntersectsRect(pathSrc, pathTgt, gRect, direction)) {
|
|
16400
|
+
blocking.push(gRect);
|
|
16401
|
+
blockingGroupIds.add(g.id);
|
|
16402
|
+
}
|
|
16403
|
+
}
|
|
16404
|
+
for (const n of nodes) {
|
|
16405
|
+
if (n.id === source.id || n.id === target.id) continue;
|
|
16406
|
+
if (n.groupId && (n.groupId === source.groupId || n.groupId === target.groupId)) continue;
|
|
16407
|
+
if (n.groupId && blockingGroupIds.has(n.groupId)) continue;
|
|
16408
|
+
const nodeRect = { x: n.x - n.width / 2, y: n.y - n.height / 2, width: n.width, height: n.height };
|
|
16409
|
+
if (curveIntersectsRect(pathSrc, pathTgt, nodeRect, direction)) {
|
|
16410
|
+
blocking.push(nodeRect);
|
|
16411
|
+
}
|
|
16412
|
+
}
|
|
16413
|
+
if (blocking.length === 0) {
|
|
16414
|
+
const sp = srcExitPt ?? nodeBorderPoint(source, tc);
|
|
16415
|
+
const tp2 = tgtEnterPt ?? nodeBorderPoint(target, sp);
|
|
16416
|
+
return [sp, tp2];
|
|
16417
|
+
}
|
|
16418
|
+
const obsLeft = Math.min(...blocking.map((o) => o.x));
|
|
16419
|
+
const obsRight = Math.max(...blocking.map((o) => o.x + o.width));
|
|
16420
|
+
const routeY = findRoutingLane(blocking, tc.y, margin);
|
|
16421
|
+
const exitX = direction === "LR" ? Math.max(sc.x, obsLeft - margin) : obsLeft - margin;
|
|
16422
|
+
const enterX = direction === "LR" ? Math.min(tc.x, obsRight + margin) : obsRight + margin;
|
|
16423
|
+
const exitPt = { x: exitX, y: routeY };
|
|
16424
|
+
const enterPt = { x: enterX, y: routeY };
|
|
16425
|
+
const tp = tgtEnterPt ?? nodeBorderPoint(target, enterPt);
|
|
16426
|
+
if (srcExitPt) {
|
|
16427
|
+
return [srcExitPt, exitPt, enterPt, tp];
|
|
16428
|
+
}
|
|
16429
|
+
return [nodeBorderPoint(source, exitPt), exitPt, enterPt, tp];
|
|
16430
|
+
}
|
|
15879
16431
|
function nodeBorderPoint(node, target) {
|
|
15880
16432
|
const hw = node.width / 2;
|
|
15881
16433
|
const hh = node.height / 2;
|
|
@@ -16159,33 +16711,29 @@ function renderGroups(svg, groups, palette, isDark) {
|
|
|
16159
16711
|
}
|
|
16160
16712
|
}
|
|
16161
16713
|
}
|
|
16162
|
-
function renderEdgePaths(svg, edges, nodes, palette, isDark, animate) {
|
|
16714
|
+
function renderEdgePaths(svg, edges, nodes, groups, palette, isDark, animate, direction) {
|
|
16163
16715
|
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
16164
16716
|
const maxRps = Math.max(...edges.map((e) => e.computedRps), 1);
|
|
16717
|
+
const { srcPts, tgtPts } = computePortPts(edges, nodeMap, direction);
|
|
16165
16718
|
for (const edge of edges) {
|
|
16166
16719
|
if (edge.points.length === 0) continue;
|
|
16167
16720
|
const targetNode = nodeMap.get(edge.targetId);
|
|
16168
16721
|
const sourceNode = nodeMap.get(edge.sourceId);
|
|
16169
16722
|
const color = edgeColor(edge, palette);
|
|
16170
16723
|
const strokeW = edgeWidth();
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
if (targetNode && pts.length > 0) {
|
|
16185
|
-
const bp = nodeBorderPoint(targetNode, pts[pts.length - 1]);
|
|
16186
|
-
pts = [...pts, bp];
|
|
16187
|
-
}
|
|
16188
|
-
const pathD = lineGenerator7(pts) ?? "";
|
|
16724
|
+
if (!sourceNode || !targetNode) continue;
|
|
16725
|
+
const key = `${edge.sourceId}:${edge.targetId}`;
|
|
16726
|
+
const pts = edgeWaypoints(
|
|
16727
|
+
sourceNode,
|
|
16728
|
+
targetNode,
|
|
16729
|
+
groups,
|
|
16730
|
+
nodes,
|
|
16731
|
+
direction,
|
|
16732
|
+
30,
|
|
16733
|
+
srcPts.get(key),
|
|
16734
|
+
tgtPts.get(key)
|
|
16735
|
+
);
|
|
16736
|
+
const pathD = buildPathD(pts, direction);
|
|
16189
16737
|
const edgeG = svg.append("g").attr("class", "infra-edge").attr("data-line-number", edge.lineNumber);
|
|
16190
16738
|
edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", color).attr("stroke-width", strokeW);
|
|
16191
16739
|
if (animate && edge.computedRps > 0) {
|
|
@@ -16200,19 +16748,34 @@ function renderEdgePaths(svg, edges, nodes, palette, isDark, animate) {
|
|
|
16200
16748
|
}
|
|
16201
16749
|
}
|
|
16202
16750
|
}
|
|
16203
|
-
function renderEdgeLabels(svg, edges, palette, isDark, animate) {
|
|
16751
|
+
function renderEdgeLabels(svg, edges, nodes, groups, palette, isDark, animate, direction) {
|
|
16752
|
+
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
16753
|
+
const { srcPts, tgtPts } = computePortPts(edges, nodeMap, direction);
|
|
16204
16754
|
for (const edge of edges) {
|
|
16205
16755
|
if (edge.points.length === 0) continue;
|
|
16206
16756
|
if (!edge.label) continue;
|
|
16207
|
-
const
|
|
16208
|
-
const
|
|
16757
|
+
const sourceNode = nodeMap.get(edge.sourceId);
|
|
16758
|
+
const targetNode = nodeMap.get(edge.targetId);
|
|
16759
|
+
if (!sourceNode || !targetNode) continue;
|
|
16760
|
+
const key = `${edge.sourceId}:${edge.targetId}`;
|
|
16761
|
+
const wps = edgeWaypoints(
|
|
16762
|
+
sourceNode,
|
|
16763
|
+
targetNode,
|
|
16764
|
+
groups,
|
|
16765
|
+
nodes,
|
|
16766
|
+
direction,
|
|
16767
|
+
30,
|
|
16768
|
+
srcPts.get(key),
|
|
16769
|
+
tgtPts.get(key)
|
|
16770
|
+
);
|
|
16771
|
+
const midPt = wps[Math.floor(wps.length / 2)];
|
|
16209
16772
|
const labelText = edge.label;
|
|
16210
16773
|
const g = svg.append("g").attr("class", animate ? "infra-edge-label" : "");
|
|
16211
16774
|
const textWidth = labelText.length * 6.5 + 8;
|
|
16212
16775
|
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);
|
|
16213
16776
|
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);
|
|
16214
16777
|
if (animate) {
|
|
16215
|
-
const pathD =
|
|
16778
|
+
const pathD = buildPathD(wps, direction);
|
|
16216
16779
|
g.insert("path", ":first-child").attr("d", pathD).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", 20);
|
|
16217
16780
|
}
|
|
16218
16781
|
}
|
|
@@ -16484,16 +17047,16 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
16484
17047
|
color: r.color,
|
|
16485
17048
|
key: r.name.toLowerCase().replace(/\s+/g, "-")
|
|
16486
17049
|
}));
|
|
16487
|
-
const pillWidth = "Capabilities".length *
|
|
17050
|
+
const pillWidth = "Capabilities".length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
16488
17051
|
let entriesWidth = 0;
|
|
16489
17052
|
for (const e of entries) {
|
|
16490
|
-
entriesWidth +=
|
|
17053
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + e.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
16491
17054
|
}
|
|
16492
17055
|
groups.push({
|
|
16493
17056
|
name: "Capabilities",
|
|
16494
17057
|
type: "role",
|
|
16495
17058
|
entries,
|
|
16496
|
-
width:
|
|
17059
|
+
width: LEGEND_CAPSULE_PAD * 2 + pillWidth + 4 + entriesWidth,
|
|
16497
17060
|
minifiedWidth: pillWidth
|
|
16498
17061
|
});
|
|
16499
17062
|
}
|
|
@@ -16509,123 +17072,72 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
16509
17072
|
}
|
|
16510
17073
|
}
|
|
16511
17074
|
if (entries.length === 0) continue;
|
|
16512
|
-
const pillWidth = tg.name.length *
|
|
17075
|
+
const pillWidth = tg.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
16513
17076
|
let entriesWidth = 0;
|
|
16514
17077
|
for (const e of entries) {
|
|
16515
|
-
entriesWidth +=
|
|
17078
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + e.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
16516
17079
|
}
|
|
16517
17080
|
groups.push({
|
|
16518
17081
|
name: tg.name,
|
|
16519
17082
|
type: "tag",
|
|
16520
17083
|
tagKey: (tg.alias ?? tg.name).toLowerCase(),
|
|
16521
17084
|
entries,
|
|
16522
|
-
width:
|
|
17085
|
+
width: LEGEND_CAPSULE_PAD * 2 + pillWidth + 4 + entriesWidth,
|
|
16523
17086
|
minifiedWidth: pillWidth
|
|
16524
17087
|
});
|
|
16525
17088
|
}
|
|
16526
17089
|
return groups;
|
|
16527
17090
|
}
|
|
16528
|
-
function
|
|
16529
|
-
if (
|
|
16530
|
-
const pillWidth = "Playback".length * LEGEND_PILL_FONT_W7 + LEGEND_PILL_PAD7;
|
|
16531
|
-
if (!playback.expanded) return pillWidth;
|
|
16532
|
-
let entriesW = 8;
|
|
16533
|
-
entriesW += LEGEND_PILL_FONT_SIZE5 * 0.8 + 6;
|
|
16534
|
-
for (const s of playback.speedOptions) {
|
|
16535
|
-
entriesW += `${s}x`.length * LEGEND_ENTRY_FONT_W7 + SPEED_BADGE_H_PAD * 2 + SPEED_BADGE_GAP;
|
|
16536
|
-
}
|
|
16537
|
-
return LEGEND_CAPSULE_PAD7 * 2 + pillWidth + entriesW;
|
|
16538
|
-
}
|
|
16539
|
-
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback) {
|
|
16540
|
-
if (legendGroups.length === 0 && !playback) return;
|
|
17091
|
+
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup) {
|
|
17092
|
+
if (legendGroups.length === 0) return;
|
|
16541
17093
|
const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
|
|
17094
|
+
if (activeGroup) {
|
|
17095
|
+
legendG.attr("data-legend-active", activeGroup.toLowerCase());
|
|
17096
|
+
}
|
|
16542
17097
|
const effectiveW = (g) => activeGroup != null && g.name.toLowerCase() === activeGroup.toLowerCase() ? g.width : g.minifiedWidth;
|
|
16543
|
-
const
|
|
16544
|
-
const trailingGaps = legendGroups.length > 0 && playbackW > 0 ? LEGEND_GROUP_GAP5 : 0;
|
|
16545
|
-
const totalLegendW = legendGroups.reduce((s, g) => s + effectiveW(g), 0) + (legendGroups.length - 1) * LEGEND_GROUP_GAP5 + trailingGaps + playbackW;
|
|
17098
|
+
const totalLegendW = legendGroups.reduce((s, g) => s + effectiveW(g), 0) + (legendGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
16546
17099
|
let cursorX = (totalWidth - totalLegendW) / 2;
|
|
16547
17100
|
for (const group of legendGroups) {
|
|
16548
17101
|
const isActive = activeGroup != null && group.name.toLowerCase() === activeGroup.toLowerCase();
|
|
16549
17102
|
const groupBg = isDark ? mix(palette.bg, palette.text, 85) : mix(palette.bg, palette.text, 92);
|
|
16550
17103
|
const pillLabel = group.name;
|
|
16551
|
-
const pillWidth = pillLabel.length *
|
|
16552
|
-
const gEl = legendG.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "infra-legend-group").attr("data-legend-group", group.name.toLowerCase()).
|
|
17104
|
+
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
17105
|
+
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");
|
|
16553
17106
|
if (isActive) {
|
|
16554
|
-
gEl.append("rect").attr("width", group.width).attr("height",
|
|
17107
|
+
gEl.append("rect").attr("width", group.width).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
16555
17108
|
}
|
|
16556
|
-
const pillXOff = isActive ?
|
|
16557
|
-
const pillYOff = isActive ?
|
|
16558
|
-
const pillH =
|
|
17109
|
+
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
17110
|
+
const pillYOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
17111
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
16559
17112
|
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);
|
|
16560
17113
|
if (isActive) {
|
|
16561
|
-
gEl.append("rect").attr("x", pillXOff).attr("y", pillYOff).attr("width", pillWidth).attr("height", pillH).attr("rx", pillH / 2).attr("fill", "none").attr("stroke",
|
|
17114
|
+
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);
|
|
16562
17115
|
}
|
|
16563
|
-
gEl.append("text").attr("x", pillXOff + pillWidth / 2).attr("y",
|
|
17116
|
+
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);
|
|
16564
17117
|
if (isActive) {
|
|
16565
17118
|
let entryX = pillXOff + pillWidth + 4;
|
|
16566
17119
|
for (const entry of group.entries) {
|
|
16567
|
-
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-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
const textX = entryX + LEGEND_DOT_R8 * 2 + LEGEND_ENTRY_DOT_GAP7;
|
|
16573
|
-
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);
|
|
16574
|
-
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W7 + LEGEND_ENTRY_TRAIL7;
|
|
17120
|
+
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");
|
|
17121
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
17122
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
17123
|
+
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);
|
|
17124
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
16575
17125
|
}
|
|
16576
17126
|
}
|
|
16577
|
-
cursorX += effectiveW(group) +
|
|
17127
|
+
cursorX += effectiveW(group) + LEGEND_GROUP_GAP;
|
|
16578
17128
|
}
|
|
16579
|
-
|
|
16580
|
-
|
|
16581
|
-
const groupBg = isDark ? mix(palette.bg, palette.text, 85) : mix(palette.bg, palette.text, 92);
|
|
16582
|
-
const pillLabel = "Playback";
|
|
16583
|
-
const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W7 + LEGEND_PILL_PAD7;
|
|
16584
|
-
const fullW = computePlaybackWidth(playback);
|
|
16585
|
-
const pbG = legendG.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "infra-legend-group infra-playback-pill").style("cursor", "pointer");
|
|
16586
|
-
if (isExpanded) {
|
|
16587
|
-
pbG.append("rect").attr("width", fullW).attr("height", LEGEND_HEIGHT8).attr("rx", LEGEND_HEIGHT8 / 2).attr("fill", groupBg);
|
|
16588
|
-
}
|
|
16589
|
-
const pillXOff = isExpanded ? LEGEND_CAPSULE_PAD7 : 0;
|
|
16590
|
-
const pillYOff = isExpanded ? LEGEND_CAPSULE_PAD7 : 0;
|
|
16591
|
-
const pillH = LEGEND_HEIGHT8 - (isExpanded ? LEGEND_CAPSULE_PAD7 * 2 : 0);
|
|
16592
|
-
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);
|
|
16593
|
-
if (isExpanded) {
|
|
16594
|
-
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);
|
|
16595
|
-
}
|
|
16596
|
-
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);
|
|
16597
|
-
if (isExpanded) {
|
|
16598
|
-
let entryX = pillXOff + pillWidth + 8;
|
|
16599
|
-
const entryY = LEGEND_HEIGHT8 / 2 + LEGEND_ENTRY_FONT_SIZE6 / 2 - 1;
|
|
16600
|
-
const ppLabel = playback.paused ? "\u25B6" : "\u23F8";
|
|
16601
|
-
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);
|
|
16602
|
-
entryX += LEGEND_PILL_FONT_SIZE5 * 0.8 + 6;
|
|
16603
|
-
for (const s of playback.speedOptions) {
|
|
16604
|
-
const label = `${s}x`;
|
|
16605
|
-
const isActive = playback.speed === s;
|
|
16606
|
-
const slotW = label.length * LEGEND_ENTRY_FONT_W7 + SPEED_BADGE_H_PAD * 2;
|
|
16607
|
-
const badgeH = LEGEND_ENTRY_FONT_SIZE6 + SPEED_BADGE_V_PAD * 2;
|
|
16608
|
-
const badgeY = (LEGEND_HEIGHT8 - badgeH) / 2;
|
|
16609
|
-
const speedG = pbG.append("g").attr("data-playback-action", "set-speed").attr("data-playback-value", String(s)).style("cursor", "pointer");
|
|
16610
|
-
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");
|
|
16611
|
-
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);
|
|
16612
|
-
entryX += slotW + SPEED_BADGE_GAP;
|
|
16613
|
-
}
|
|
16614
|
-
}
|
|
16615
|
-
cursorX += fullW + LEGEND_GROUP_GAP5;
|
|
16616
|
-
}
|
|
16617
|
-
}
|
|
16618
|
-
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
17129
|
+
}
|
|
17130
|
+
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, _playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
16619
17131
|
d3Selection9.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
16620
17132
|
const legendGroups = computeInfraLegendGroups(layout.nodes, tagGroups ?? [], palette, layout.edges);
|
|
16621
|
-
const hasLegend = legendGroups.length > 0
|
|
17133
|
+
const hasLegend = legendGroups.length > 0;
|
|
16622
17134
|
const fixedLegend = !exportMode && hasLegend;
|
|
16623
|
-
const legendOffset = hasLegend && !fixedLegend ?
|
|
17135
|
+
const legendOffset = hasLegend && !fixedLegend ? LEGEND_HEIGHT : 0;
|
|
16624
17136
|
const titleOffset = title ? 40 : 0;
|
|
16625
17137
|
const totalWidth = layout.width;
|
|
16626
17138
|
const totalHeight = layout.height + titleOffset + legendOffset;
|
|
16627
17139
|
const shouldAnimate = animate !== false;
|
|
16628
|
-
const rootSvg = d3Selection9.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", "100%").attr("height", fixedLegend ? `calc(100% - ${
|
|
17140
|
+
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");
|
|
16629
17141
|
if (shouldAnimate) {
|
|
16630
17142
|
rootSvg.append("style").text(`
|
|
16631
17143
|
@keyframes infra-pulse-warning {
|
|
@@ -16673,7 +17185,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
16673
17185
|
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);
|
|
16674
17186
|
}
|
|
16675
17187
|
renderGroups(svg, layout.groups, palette, isDark);
|
|
16676
|
-
renderEdgePaths(svg, layout.edges, layout.nodes, palette, isDark, shouldAnimate);
|
|
17188
|
+
renderEdgePaths(svg, layout.edges, layout.nodes, layout.groups, palette, isDark, shouldAnimate, layout.direction);
|
|
16677
17189
|
const fanoutSourceIds = collectFanoutSourceIds(layout.edges);
|
|
16678
17190
|
const scaledGroupIds = new Set(
|
|
16679
17191
|
layout.groups.filter((g) => {
|
|
@@ -16685,14 +17197,14 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
16685
17197
|
if (shouldAnimate) {
|
|
16686
17198
|
renderRejectParticles(svg, layout.nodes);
|
|
16687
17199
|
}
|
|
16688
|
-
renderEdgeLabels(svg, layout.edges, palette, isDark, shouldAnimate);
|
|
17200
|
+
renderEdgeLabels(svg, layout.edges, layout.nodes, layout.groups, palette, isDark, shouldAnimate, layout.direction);
|
|
16689
17201
|
if (hasLegend) {
|
|
16690
17202
|
if (fixedLegend) {
|
|
16691
17203
|
const containerWidth = container.clientWidth || totalWidth;
|
|
16692
|
-
const legendSvg = d3Selection9.select(container).append("svg").attr("class", "infra-legend-fixed").attr("width", "100%").attr("height",
|
|
16693
|
-
renderLegend3(legendSvg, legendGroups, containerWidth, LEGEND_FIXED_GAP3 / 2, palette, isDark, activeGroup ?? null
|
|
17204
|
+
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");
|
|
17205
|
+
renderLegend3(legendSvg, legendGroups, containerWidth, LEGEND_FIXED_GAP3 / 2, palette, isDark, activeGroup ?? null);
|
|
16694
17206
|
} else {
|
|
16695
|
-
renderLegend3(rootSvg, legendGroups, totalWidth, titleOffset + layout.height + 4, palette, isDark, activeGroup ?? null
|
|
17207
|
+
renderLegend3(rootSvg, legendGroups, totalWidth, titleOffset + layout.height + 4, palette, isDark, activeGroup ?? null);
|
|
16696
17208
|
}
|
|
16697
17209
|
}
|
|
16698
17210
|
}
|
|
@@ -16703,7 +17215,7 @@ function parseAndLayoutInfra(content) {
|
|
|
16703
17215
|
const layout = layoutInfra(computed);
|
|
16704
17216
|
return { parsed, computed, layout };
|
|
16705
17217
|
}
|
|
16706
|
-
var 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,
|
|
17218
|
+
var 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;
|
|
16707
17219
|
var init_renderer8 = __esm({
|
|
16708
17220
|
"src/infra/renderer.ts"() {
|
|
16709
17221
|
"use strict";
|
|
@@ -16714,6 +17226,7 @@ var init_renderer8 = __esm({
|
|
|
16714
17226
|
init_parser9();
|
|
16715
17227
|
init_compute();
|
|
16716
17228
|
init_layout8();
|
|
17229
|
+
init_legend_constants();
|
|
16717
17230
|
NODE_FONT_SIZE4 = 13;
|
|
16718
17231
|
META_FONT_SIZE4 = 10;
|
|
16719
17232
|
META_LINE_HEIGHT8 = 14;
|
|
@@ -16729,21 +17242,7 @@ var init_renderer8 = __esm({
|
|
|
16729
17242
|
NODE_PAD_BOTTOM2 = 10;
|
|
16730
17243
|
COLLAPSE_BAR_HEIGHT5 = 6;
|
|
16731
17244
|
COLLAPSE_BAR_INSET2 = 0;
|
|
16732
|
-
LEGEND_HEIGHT8 = 28;
|
|
16733
|
-
LEGEND_PILL_PAD7 = 16;
|
|
16734
|
-
LEGEND_PILL_FONT_SIZE5 = 11;
|
|
16735
|
-
LEGEND_PILL_FONT_W7 = LEGEND_PILL_FONT_SIZE5 * 0.6;
|
|
16736
|
-
LEGEND_CAPSULE_PAD7 = 4;
|
|
16737
|
-
LEGEND_DOT_R8 = 4;
|
|
16738
|
-
LEGEND_ENTRY_FONT_SIZE6 = 10;
|
|
16739
|
-
LEGEND_ENTRY_FONT_W7 = LEGEND_ENTRY_FONT_SIZE6 * 0.6;
|
|
16740
|
-
LEGEND_ENTRY_DOT_GAP7 = 4;
|
|
16741
|
-
LEGEND_ENTRY_TRAIL7 = 8;
|
|
16742
|
-
LEGEND_GROUP_GAP5 = 12;
|
|
16743
17245
|
LEGEND_FIXED_GAP3 = 16;
|
|
16744
|
-
SPEED_BADGE_H_PAD = 5;
|
|
16745
|
-
SPEED_BADGE_V_PAD = 3;
|
|
16746
|
-
SPEED_BADGE_GAP = 6;
|
|
16747
17246
|
COLOR_HEALTHY = "#22c55e";
|
|
16748
17247
|
COLOR_WARNING = "#eab308";
|
|
16749
17248
|
COLOR_OVERLOADED = "#ef4444";
|
|
@@ -16760,7 +17259,6 @@ var init_renderer8 = __esm({
|
|
|
16760
17259
|
REJECT_DURATION_MAX = 3;
|
|
16761
17260
|
REJECT_COUNT_MIN = 1;
|
|
16762
17261
|
REJECT_COUNT_MAX = 3;
|
|
16763
|
-
lineGenerator7 = d3Shape7.line().x((d) => d.x).y((d) => d.y).curve(d3Shape7.curveBasis);
|
|
16764
17262
|
PROP_DISPLAY = {
|
|
16765
17263
|
"cache-hit": "cache hit",
|
|
16766
17264
|
"firewall-block": "firewall block",
|
|
@@ -16945,7 +17443,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
|
|
|
16945
17443
|
}
|
|
16946
17444
|
}
|
|
16947
17445
|
} else if (edge.points.length >= 2) {
|
|
16948
|
-
const pathD =
|
|
17446
|
+
const pathD = lineGenerator7(edge.points);
|
|
16949
17447
|
if (pathD) {
|
|
16950
17448
|
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");
|
|
16951
17449
|
}
|
|
@@ -17009,7 +17507,7 @@ function renderStateForExport(content, theme, palette) {
|
|
|
17009
17507
|
document.body.removeChild(container);
|
|
17010
17508
|
}
|
|
17011
17509
|
}
|
|
17012
|
-
var 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,
|
|
17510
|
+
var 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;
|
|
17013
17511
|
var init_state_renderer = __esm({
|
|
17014
17512
|
"src/graph/state-renderer.ts"() {
|
|
17015
17513
|
"use strict";
|
|
@@ -17029,7 +17527,7 @@ var init_state_renderer = __esm({
|
|
|
17029
17527
|
PSEUDOSTATE_RADIUS = 10;
|
|
17030
17528
|
STATE_CORNER_RADIUS = 10;
|
|
17031
17529
|
GROUP_EXTRA_PADDING2 = 12;
|
|
17032
|
-
|
|
17530
|
+
lineGenerator7 = d3Shape8.line().x((d) => d.x).y((d) => d.y).curve(d3Shape8.curveBasis);
|
|
17033
17531
|
}
|
|
17034
17532
|
});
|
|
17035
17533
|
|
|
@@ -17764,9 +18262,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17764
18262
|
const GROUP_PADDING_BOTTOM = 8;
|
|
17765
18263
|
const GROUP_LABEL_SIZE = 11;
|
|
17766
18264
|
const titleOffset = title ? TITLE_HEIGHT5 : 0;
|
|
17767
|
-
const legendOffset = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT9 + LEGEND_BOTTOM_GAP : 0;
|
|
17768
18265
|
const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
|
|
17769
|
-
const participantStartY = TOP_MARGIN + titleOffset +
|
|
18266
|
+
const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
|
|
17770
18267
|
const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
|
|
17771
18268
|
const hasActors = participants.some((p) => p.type === "actor");
|
|
17772
18269
|
const messageStartOffset = MESSAGE_START_OFFSET + (hasActors ? 20 : 0);
|
|
@@ -17848,7 +18345,9 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17848
18345
|
participants.length * PARTICIPANT_GAP,
|
|
17849
18346
|
PARTICIPANT_BOX_WIDTH + 40
|
|
17850
18347
|
);
|
|
17851
|
-
const
|
|
18348
|
+
const contentHeight = participantStartY + PARTICIPANT_BOX_HEIGHT + Math.max(lifelineLength, 40) + 40;
|
|
18349
|
+
const legendSpace = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT : 0;
|
|
18350
|
+
const totalHeight = contentHeight + legendSpace;
|
|
17852
18351
|
const containerWidth = options?.exportWidth ?? container.getBoundingClientRect().width;
|
|
17853
18352
|
const svgWidth = Math.max(totalWidth, containerWidth);
|
|
17854
18353
|
const diagramWidth = participants.length * PARTICIPANT_GAP;
|
|
@@ -17906,13 +18405,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17906
18405
|
}
|
|
17907
18406
|
}
|
|
17908
18407
|
if (parsed.tagGroups.length > 0) {
|
|
17909
|
-
const legendY =
|
|
18408
|
+
const legendY = contentHeight;
|
|
17910
18409
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
17911
18410
|
const legendItems = [];
|
|
17912
18411
|
for (const tg of parsed.tagGroups) {
|
|
17913
18412
|
if (tg.entries.length === 0) continue;
|
|
17914
18413
|
const isActive = !!activeTagGroup && tg.name.toLowerCase() === activeTagGroup.toLowerCase();
|
|
17915
|
-
const pillWidth = tg.name.length *
|
|
18414
|
+
const pillWidth = tg.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
17916
18415
|
const entries = tg.entries.map((e) => ({
|
|
17917
18416
|
value: e.value,
|
|
17918
18417
|
color: resolveColor(e.color)
|
|
@@ -17921,38 +18420,42 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
17921
18420
|
if (isActive) {
|
|
17922
18421
|
let entriesWidth = 0;
|
|
17923
18422
|
for (const entry of entries) {
|
|
17924
|
-
entriesWidth +=
|
|
18423
|
+
entriesWidth += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
17925
18424
|
}
|
|
17926
|
-
totalWidth2 =
|
|
18425
|
+
totalWidth2 = LEGEND_CAPSULE_PAD * 2 + pillWidth + 4 + entriesWidth;
|
|
17927
18426
|
}
|
|
17928
18427
|
legendItems.push({ group: tg, isActive, pillWidth, totalWidth: totalWidth2, entries });
|
|
17929
18428
|
}
|
|
17930
|
-
const totalLegendWidth = legendItems.reduce((s, item) => s + item.totalWidth, 0) + (legendItems.length - 1) *
|
|
18429
|
+
const totalLegendWidth = legendItems.reduce((s, item) => s + item.totalWidth, 0) + (legendItems.length - 1) * LEGEND_GROUP_GAP;
|
|
17931
18430
|
let legendX = (svgWidth - totalLegendWidth) / 2;
|
|
18431
|
+
const legendContainer = svg.append("g").attr("class", "sequence-legend");
|
|
18432
|
+
if (activeTagGroup) {
|
|
18433
|
+
legendContainer.attr("data-legend-active", activeTagGroup.toLowerCase());
|
|
18434
|
+
}
|
|
17932
18435
|
for (const item of legendItems) {
|
|
17933
|
-
const gEl =
|
|
18436
|
+
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");
|
|
17934
18437
|
if (item.isActive) {
|
|
17935
|
-
gEl.append("rect").attr("width", item.totalWidth).attr("height",
|
|
18438
|
+
gEl.append("rect").attr("width", item.totalWidth).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
|
|
17936
18439
|
}
|
|
17937
|
-
const pillXOff = item.isActive ?
|
|
17938
|
-
const pillYOff = item.isActive ?
|
|
17939
|
-
const pillH =
|
|
18440
|
+
const pillXOff = item.isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
18441
|
+
const pillYOff = item.isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
18442
|
+
const pillH = LEGEND_HEIGHT - (item.isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
17940
18443
|
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);
|
|
17941
18444
|
if (item.isActive) {
|
|
17942
18445
|
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);
|
|
17943
18446
|
}
|
|
17944
|
-
gEl.append("text").attr("x", pillXOff + item.pillWidth / 2).attr("y",
|
|
18447
|
+
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);
|
|
17945
18448
|
if (item.isActive) {
|
|
17946
18449
|
let entryX = pillXOff + item.pillWidth + 4;
|
|
17947
18450
|
for (const entry of item.entries) {
|
|
17948
18451
|
const entryG = gEl.append("g").attr("data-legend-entry", entry.value.toLowerCase()).style("cursor", "pointer");
|
|
17949
|
-
entryG.append("circle").attr("cx", entryX +
|
|
17950
|
-
const textX = entryX +
|
|
17951
|
-
entryG.append("text").attr("x", textX).attr("y",
|
|
17952
|
-
entryX = textX + entry.value.length *
|
|
18452
|
+
entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
18453
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
18454
|
+
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);
|
|
18455
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
17953
18456
|
}
|
|
17954
18457
|
}
|
|
17955
|
-
legendX += item.totalWidth +
|
|
18458
|
+
legendX += item.totalWidth + LEGEND_GROUP_GAP;
|
|
17956
18459
|
}
|
|
17957
18460
|
}
|
|
17958
18461
|
for (const group of groups) {
|
|
@@ -18473,7 +18976,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
|
|
|
18473
18976
|
});
|
|
18474
18977
|
}
|
|
18475
18978
|
}
|
|
18476
|
-
var 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,
|
|
18979
|
+
var 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;
|
|
18477
18980
|
var init_renderer9 = __esm({
|
|
18478
18981
|
"src/sequence/renderer.ts"() {
|
|
18479
18982
|
"use strict";
|
|
@@ -18483,6 +18986,7 @@ var init_renderer9 = __esm({
|
|
|
18483
18986
|
init_colors();
|
|
18484
18987
|
init_parser();
|
|
18485
18988
|
init_tag_resolution();
|
|
18989
|
+
init_legend_constants();
|
|
18486
18990
|
PARTICIPANT_GAP = 160;
|
|
18487
18991
|
PARTICIPANT_BOX_WIDTH = 120;
|
|
18488
18992
|
PARTICIPANT_BOX_HEIGHT = 50;
|
|
@@ -18504,18 +19008,6 @@ var init_renderer9 = __esm({
|
|
|
18504
19008
|
NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
|
|
18505
19009
|
COLLAPSED_NOTE_H = 20;
|
|
18506
19010
|
COLLAPSED_NOTE_W = 40;
|
|
18507
|
-
LEGEND_HEIGHT9 = 28;
|
|
18508
|
-
LEGEND_PILL_PAD8 = 16;
|
|
18509
|
-
LEGEND_PILL_FONT_SIZE6 = 11;
|
|
18510
|
-
LEGEND_PILL_FONT_W8 = LEGEND_PILL_FONT_SIZE6 * 0.6;
|
|
18511
|
-
LEGEND_CAPSULE_PAD8 = 4;
|
|
18512
|
-
LEGEND_DOT_R9 = 4;
|
|
18513
|
-
LEGEND_ENTRY_FONT_SIZE7 = 10;
|
|
18514
|
-
LEGEND_ENTRY_FONT_W8 = LEGEND_ENTRY_FONT_SIZE7 * 0.6;
|
|
18515
|
-
LEGEND_ENTRY_DOT_GAP8 = 4;
|
|
18516
|
-
LEGEND_ENTRY_TRAIL8 = 8;
|
|
18517
|
-
LEGEND_GROUP_GAP6 = 12;
|
|
18518
|
-
LEGEND_BOTTOM_GAP = 8;
|
|
18519
19011
|
LABEL_CHAR_WIDTH = 7.5;
|
|
18520
19012
|
LABEL_MAX_CHARS = Math.floor((PARTICIPANT_BOX_WIDTH - 10) / LABEL_CHAR_WIDTH);
|
|
18521
19013
|
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);
|
|
@@ -20128,9 +20620,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20128
20620
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
20129
20621
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
20130
20622
|
const margin = {
|
|
20131
|
-
top: 104 + markerMargin
|
|
20623
|
+
top: 104 + markerMargin,
|
|
20132
20624
|
right: 40 + scaleMargin,
|
|
20133
|
-
bottom: 40,
|
|
20625
|
+
bottom: 40 + tagLegendReserve,
|
|
20134
20626
|
left: 60 + scaleMargin
|
|
20135
20627
|
};
|
|
20136
20628
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20237,9 +20729,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20237
20729
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
20238
20730
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
20239
20731
|
const margin = {
|
|
20240
|
-
top: 104 + markerMargin
|
|
20732
|
+
top: 104 + markerMargin,
|
|
20241
20733
|
right: 200,
|
|
20242
|
-
bottom: 40,
|
|
20734
|
+
bottom: 40 + tagLegendReserve,
|
|
20243
20735
|
left: 60 + scaleMargin
|
|
20244
20736
|
};
|
|
20245
20737
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20376,9 +20868,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20376
20868
|
const dynamicLeftMargin = Math.max(120, maxGroupNameLen * 7 + 30);
|
|
20377
20869
|
const baseTopMargin = title ? 50 : 20;
|
|
20378
20870
|
const margin = {
|
|
20379
|
-
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin
|
|
20871
|
+
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin,
|
|
20380
20872
|
right: 40,
|
|
20381
|
-
bottom: 40 + scaleMargin,
|
|
20873
|
+
bottom: 40 + scaleMargin + tagLegendReserve,
|
|
20382
20874
|
left: dynamicLeftMargin
|
|
20383
20875
|
};
|
|
20384
20876
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20522,9 +21014,9 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20522
21014
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
20523
21015
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
20524
21016
|
const margin = {
|
|
20525
|
-
top: 104 + (timelineScale ? 40 : 0) + markerMargin
|
|
21017
|
+
top: 104 + (timelineScale ? 40 : 0) + markerMargin,
|
|
20526
21018
|
right: 40,
|
|
20527
|
-
bottom: 40 + scaleMargin,
|
|
21019
|
+
bottom: 40 + scaleMargin + tagLegendReserve,
|
|
20528
21020
|
left: 60
|
|
20529
21021
|
};
|
|
20530
21022
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -20655,17 +21147,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20655
21147
|
});
|
|
20656
21148
|
}
|
|
20657
21149
|
if (parsed.timelineTagGroups.length > 0) {
|
|
20658
|
-
const LG_HEIGHT =
|
|
20659
|
-
const LG_PILL_PAD =
|
|
20660
|
-
const LG_PILL_FONT_SIZE =
|
|
20661
|
-
const LG_PILL_FONT_W =
|
|
20662
|
-
const LG_CAPSULE_PAD =
|
|
20663
|
-
const LG_DOT_R =
|
|
20664
|
-
const LG_ENTRY_FONT_SIZE =
|
|
20665
|
-
const LG_ENTRY_FONT_W =
|
|
20666
|
-
const LG_ENTRY_DOT_GAP =
|
|
20667
|
-
const LG_ENTRY_TRAIL =
|
|
20668
|
-
const LG_GROUP_GAP =
|
|
21150
|
+
const LG_HEIGHT = LEGEND_HEIGHT;
|
|
21151
|
+
const LG_PILL_PAD = LEGEND_PILL_PAD;
|
|
21152
|
+
const LG_PILL_FONT_SIZE = LEGEND_PILL_FONT_SIZE;
|
|
21153
|
+
const LG_PILL_FONT_W = LEGEND_PILL_FONT_W;
|
|
21154
|
+
const LG_CAPSULE_PAD = LEGEND_CAPSULE_PAD;
|
|
21155
|
+
const LG_DOT_R = LEGEND_DOT_R;
|
|
21156
|
+
const LG_ENTRY_FONT_SIZE = LEGEND_ENTRY_FONT_SIZE;
|
|
21157
|
+
const LG_ENTRY_FONT_W = LEGEND_ENTRY_FONT_W;
|
|
21158
|
+
const LG_ENTRY_DOT_GAP = LEGEND_ENTRY_DOT_GAP;
|
|
21159
|
+
const LG_ENTRY_TRAIL = LEGEND_ENTRY_TRAIL;
|
|
21160
|
+
const LG_GROUP_GAP = LEGEND_GROUP_GAP;
|
|
20669
21161
|
const LG_ICON_W = 20;
|
|
20670
21162
|
const mainSvg = d3Selection12.select(container).select("svg");
|
|
20671
21163
|
const mainG = mainSvg.select("g");
|
|
@@ -20698,6 +21190,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20698
21190
|
);
|
|
20699
21191
|
}, drawLegend2 = function() {
|
|
20700
21192
|
mainSvg.selectAll(".tl-tag-legend-group").remove();
|
|
21193
|
+
mainSvg.selectAll(".tl-tag-legend-container").remove();
|
|
20701
21194
|
const effectiveColorKey = (currentActiveGroup ?? currentSwimlaneGroup)?.toLowerCase() ?? null;
|
|
20702
21195
|
const visibleGroups = viewMode ? legendGroups.filter(
|
|
20703
21196
|
(lg) => effectiveColorKey != null && lg.group.name.toLowerCase() === effectiveColorKey
|
|
@@ -20708,13 +21201,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20708
21201
|
return s + (isActive ? lg.expandedWidth : lg.minifiedWidth);
|
|
20709
21202
|
}, 0) + (visibleGroups.length - 1) * LG_GROUP_GAP;
|
|
20710
21203
|
let cx = (width - totalW) / 2;
|
|
21204
|
+
const legendContainer = mainSvg.append("g").attr("class", "tl-tag-legend-container");
|
|
21205
|
+
if (currentActiveGroup) {
|
|
21206
|
+
legendContainer.attr("data-legend-active", currentActiveGroup.toLowerCase());
|
|
21207
|
+
}
|
|
20711
21208
|
for (const lg of visibleGroups) {
|
|
20712
21209
|
const groupKey = lg.group.name.toLowerCase();
|
|
20713
21210
|
const isActive = viewMode || currentActiveGroup != null && currentActiveGroup.toLowerCase() === groupKey;
|
|
20714
21211
|
const isSwimActive = currentSwimlaneGroup != null && currentSwimlaneGroup.toLowerCase() === groupKey;
|
|
20715
21212
|
const pillLabel = lg.group.name;
|
|
20716
21213
|
const pillWidth = pillLabel.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
20717
|
-
const gEl =
|
|
21214
|
+
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__");
|
|
20718
21215
|
if (!viewMode) {
|
|
20719
21216
|
gEl.style("cursor", "pointer").on("click", () => {
|
|
20720
21217
|
currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
|
|
@@ -20804,7 +21301,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
20804
21301
|
});
|
|
20805
21302
|
};
|
|
20806
21303
|
var drawSwimlaneIcon = drawSwimlaneIcon2, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
|
|
20807
|
-
const legendY =
|
|
21304
|
+
const legendY = height - LG_HEIGHT - 4;
|
|
20808
21305
|
const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
|
|
20809
21306
|
const legendGroups = parsed.timelineTagGroups.map((g) => {
|
|
20810
21307
|
const pillW = g.name.length * LG_PILL_FONT_W + LG_PILL_PAD;
|
|
@@ -21575,7 +22072,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21575
22072
|
const orgParsed = parseOrg2(content, effectivePalette2);
|
|
21576
22073
|
if (orgParsed.error) return "";
|
|
21577
22074
|
const collapsedNodes = orgExportState?.collapsedNodes;
|
|
21578
|
-
const activeTagGroup = orgExportState?.activeTagGroup ?? null;
|
|
22075
|
+
const activeTagGroup = orgExportState?.activeTagGroup ?? options?.tagGroup ?? null;
|
|
21579
22076
|
const hiddenAttributes = orgExportState?.hiddenAttributes;
|
|
21580
22077
|
const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
21581
22078
|
const orgLayout = layoutOrg2(
|
|
@@ -21604,7 +22101,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21604
22101
|
const sitemapParsed = parseSitemap2(content, effectivePalette2);
|
|
21605
22102
|
if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
|
|
21606
22103
|
const collapsedNodes = orgExportState?.collapsedNodes;
|
|
21607
|
-
const activeTagGroup = orgExportState?.activeTagGroup ?? null;
|
|
22104
|
+
const activeTagGroup = orgExportState?.activeTagGroup ?? options?.tagGroup ?? null;
|
|
21608
22105
|
const hiddenAttributes = orgExportState?.hiddenAttributes;
|
|
21609
22106
|
const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
21610
22107
|
const sitemapLayout = layoutSitemap2(
|
|
@@ -21632,7 +22129,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21632
22129
|
container2.style.position = "absolute";
|
|
21633
22130
|
container2.style.left = "-9999px";
|
|
21634
22131
|
document.body.appendChild(container2);
|
|
21635
|
-
renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark");
|
|
22132
|
+
renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark", void 0, void 0, options?.tagGroup);
|
|
21636
22133
|
return finalizeSvgExport(container2, theme, effectivePalette2, options);
|
|
21637
22134
|
}
|
|
21638
22135
|
if (detectedType === "class") {
|
|
@@ -21664,7 +22161,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21664
22161
|
const exportWidth = erLayout.width + PADDING * 2;
|
|
21665
22162
|
const exportHeight = erLayout.height + PADDING * 2 + titleOffset;
|
|
21666
22163
|
const container2 = createExportContainer(exportWidth, exportHeight);
|
|
21667
|
-
renderERDiagram2(container2, erParsed, erLayout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight });
|
|
22164
|
+
renderERDiagram2(container2, erParsed, erLayout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight }, options?.tagGroup);
|
|
21668
22165
|
return finalizeSvgExport(container2, theme, effectivePalette2, options);
|
|
21669
22166
|
}
|
|
21670
22167
|
if (detectedType === "initiative-status") {
|
|
@@ -21701,7 +22198,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21701
22198
|
const exportHeight = c4Layout.height + PADDING * 2 + titleOffset;
|
|
21702
22199
|
const container2 = createExportContainer(exportWidth, exportHeight);
|
|
21703
22200
|
const renderFn = c4Level === "deployment" || c4Level === "components" && c4System && c4Container || c4Level === "containers" && c4System ? renderC4Containers2 : renderC4Context2;
|
|
21704
|
-
renderFn(container2, c4Parsed, c4Layout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight });
|
|
22201
|
+
renderFn(container2, c4Parsed, c4Layout, effectivePalette2, theme === "dark", void 0, { width: exportWidth, height: exportHeight }, options?.tagGroup);
|
|
21705
22202
|
return finalizeSvgExport(container2, theme, effectivePalette2, options);
|
|
21706
22203
|
}
|
|
21707
22204
|
if (detectedType === "flowchart") {
|
|
@@ -21724,16 +22221,16 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21724
22221
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
21725
22222
|
const infraParsed = parseInfra2(content);
|
|
21726
22223
|
if (infraParsed.error || infraParsed.nodes.length === 0) return "";
|
|
21727
|
-
const
|
|
21728
|
-
const infraComputed = computeInfra2(infraParsed, selectedScenario ? { scenario: selectedScenario } : {});
|
|
22224
|
+
const infraComputed = computeInfra2(infraParsed);
|
|
21729
22225
|
const infraLayout = layoutInfra2(infraComputed);
|
|
22226
|
+
const activeTagGroup = options?.tagGroup ?? null;
|
|
21730
22227
|
const titleOffset = infraParsed.title ? 40 : 0;
|
|
21731
22228
|
const legendGroups = computeInfraLegendGroups2(infraLayout.nodes, infraParsed.tagGroups, effectivePalette2);
|
|
21732
22229
|
const legendOffset = legendGroups.length > 0 ? 28 : 0;
|
|
21733
22230
|
const exportWidth = infraLayout.width;
|
|
21734
22231
|
const exportHeight = infraLayout.height + titleOffset + legendOffset;
|
|
21735
22232
|
const container2 = createExportContainer(exportWidth, exportHeight);
|
|
21736
|
-
renderInfra2(container2, infraLayout, effectivePalette2, theme === "dark", infraParsed.title, infraParsed.titleLineNumber, infraParsed.tagGroups,
|
|
22233
|
+
renderInfra2(container2, infraLayout, effectivePalette2, theme === "dark", infraParsed.title, infraParsed.titleLineNumber, infraParsed.tagGroups, activeTagGroup, false, null, null, true);
|
|
21737
22234
|
const infraSvg = container2.querySelector("svg");
|
|
21738
22235
|
if (infraSvg) {
|
|
21739
22236
|
infraSvg.setAttribute("width", String(exportWidth));
|
|
@@ -21777,7 +22274,8 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21777
22274
|
const seqParsed = parseSequenceDgmo2(content);
|
|
21778
22275
|
if (seqParsed.error || seqParsed.participants.length === 0) return "";
|
|
21779
22276
|
renderSequenceDiagram2(container, seqParsed, effectivePalette, isDark, void 0, {
|
|
21780
|
-
exportWidth: EXPORT_WIDTH
|
|
22277
|
+
exportWidth: EXPORT_WIDTH,
|
|
22278
|
+
activeTagGroup: options?.tagGroup
|
|
21781
22279
|
});
|
|
21782
22280
|
} else if (parsed.type === "wordcloud") {
|
|
21783
22281
|
await renderWordCloudAsync(container, parsed, effectivePalette, isDark, dims);
|
|
@@ -21791,7 +22289,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
|
|
|
21791
22289
|
isDark,
|
|
21792
22290
|
void 0,
|
|
21793
22291
|
dims,
|
|
21794
|
-
orgExportState?.activeTagGroup,
|
|
22292
|
+
orgExportState?.activeTagGroup ?? options?.tagGroup,
|
|
21795
22293
|
orgExportState?.swimlaneTagGroup
|
|
21796
22294
|
);
|
|
21797
22295
|
} else if (parsed.type === "venn") {
|
|
@@ -21815,6 +22313,7 @@ var init_d3 = __esm({
|
|
|
21815
22313
|
init_diagnostics();
|
|
21816
22314
|
init_parsing();
|
|
21817
22315
|
init_tag_groups();
|
|
22316
|
+
init_legend_constants();
|
|
21818
22317
|
DEFAULT_CLOUD_OPTIONS = {
|
|
21819
22318
|
rotate: "none",
|
|
21820
22319
|
max: 0,
|
|
@@ -21990,7 +22489,7 @@ async function render(content, options) {
|
|
|
21990
22489
|
c4Level: options?.c4Level,
|
|
21991
22490
|
c4System: options?.c4System,
|
|
21992
22491
|
c4Container: options?.c4Container,
|
|
21993
|
-
|
|
22492
|
+
tagGroup: options?.tagGroup
|
|
21994
22493
|
});
|
|
21995
22494
|
}
|
|
21996
22495
|
|