mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-11-11 12:18:47 +00:00
One row per repeater whose configured region list is known, answering a question no other view answers: you declare these regions, but were you seen forwarding them? default_scope says what a node's adverts were observed under and transported_scopes (#1751) says what it carried, but nothing lined the declared list up against observed forwarding. The declared side merges every confirmed-scope source the database carries, newest answer per node wins, rather than naming one. On a stock install only nodes.configured_scope (#1865/#1971) exists and it degrades to the one-source case; deployments that collect the same fact another way keep working. Reading a single hard-coded source would have rendered an empty page on the very instance the evidence came from. Declared and observed are compared through normScope, so a leading "#" and a bare region name are one region. Unobserved regions render neutral, not red: absence over a short window is weak evidence, which the page header already states in words. Ported from a long-running fork deployment with its 17 server tests, rewired to the upstream data source, plus 14 frontend cases asserting rendered markup.
82 lines
4.5 KiB
CSS
82 lines
4.5 KiB
CSS
/* Network-wide scope audit page (#/scope-audit). Reuses .ns-decl / .ns-table /
|
|
.ns-empty / .ns-truncated from node-scopes.css (same badge vocabulary, so
|
|
the per-node and network-wide views agree visually) plus .analytics-time-range
|
|
for the window buttons. No hex/rgb literals — --text-muted / --border /
|
|
--status-* / --link-color are defined globally. */
|
|
|
|
.sa-page { max-width: 1200px; margin: 0 auto; padding: 12px 16px; }
|
|
|
|
.sa-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
|
|
.sa-head h2 { margin: 0; font-size: 18px; }
|
|
|
|
.sa-intro { color: var(--text-muted); font-size: 12px; margin: 4px 0 10px; }
|
|
.sa-intro a { color: var(--link-color); }
|
|
|
|
/* .sa-search reuses .nodes-search (style.css) for the input's colours/border
|
|
so this page doesn't invent a second search-box style — only the sizing
|
|
below is page-specific. */
|
|
.sa-search-bar { margin: 0 0 10px; }
|
|
.sa-search { width: 100%; max-width: 360px; }
|
|
|
|
.sa-window-note {
|
|
font-size: 12px; color: var(--text-muted); margin: 4px 0 10px;
|
|
border-left: 3px solid var(--border); padding: 4px 10px;
|
|
}
|
|
|
|
.sa-table-wrap { overflow-x: auto; }
|
|
.sa-table { min-width: 720px; }
|
|
.sa-name a { color: var(--link-color); text-decoration: none; }
|
|
.sa-name a:hover { text-decoration: underline; }
|
|
.sa-name-unknown { color: var(--text-muted); font-style: italic; }
|
|
.sa-role { font-size: 10px; text-transform: uppercase; letter-spacing: .3px; }
|
|
|
|
.sa-chip {
|
|
display: inline-block; padding: 1px 6px; margin: 1px 2px 1px 0; border-radius: 4px;
|
|
font-size: 11px; font-family: var(--mono); white-space: nowrap;
|
|
}
|
|
.sa-chip-declared { background: var(--section-bg, var(--card-bg)); color: var(--text); border: 1px solid var(--border); }
|
|
.sa-chip-undeclared { background: color-mix(in srgb, var(--status-yellow) 18%, transparent); color: var(--status-amber-text); }
|
|
.sa-chip-wildcard { background: var(--section-bg, var(--card-bg)); color: var(--text-muted); font-weight: 700; }
|
|
.sa-chip-ambiguous { background: var(--section-bg, var(--card-bg)); color: var(--text-muted); border: 1px dashed var(--border); font-family: inherit; font-style: italic; }
|
|
|
|
.sa-count { font-size: 11px; margin-top: 6px; }
|
|
|
|
/* sa-summary is the per-configState count line at the top of the page —
|
|
"13 no scopes configured" etc — reusing .ns-decl (node-scopes.css) for the
|
|
count pill so it shares the exact colour vocabulary the row-level Config
|
|
badge (also .ns-decl) uses, rather than a separate summary palette. */
|
|
.sa-summary { display: flex; flex-wrap: wrap; gap: 10px 16px; font-size: 12px; color: var(--text-muted); margin: 2px 0 10px; }
|
|
.sa-summary-item { display: inline-flex; align-items: center; gap: 5px; }
|
|
|
|
@media (max-width: 640px) {
|
|
.sa-table th:nth-child(6), .sa-table td:nth-child(6) { display: none; }
|
|
}
|
|
|
|
/* Observed forwarding, the green half of the merged Scopes column. Pairs with
|
|
.sa-chip-unobserved below, which stays neutral. Only the observed side is
|
|
coloured: red on every unobserved region would read as an alarm on rows that
|
|
are often just a quiet region over a short window. */
|
|
.sa-chip-observed { background: color-mix(in srgb, var(--status-green) 16%, transparent); color: var(--status-green-text); }
|
|
|
|
/* Declared but not observed in this window. Deliberately NOT red: absence over
|
|
a short window is weak evidence, which the page header says in words, so it
|
|
should not shout in colour. */
|
|
.sa-chip-unobserved { background: var(--section-bg, var(--card-bg)); color: var(--text-muted); border: 1px solid var(--border); }
|
|
|
|
/* Provenance note under the intro. Same muted treatment as .sa-intro; it is
|
|
context, not a finding, and must not compete with the table. */
|
|
.sa-sources { color: var(--text-muted); font-size: 12px; margin: 0 0 10px; line-height: 1.5; }
|
|
.sa-sources a { color: var(--link-color); }
|
|
.sa-sources code { font-family: var(--mono); font-size: 11px; }
|
|
|
|
/* Provenance note under the intro, and the empty state that has to carry the
|
|
same explanation on its own: on a stock install the table IS empty, so that
|
|
is where an operator actually reads it. Muted like .sa-intro; this is
|
|
context, not a finding, and must not compete with the table. */
|
|
.sa-sources { color: var(--text-muted); font-size: 12px; margin: 0 0 10px; line-height: 1.5; }
|
|
.sa-sources a, .sa-empty a { color: var(--link-color); }
|
|
.sa-sources code, .sa-empty code { font-family: var(--mono); font-size: 11px; }
|
|
.sa-empty { line-height: 1.6; max-width: 70ch; }
|
|
.sa-empty ul { margin: 8px 0; padding-left: 20px; }
|
|
.sa-empty li { margin: 4px 0; }
|