diff --git a/frontend/css/status_strip.css b/frontend/css/status_strip.css new file mode 100644 index 00000000..aa3dc0c4 --- /dev/null +++ b/frontend/css/status_strip.css @@ -0,0 +1,105 @@ +/* Operator status footer — console-style telemetry in panel chrome. */ + +.status-strip { + display: flex; + align-items: baseline; + flex-wrap: wrap; + gap: 0.35rem; + margin-top: 1.25rem; + padding: 0.5rem 0.75rem; + border-radius: var(--radius-sm); + border: 1px solid rgba(255, 255, 255, 0.04); + background: rgba(6, 182, 212, 0.03); + font-family: var(--font-mono); + font-size: 0.68rem; + letter-spacing: 0.04em; + color: var(--text-muted); +} + +.status-strip__label { + color: var(--text-secondary); + font-weight: 600; + letter-spacing: 0.1em; + font-size: 0.62rem; +} + +.status-strip__sep { + opacity: 0.35; +} + +.status-strip__items { + color: var(--accent-cyan); + text-shadow: 0 0 6px rgba(6, 182, 212, 0.12); +} + +.status-strip__updated { + color: var(--text-muted); + font-style: italic; +} + +.status-strip--quiet .status-strip__items { + color: var(--text-muted); + text-shadow: none; + font-style: italic; +} + +.map-topology-hint { + position: absolute; + left: 50%; + bottom: 2.75rem; + transform: translateX(-50%); + z-index: 800; + max-width: min(92%, 28rem); + padding: 0.45rem 0.75rem; + border-radius: var(--radius-sm); + border: 1px solid rgba(245, 158, 11, 0.35); + background: rgba(15, 23, 42, 0.92); + color: var(--accent-amber); + font-family: var(--font-mono); + font-size: 0.68rem; + letter-spacing: 0.03em; + text-align: center; + pointer-events: none; + box-shadow: var(--glow-amber); +} + +.mqtt-runtime { + margin: 0 0 1rem; + padding: 0.55rem 0.75rem; + border-radius: var(--radius-sm); + border: 1px solid rgba(255, 255, 255, 0.05); + background: rgba(0, 229, 160, 0.04); + font-family: var(--font-mono); + font-size: 0.68rem; + letter-spacing: 0.03em; + color: var(--text-muted); +} + +.mqtt-runtime--offline { + background: rgba(239, 68, 68, 0.06); + border-color: rgba(239, 68, 68, 0.25); +} + +.mqtt-runtime--disabled { + background: rgba(100, 116, 139, 0.08); +} + +.mqtt-runtime__label { + color: var(--text-secondary); + font-weight: 600; + letter-spacing: 0.1em; + font-size: 0.62rem; + margin-right: 0.35rem; +} + +.mqtt-runtime__state { + color: var(--accent-green); +} + +.mqtt-runtime--offline .mqtt-runtime__state { + color: var(--accent-red); +} + +.mqtt-runtime--disabled .mqtt-runtime__state { + color: var(--text-muted); +} diff --git a/frontend/css/topology.css b/frontend/css/topology.css new file mode 100644 index 00000000..273c1998 --- /dev/null +++ b/frontend/css/topology.css @@ -0,0 +1,129 @@ +.section[data-section="topology"] { + overflow-y: auto; +} + +.topology-panel { + padding: 1.25rem; + max-width: 1100px; + margin: 0 auto; +} + +.topology-panel__header { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + justify-content: space-between; + gap: 1rem; + margin-bottom: 0.75rem; +} + +.topology-panel__title { + margin: 0; + font-size: 1.1rem; + color: var(--text-primary, #f1f5f9); +} + +.topology-panel__desc { + margin: 0.25rem 0 0; + font-size: 0.78rem; + color: var(--text-secondary, #94a3b8); + max-width: 36rem; +} + +.topology-panel__controls { + display: inline-flex; + gap: 0.35rem; +} + +.topology-btn { + background: rgba(15, 23, 42, 0.8); + border: 1px solid rgba(51, 65, 85, 0.8); + color: var(--text-secondary, #94a3b8); + font-size: 0.72rem; + font-family: var(--font-mono); + padding: 0.25rem 0.55rem; + border-radius: 6px; + cursor: pointer; +} + +.topology-btn--active, +.topology-btn:hover { + color: var(--accent-cyan, #06b6d4); + border-color: rgba(6, 182, 212, 0.45); +} + +.topology-panel__meta { + display: flex; + gap: 1rem; + font-family: var(--font-mono); + font-size: 0.68rem; + color: var(--text-muted); + margin-bottom: 0.5rem; +} + +.topology-panel__canvas-wrap { + position: relative; + background: var(--card-bg, #111827); + border: 1px solid var(--border, #1e293b); + border-radius: 12px; + min-height: 420px; + overflow: hidden; +} + +.topology-svg { + display: block; + width: 100%; + min-height: 420px; +} + +.topology-empty { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-secondary, #94a3b8); + font-size: 0.85rem; + pointer-events: none; +} + +.topology-legend { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 0.75rem; + font-size: 0.68rem; + color: var(--text-muted); +} + +.topology-swatch { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 0.3rem; + vertical-align: middle; +} + +.topology-swatch--mt { background: var(--accent-cyan); } +.topology-swatch--mc { background: var(--accent-purple); } +.topology-swatch--ni { background: var(--accent-cyan); } +.topology-swatch--tr { + background: transparent; + border: 1px dashed var(--accent-amber); + border-radius: 2px; + width: 14px; + height: 0; +} +.topology-swatch--weak { + background: transparent; + border: 1px dashed var(--text-muted); + border-radius: 2px; + width: 14px; + height: 0; +} + +@media (max-width: 768px) { + .topology-panel { padding: 0.85rem; } + .topology-panel__header { flex-direction: column; } +} diff --git a/frontend/index.html b/frontend/index.html index 239398cc..0d3092c8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -21,6 +21,7 @@ + @@ -33,6 +34,7 @@ + @@ -102,6 +104,21 @@ Stats +