diff --git a/docs/pr-media/1688/chat-no-health-bar.png b/docs/pr-media/1688/chat-no-health-bar.png
new file mode 100644
index 00000000..f79ee650
Binary files /dev/null and b/docs/pr-media/1688/chat-no-health-bar.png differ
diff --git a/docs/pr-media/1688/insights-system-health.png b/docs/pr-media/1688/insights-system-health.png
new file mode 100644
index 00000000..c9788835
Binary files /dev/null and b/docs/pr-media/1688/insights-system-health.png differ
diff --git a/static/index.html b/static/index.html
index 5a09626b..0903badd 100644
--- a/static/index.html
+++ b/static/index.html
@@ -79,27 +79,6 @@
${overviewCards.map(c => `
${c.icon}
${c.value}
${esc(c.label)}
`).join('')}
diff --git a/static/style.css b/static/style.css
index b1a66c3c..d5e8fe61 100644
--- a/static/style.css
+++ b/static/style.css
@@ -293,18 +293,20 @@
.layout{display:flex;width:100%;flex:1 1 auto;min-height:0;}
.app-titlebar{display:flex;align-items:center;justify-content:center;height:38px;flex-shrink:0;background:var(--sidebar);border-bottom:1px solid var(--border);padding:0 12px;padding-top:var(--app-titlebar-safe-top);padding-left:max(12px,env(safe-area-inset-left,0));padding-right:max(12px,env(safe-area-inset-right,0));box-sizing:content-box;font-size:12px;color:var(--muted);user-select:none;-webkit-app-region:drag;position:relative;z-index:20;}
.app-titlebar-inner{display:flex;align-items:center;gap:8px;min-width:0;max-width:100%;justify-content:center;}
- .system-health-panel{display:flex;align-items:center;gap:14px;min-height:38px;padding:6px 14px;padding-left:max(14px,env(safe-area-inset-left,0));padding-right:max(14px,env(safe-area-inset-right,0));border-bottom:1px solid var(--border);background:color-mix(in srgb,var(--sidebar) 92%,var(--bg));color:var(--muted);box-sizing:border-box;flex-shrink:0;}
+ .system-health-panel.insights-card{display:flex;flex-direction:column;gap:12px;color:var(--muted);}
.system-health-panel.unavailable{display:none;}
- .system-health-title{display:flex;align-items:center;gap:7px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;color:var(--muted);}
+ .system-health-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;}
+ .system-health-sub{font-size:11px;color:var(--muted);margin-top:-4px;}
.system-health-dot{width:7px;height:7px;border-radius:999px;background:var(--accent);box-shadow:0 0 0 3px var(--accent-bg);opacity:.88;}
.system-health-panel.loading .system-health-dot{background:var(--muted);box-shadow:none;opacity:.55;}
- .system-health-status{font-size:10px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--muted);opacity:.8;}
- .system-health-metrics{display:grid;grid-template-columns:repeat(3,minmax(90px,1fr));gap:10px;flex:1;min-width:0;}
- .system-health-metric{min-width:0;display:flex;flex-direction:column;gap:3px;}
+ .system-health-status{display:inline-flex;align-items:center;gap:7px;border-radius:999px;padding:3px 8px;font-size:11px;font-weight:700;border:1px solid var(--border);color:var(--muted);background:var(--surface);white-space:nowrap;}
+ .system-health-metrics{display:grid;grid-template-columns:repeat(3,minmax(120px,1fr));gap:10px;min-width:0;}
+ .system-health-metric{min-width:0;display:flex;flex-direction:column;gap:5px;padding:10px 11px;border:1px solid var(--border);border-radius:8px;background:var(--surface);}
.system-health-label{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:11px;line-height:1;color:var(--muted);}
.system-health-value{font-variant-numeric:tabular-nums;color:var(--text);font-weight:650;}
.system-health-bar{height:5px;overflow:hidden;border-radius:999px;background:color-mix(in srgb,var(--border) 70%,transparent);border:1px solid color-mix(in srgb,var(--border) 75%,transparent);}
.system-health-bar-fill{height:100%;width:0%;border-radius:inherit;background:linear-gradient(90deg,var(--accent),var(--accent-hover));transition:width .25s ease;}
+ .system-health-foot{font-size:11px;color:var(--muted);line-height:1.45;opacity:.82;}
.app-titlebar-icon{display:inline-flex;align-items:center;color:var(--accent);}
.app-titlebar-title{font-size:12px;font-weight:600;color:var(--text);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:60vw;}
.app-titlebar-sub{font-size:10px;color:var(--muted);background:var(--hover-bg);padding:2px 7px;border-radius:4px;font-family:'SF Mono',ui-monospace,monospace;white-space:nowrap;flex-shrink:0;}
@@ -1292,9 +1294,9 @@
.app-titlebar{justify-content:space-between;}
.app-titlebar-hamburger,.app-titlebar-spacer{display:flex;}
.app-titlebar-inner{flex:1 1 auto;}
- .system-health-panel{align-items:stretch;gap:6px;min-height:0;padding:6px 10px;flex-direction:column;}
- .system-health-title{font-size:10px;justify-content:space-between;}
- .system-health-metrics{grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;}
+ .system-health-panel.insights-card{gap:10px;padding:12px;}
+ .system-health-head{align-items:flex-start;}
+ .system-health-metrics{grid-template-columns:1fr;gap:8px;}
.system-health-label{font-size:10px;gap:4px;}
.system-health-bar{height:4px;}
/* Overlay backdrop */
diff --git a/static/ui.js b/static/ui.js
index dd7e3d45..6711bb30 100644
--- a/static/ui.js
+++ b/static/ui.js
@@ -3155,6 +3155,7 @@ function renderSystemHealth(payload){
}
async function pollSystemHealth(){
if(document.visibilityState !== 'visible') return;
+ if(!_systemHealthPanelIsVisible()) return;
try{
const payload=await api('/api/system/health');
renderSystemHealth(payload);
@@ -3162,8 +3163,13 @@ async function pollSystemHealth(){
setSystemHealthUnavailable('Unavailable');
}
}
+function _systemHealthPanelIsVisible(){
+ return document.visibilityState === 'visible' &&
+ !!document.querySelector('main.main.showing-insights') &&
+ !!$('systemHealthPanel');
+}
function startSystemHealthMonitor(){
- if(document.visibilityState !== 'visible') return;
+ if(!_systemHealthPanelIsVisible()) return;
if(_systemHealthTimer) return;
void pollSystemHealth();
_systemHealthTimer=setInterval(pollSystemHealth,SYSTEM_HEALTH_INTERVAL_MS);
@@ -3172,7 +3178,7 @@ function stopSystemHealthMonitor(){
if(_systemHealthTimer){clearInterval(_systemHealthTimer);_systemHealthTimer=null;}
}
function _syncSystemHealthMonitorVisibility(){
- if(document.visibilityState === 'visible') startSystemHealthMonitor();
+ if(_systemHealthPanelIsVisible()) startSystemHealthMonitor();
else stopSystemHealthMonitor();
}
document.addEventListener('visibilitychange',_syncSystemHealthMonitorVisibility);
diff --git a/tests/test_issue693_system_health_panel.py b/tests/test_issue693_system_health_panel.py
index d35e52bb..1161dc0a 100644
--- a/tests/test_issue693_system_health_panel.py
+++ b/tests/test_issue693_system_health_panel.py
@@ -10,6 +10,7 @@ from urllib.parse import urlparse
REPO_ROOT = pathlib.Path(__file__).parent.parent
UI_JS = (REPO_ROOT / "static" / "ui.js").read_text(encoding="utf-8")
+PANELS_JS = (REPO_ROOT / "static" / "panels.js").read_text(encoding="utf-8")
INDEX_HTML = (REPO_ROOT / "static" / "index.html").read_text(encoding="utf-8")
STYLE_CSS = (REPO_ROOT / "static" / "style.css").read_text(encoding="utf-8")
ROUTES_PY = (REPO_ROOT / "api" / "routes.py").read_text(encoding="utf-8")
@@ -137,27 +138,36 @@ def test_system_health_route_returns_only_sanitized_payload(monkeypatch):
assert set(payload) == {"status", "available", "checked_at", "cpu", "memory", "disk", "errors"}
-def test_system_health_panel_markup_and_styles_exist():
- assert 'id="systemHealthPanel"' in INDEX_HTML
- assert 'aria-label="Host resource health"' in INDEX_HTML
- assert 'data-system-health-metric="cpu"' in INDEX_HTML
- assert 'data-system-health-metric="memory"' in INDEX_HTML
- assert 'data-system-health-metric="disk"' in INDEX_HTML
- assert ".system-health-panel" in STYLE_CSS
+def test_system_health_panel_markup_and_styles_live_under_insights_not_top_chrome():
+ top_shell = INDEX_HTML[: INDEX_HTML.index('
')]
+ assert 'id="systemHealthPanel"' not in top_shell
+ assert 'aria-label="Host resource health"' not in top_shell
+ assert 'function _renderSystemHealthPanel()' in PANELS_JS
+ assert 'id="systemHealthPanel"' in PANELS_JS
+ assert 'aria-label="Host resource health"' in PANELS_JS
+ assert 'System health' in PANELS_JS
+ assert 'Current VPS resource usage' in PANELS_JS
+ assert PANELS_JS.index('_renderSystemHealthPanel()') < PANELS_JS.index('_renderLlmWikiStatus(wikiStatus)')
+ assert 'data-system-health-metric="cpu"' in PANELS_JS
+ assert 'data-system-health-metric="memory"' in PANELS_JS
+ assert 'data-system-health-metric="disk"' in PANELS_JS
+ assert ".system-health-panel.insights-card" in STYLE_CSS
assert ".system-health-bar-fill" in STYLE_CSS
assert ".system-health-panel.unavailable" in STYLE_CSS
- assert "@media(max-width:640px)" in STYLE_CSS and ".system-health-panel" in STYLE_CSS
+ assert "@media(max-width:640px)" in STYLE_CSS and ".system-health-panel.insights-card" in STYLE_CSS
def test_system_health_frontend_polls_visible_and_renders_progress_labels():
assert "const SYSTEM_HEALTH_INTERVAL_MS=5000" in UI_JS
assert "api('/api/system/health')" in UI_JS
assert "document.visibilityState !== 'visible'" in UI_JS
+ assert "document.querySelector('main.main.showing-insights')" in UI_JS
assert "document.addEventListener('visibilitychange',_syncSystemHealthMonitorVisibility)" in UI_JS
+ assert "typeof _syncSystemHealthMonitorVisibility === 'function'" in PANELS_JS
assert "function renderSystemHealth(payload)" in UI_JS
assert "setSystemHealthUnavailable" in UI_JS
- assert "data-system-health-metric" in INDEX_HTML
- assert "CPU" in INDEX_HTML and "RAM" in INDEX_HTML and "Disk" in INDEX_HTML
+ assert "data-system-health-metric" in PANELS_JS
+ assert "CPU" in PANELS_JS and "RAM" in PANELS_JS and "Disk" in PANELS_JS
assert "aria-valuenow" in UI_JS
assert "style.width=`${percent}%`" in UI_JS