Complete local coordination view switching and record remaining blockers
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e3b8-9815-70f2-9ac5-37bcde2c5090
This commit is contained in:
codex 2026-09-27 18:44:21 +02:00
parent 6923d4bfd0
commit b47dd43b04
7 changed files with 227 additions and 55 deletions

View file

@ -568,8 +568,8 @@ def graph_explorer_page() -> str:
(() => {
const manifestUrl = "/exports/graph-explorer/manifest";
const pageParams = new URLSearchParams(window.location.search);
const graphMode = pageParams.get("mode");
const includeResidualsParam = ["1", "true", "yes"].includes(
let graphMode = pageParams.get("mode") === "coordination" ? "coordination" : "full";
let includeResidualsParam = ["1", "true", "yes"].includes(
(pageParams.get("include_residuals") || "").toLowerCase()
);
const graphUrl = graphMode === "coordination"
@ -2141,6 +2141,7 @@ def graph_explorer_page() -> str:
const state = currentViewState();
if (state.search) params.set("search", state.search);
if (state.mode && state.mode !== "full") params.set("mode", state.mode);
if (state.mode === "coordination" && includeResidualsParam) params.set("include_residuals", "true");
if (state.layout && state.layout !== "cose") params.set("layout", state.layout);
if (state.labelMode && state.labelMode !== "auto") params.set("labelMode", state.labelMode);
if (state.nodeTypes.length !== allNodeTypes.length) params.set("nodeTypes", state.nodeTypes.join(","));
@ -2201,6 +2202,10 @@ def graph_explorer_page() -> str:
};
const applyViewState = (state, options = {}) => {
if (cy && "mode" in state && ((state.mode === "coordination") !== (graphMode === "coordination"))) {
switchGraph(state.mode, includeResidualsParam, state);
return;
}
if ("search" in state) searchInput.value = state.search || "";
if ("mode" in state) modeSelect.value = optionExists(modeSelect, state.mode) ? state.mode : "full";
if ("layout" in state) layoutSelect.value = optionExists(layoutSelect, state.layout) ? state.layout : "cose";
@ -2828,11 +2833,7 @@ def graph_explorer_page() -> str:
residualsWrap.style.display = coordinationActive ? "" : "none";
residualsToggle.checked = includeResidualsParam;
residualsToggle.addEventListener("change", () => {
const url = new URL(window.location.href);
url.searchParams.set("mode", "coordination");
if (residualsToggle.checked) url.searchParams.set("include_residuals", "true");
else url.searchParams.delete("include_residuals");
window.location.search = url.search;
switchGraph("coordination", residualsToggle.checked);
});
}
profiles = loadProfiles();
@ -2841,12 +2842,6 @@ def graph_explorer_page() -> str:
...element,
data: {...element.data, color: layerColors[element.data.layer] || "#667085"}
}));
if (elements.length === 0) {
canvas.innerHTML = "<p class='canvas-notice'>No graph entities were returned by the registry.</p>";
counts.textContent = "0 nodes / 0 edges";
hiddenSummary.textContent = "Hidden 0";
return;
}
canvas.innerHTML = "";
cy = cytoscape({
container: canvas,
@ -2975,6 +2970,69 @@ def graph_explorer_page() -> str:
applyViewState(urlState, {skipUrl: true});
runLayout();
updateUrlState();
if (!elements.length) profileSummary.textContent = "No graph entities were returned by the registry.";
};
let graphRequest = 0;
const switchGraph = async (mode, includeResiduals = includeResidualsParam, viewState = null) => {
const request = ++graphRequest;
const coordination = mode === "coordination";
const url = coordination
? `/exports/graph-explorer?mode=coordination${includeResiduals ? "&include_residuals=true" : ""}`
: "/exports/graph-explorer";
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const payload = await response.json();
if (!Array.isArray(payload.elements)) throw new Error("Invalid graph response");
if (request !== graphRequest) return;
const elements = payload.elements.map((element) => ({
...element,
data: {...element.data, color: layerColors[element.data.layer] || "#667085"}
}));
graphMode = coordination ? "coordination" : "full";
includeResidualsParam = includeResiduals;
activeMode = mode;
modeSelect.value = mode;
document.getElementById("include-residuals-wrap").style.display = coordination ? "" : "none";
document.getElementById("include-residuals").checked = includeResiduals;
selected = null;
selectedAnchor = null;
selectedZoneId = "";
focusSet = null;
manualOverrides = {};
filterRules = [];
editingRuleId = "";
collapsedZoneSnapshots.clear();
zoneContainerState.clear();
zoneSummaries.clear();
currentProfileId = "";
profileSelect.value = "";
allEdgeTypes = [...new Set(elements.map(({data}) => data.edgeType).filter(Boolean))].sort();
renderChecklist(nodeTypeFilter, allNodeTypes, nodeTypeLabels, "node-type");
renderChecklist(edgeTypeFilter, allEdgeTypes, {}, "edge-type");
cy.elements().remove();
cy.add(elements);
popup.style.display = "none";
refreshRuleBuilder();
renderRules();
syncFilterSummaries();
if (viewState) applyViewState(viewState);
else {
applyFilters();
runLayout();
updateUrlState();
}
showDetails(null);
updateProfileControls();
updateProfileSummary();
if (!elements.length) profileSummary.textContent = "No graph entities were returned by the registry.";
} catch (error) {
if (request !== graphRequest) return;
modeSelect.value = activeMode;
document.getElementById("include-residuals").checked = includeResidualsParam;
profileSummary.textContent = `Could not load graph: ${error.message}`;
}
};
const handleFilterChange = () => {
@ -2992,27 +3050,14 @@ def graph_explorer_page() -> str:
nodeTypeFilter.addEventListener("change", handleFilterChange);
edgeTypeFilter.addEventListener("change", handleFilterChange);
modeSelect.addEventListener("input", () => {
if (modeSelect.value === "coordination" && graphMode !== "coordination") {
const url = new URL(window.location.href);
url.searchParams.set("mode", "coordination");
window.location.search = url.search;
const mode = modeSelect.value || "full";
if ((mode === "coordination") !== (graphMode === "coordination")) {
switchGraph(mode);
return;
}
if (graphMode === "coordination" && modeSelect.value !== "coordination") {
const url = new URL(window.location.href);
url.searchParams.delete("mode");
url.searchParams.delete("include_residuals");
window.location.search = url.search;
return;
}
activeMode = modeSelect.value || "full";
focusSet = null;
currentProfileId = "";
profileSelect.value = "";
applyFilters();
updateProfileControls();
updateProfileSummary();
updateUrlState();
graphRequest += 1;
activeMode = mode;
handleFilterChange();
});
layoutSelect.addEventListener("input", () => {
currentProfileId = "";
@ -3179,8 +3224,8 @@ def graph_explorer_page() -> str:
document.querySelector("[data-action='focus']").addEventListener("click", applyFocus);
document.querySelector("[data-action='clear']").addEventListener("click", () => {
searchInput.value = "";
modeSelect.value = "full";
activeMode = "full";
modeSelect.value = graphMode;
activeMode = graphMode;
labelSelect.value = "auto";
activeLabelMode = "auto";
zoneBoundaryToggle.checked = true;