Skip to content

web: sync idle_decay_minutes from /api/settings instead of hard-coded mirror #874

@njbrake

Description

@njbrake

Note: this issue was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.

Background

#863 / #872 added idle_decay_minutes to Config.theme (configurable via the TUI's Settings -> Theme view, or directly in config.toml). The TUI honors the user's setting at runtime via HomeView.idle_decay_window.

The dashboard does not. web/src/lib/session.ts declares:

export const IDLE_DECAY_WINDOW_MS = 20 * 60 * 1000;

The Rust Config.theme.idle_decay_minutes field IS already serialized and reachable at /api/settings (the existing handler returns the full Config blob), but the web side never reads it. So a user who changes the decay window in the TUI sees the new value reflected in the TUI but not in the dashboard.

Proposal

Wire the web dashboard to fetch the configured value once on app boot and use it in place of the hard-coded constant.

Sketch:

  1. Extend web/src/lib/api.ts with a typed fetchSettings() (or extract just the relevant slice) that returns { theme: { idle_decay_minutes: number } } plus whatever else is useful.
  2. Store the resolved window in a small React context (or a Zustand-style atom if the project leans that way) created at the top of App.tsx. Default to 20 min while the fetch is in flight so the UI doesn't flicker.
  3. Change isFreshIdle / idleAgeMs / getStatusDotClass / getStatusTextClass / isSessionActive from pure functions over IDLE_DECAY_WINDOW_MS to either accept a window parameter or read from the context. Probably the explicit param keeps testability cleanest.
  4. Update the vitest cases in web/src/lib/session.test.ts to pass the window through (or use a default in test fixtures).

Out of scope

  • No new keybind / settings UI on the web side; reads through the existing TUI-driven config.
  • No live-refresh: a settings change in the TUI mid-session won't reflect in the dashboard until the user reloads the page. That's acceptable; the gradient is a soft signal, not a critical one.

Acceptance

  • A dashboard session that just stopped reflects the user's configured decay window (e.g. 5 min if the user set idle_decay_minutes = 5), not the hard-coded 20.
  • Setting idle_decay_minutes = 0 in config.toml makes the dashboard skip the freshness signal entirely (matching TUI behavior).
  • vitest still passes; no new Playwright cases required.

Why split from #872

The PR scope was already broad (TUI + web colors + settings wiring + a status-poller bug fix). Pulling the web/server config sync into its own PR keeps the diff reviewable and gives the existing PR a clean ship.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions