diff --git a/docs/graph-explorer-contract.md b/docs/graph-explorer-contract.md index 23a82f0..cc48247 100644 --- a/docs/graph-explorer-contract.md +++ b/docs/graph-explorer-contract.md @@ -154,6 +154,23 @@ The graph explorer should support zone-oriented modes for Fabric payloads: Zone modes are diagnostic views. They answer "where does this run or who can reach it here?" without mutating the underlying Fabric responsibility boundary. +Zone boundary overlays are a visual layer over the graph canvas. They should be +computed from visible node positions after layout/filtering rather than modeled +as graph parent nodes. The default boundary grouping is `deploymentEnvironment`: + +| Overlay label | Matching nodes | +|---------------|----------------| +| `dev-tegwick` | visible nodes with `deploymentEnvironment: dev` in the private local development overlay | +| `test` | visible nodes with `deploymentEnvironment: test` or legacy `staging` | +| `prod` | visible nodes with `deploymentEnvironment: prod` | + +Nodes without deployment overlay data are not enclosed. Edge-only deployment +overlay evidence may contribute to zone summaries and warnings, but it should +not create a boundary unless at least one visible node belongs to the same zone. +When access-zone grouping is selected, boundaries use `accessZone` values such +as `private-dev`, `collaborator-test`, `production-public`, or +`production-admin`. + Useful warnings for the graph explorer include: - control surfaces in user-facing access zones; diff --git a/railiance_fabric/graph_explorer_ui.py b/railiance_fabric/graph_explorer_ui.py index 5cef4c7..7f5db02 100644 --- a/railiance_fabric/graph_explorer_ui.py +++ b/railiance_fabric/graph_explorer_ui.py @@ -36,7 +36,50 @@ def graph_explorer_page() -> str: } .main { display: grid; grid-template-columns: minmax(0, 1fr) 340px; min-height: 0; } .canvas-wrap { position: relative; min-width: 0; min-height: 0; } - #graph-canvas { position: absolute; inset: 0; } + #graph-canvas { position: absolute; inset: 0; z-index: 0; } + .zone-overlay { + position: absolute; + inset: 0; + z-index: 2; + overflow: hidden; + pointer-events: none; + } + .zone-boundary { + position: absolute; + border: 2px solid var(--zone-color, #2563eb); + border-radius: 8px; + background: var(--zone-fill, rgba(37, 99, 235, .06)); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .74), 0 10px 32px rgba(23, 32, 51, .08); + min-height: 56px; + min-width: 90px; + pointer-events: none; + } + .zone-boundary.selected { + border-width: 3px; + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .82), 0 14px 36px rgba(23, 32, 51, .14); + } + .zone-label { + position: absolute; + top: 8px; + left: 10px; + z-index: 1; + min-height: 26px; + max-width: calc(100% - 20px); + border-color: var(--zone-color, #2563eb); + color: var(--zone-color, #2563eb); + background: rgba(255, 255, 255, .96); + box-shadow: 0 8px 18px rgba(23, 32, 51, .1); + font-size: 12px; + font-weight: 700; + overflow: hidden; + padding: 3px 8px; + pointer-events: auto; + text-overflow: ellipsis; + } + .zone-label:focus-visible { + outline: 2px solid var(--zone-color, #2563eb); + outline-offset: 2px; + } .side { border-left: 1px solid var(--line); background: var(--panel); @@ -181,6 +224,14 @@ def graph_explorer_page() -> str: min-width: 84px; padding: 4px 6px; } + .map-control-field input[type="checkbox"] { + width: 18px; + height: 18px; + min-height: 0; + margin: 5px 0; + padding: 0; + accent-color: var(--accent); + } .selection-anchor { position: absolute; z-index: 5; @@ -390,7 +441,17 @@ def graph_explorer_page() -> str:
+
+ +