Plot live drive activity every three minutes (#91)

- Change default collection cadence from 5 minutes to 3 minutes
  (CADENCE_DEFAULT_S=180, systemd OnUnitInactiveSec=3min,
  runit CADENCE=180)
- Update freshness threshold to 450s (2×180 + AccuracySec + 60)
- Add _query_live_graph_data(): queries raw samples from the last
  3 hours and computes interval byte deltas with actual timestamps
- Add LiveActivityGraph widget: vertical bar chart of interval
  volumes with read/write toggle (w key), arrow key inspection,
  and click support
- Wire live graph into TUI layout (full-width row between daily
  graph and drive health), refresh cycle, and CSS grid
- Replace t theme binding with t today/live binding; theme
  selection via preferences file
- Add w binding for read/write toggle on live graph
- Update action legend, help screen, and grid layout for new
  live-activity row
- Add 20 tests covering cadence constants, live query, widget
  rendering, toggle, and TUI integration
- Update all cadence documentation (README, ADR 0003, acceptance
  criteria LC-2, fenris-redesign constants table, CHANGELOG)
This commit is contained in:
xavierk
2026-09-18 13:17:18 +05:30
parent 4f884b4b73
commit e12f4a574c
14 changed files with 735 additions and 70 deletions
+18 -11
View File
@@ -171,8 +171,8 @@ class TestKeyBindings:
assert "m" in binding_keys
@pytest.mark.asyncio
async def test_t_cycles_through_themes(self, tmp_path):
"""Pressing t cycles through the three presets."""
async def test_t_goes_to_today(self, tmp_path):
"""Pressing t returns to today's live view (issue #91)."""
from fenris.tui import FenrisTuiApp
config_home = _make_prefs_dir(tmp_path)
@@ -182,12 +182,17 @@ class TestKeyBindings:
# Start at amber
assert app.theme == "fenris-amber"
# Press t to cycle
# Enter drill mode on the graph
graph = app.query_one("#usage-history")
graph.view_mode = "hourly"
graph.drill_day = "2026-09-30"
# Press t to go back to today
await pilot.press("t")
await pilot.pause()
# Should be nord or high_contrast now
assert app.theme in ("fenris-nord", "fenris-high-contrast")
# Should be back in daily mode
assert graph.view_mode == "daily"
@pytest.mark.asyncio
async def test_m_toggles_reduced_motion(self, tmp_path):
@@ -226,21 +231,23 @@ class TestPersistence:
async def test_theme_survives_restart(self, tmp_path):
"""Theme preference persists across TUI restart."""
from fenris.tui import FenrisTuiApp
from fenris.preferences import save_preferences
config_home = _make_prefs_dir(tmp_path)
with patch.dict(os.environ, {"XDG_CONFIG_HOME": str(config_home)}):
# First run: change theme
# First run: change theme via preferences API
app1 = FenrisTuiApp(store_path=tmp_path / "nonexistent.db")
async with app1.run_test() as pilot:
await pilot.press("t")
await pilot.pause()
theme_after_t = app1.theme
assert theme_after_t != "fenris-amber"
save_preferences(theme="nord", reduced_motion=False)
app1._current_theme_name = "nord"
app1.theme = "fenris-nord"
theme_after = app1.theme
assert theme_after == "fenris-nord"
# Second run: theme should persist
app2 = FenrisTuiApp(store_path=tmp_path / "nonexistent.db")
async with app2.run_test() as pilot:
assert app2.theme == theme_after_t
assert app2.theme == theme_after
@pytest.mark.asyncio
async def test_reduced_motion_survives_restart(self, tmp_path):