Model graph: decision and registry links on by default, placed near their elements

This commit is contained in:
root-at-skic committed 2026-10-21 22:33:02 +03:00
1 parent 27d1b4381c
commit bc9f55e2fa
4 files changed
+17 -8

No files matched your search

+3 -2
View File
@@ -131,6 +131,7 @@ class GraphPage {
this.saved = (host.getState().graphView as SavedView | undefined) ?? {};
if (initial.query !== undefined) this.saved.query = initial.query;
if (this.saved.labels === undefined) this.saved.labels = initial.graph.edges.length <= LABELS_UP_TO;
if (this.saved.links === undefined) this.saved.links = true;
this.hiddenTypes = new Set(this.saved.hidden ?? []);
this.applyTheme(this.saved.theme ?? archifyThemePreference());
this.index();
@@ -261,7 +262,7 @@ class GraphPage {
this.syncLayoutButton();
});
this.linksButton.type = 'button';
this.linksButton.title = 'Show the links from decisions and registries to their elements; off, they show for the selected element only';
this.linksButton.title = 'Links from decisions and registries to their elements: on for all, off for the selected element only';
this.linksButton.addEventListener('click', () => {
this.saved.links = !this.saved.links;
this.save();
@@ -300,7 +301,7 @@ class GraphPage {
table.appendChild(row);
}
this.help.appendChild(table);
this.help.appendChild(el('p', '', 'Matches stay bright, the rest dims. Connections show when both ends match. Click an element for its details; drag to move it; scroll to zoom. Press / to type a query. Layered puts clients on the left and data on the right, with registries before and decisions after; Links shows every decision and registry link instead of the selected element\u2019s only.'));
this.help.appendChild(el('p', '', 'Matches stay bright, the rest dims. Connections show when both ends match. Click an element for its details; drag to move it; scroll to zoom. Press / to type a query. Layered puts clients on the left and data on the right, with registries before and decisions after; Links switches the dotted decision and registry links between all of them and the selected element\u2019s only.'));
this.main.append(this.canvas, this.side, this.help);
this.root.append(top, bar, this.main);
document.body.appendChild(this.root);
+12 -4
View File
@@ -70,11 +70,19 @@ export function layeredLayout(nodes: GraphNode[], edges: GraphEdge[], options: L
list.forEach((n, i) => positions.set(n.id, { x: index * options.columnGap, y: offset + i * options.rowGap }));
});
const width = Math.max(0, used.length - 1) * options.columnGap;
// Registries and decisions sit above or below the elements they link to: at the mean x of those
// elements, pushed apart only as far as needed so they do not overlap.
const spread = (list: GraphNode[], y: number, gap: number) => {
const total = Math.max(0, list.length - 1) * gap;
list.forEach((n, i) => positions.set(n.id, { x: width / 2 - total / 2 + i * gap, y }));
const wanted = list.map((n) => {
const xs = (neighbours.get(n.id) ?? []).map((id) => positions.get(id)?.x).filter((x): x is number => x !== undefined);
return { n, x: xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : width / 2 };
});
wanted.sort((a, b) => a.x - b.x || a.n.label.localeCompare(b.n.label));
for (let i = 1; i < wanted.length; i += 1) wanted[i].x = Math.max(wanted[i].x, wanted[i - 1].x + gap);
for (let i = wanted.length - 2; i >= 0; i -= 1) wanted[i].x = Math.min(wanted[i].x, wanted[i + 1].x - gap);
for (const { n, x } of wanted) positions.set(n.id, { x, y });
};
spread(registries, -options.rowGap * 1.6, Math.max(options.columnGap, 200));
spread(decisions, tallest * options.rowGap + options.rowGap * 0.6, Math.max(options.columnGap, 240));
spread(registries, -options.rowGap * 1.6, 200);
spread(decisions, tallest * options.rowGap + options.rowGap * 0.6, 250);
return positions;
}