refactor: extract the Jobs view state, track selection by job ID

Phase 10 of the whole-project review (findings 5.1 and 5.2), folded into
the ROADMAP file-split item as that plan asks.

5.2 was a real defect. `selected` was an index into a snapshot of the jobs
slice, and every path that changed the slice patched it by hand. The one
path that could not — adopting a different jobs file, where the Service
replaces the whole list and the view only hears about it through the
refresh JobsLoaded triggers — left the details pane redrawing from an
index that belonged to the previous list, describing whichever job now sat
there (or clearing when the new list was shorter) while the list highlight
stayed put. The selection is now a job ID; rows are derived from it at
render time, and refresh ends by pointing the highlight at the selected
job, so the two can no longer disagree.

5.1: newJobsView was one 330-line constructor whose dozen closures shared
seven mutable locals. It is now a jobsView struct over a jobsViewState
that owns the snapshot, the folder filter, and the selection — the
invariant that used to be maintained by hand in five places lives in one
place — split across jobs_view.go (construction, refresh, layout),
jobs_view_state.go, jobs_view_list.go, and jobs_view_toolbar.go. The
folder-option rebuild that appeared verbatim in three handlers is one
method.

Behaviour that changed beyond the fix: switching the folder filter keeps
the current selection when the new filter still shows it, instead of
always jumping to the folder's first job.

Docs: ARCHITECTURE records the new file layout and the selection-by-ID
contract; ROADMAP drops jobs_view.go from the over-guideline table and
refreshes the other five numbers (finding 2.4); TESTS documents the new
state test file and the adoption regression test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 22:38:47 +03:00
parent ca2a8c8aa7
commit bd7ebde68e
10 changed files with 913 additions and 355 deletions
+12
View File
@@ -51,6 +51,12 @@ the app icon (experimental).**
gets slower the longer the app has been running. Measured on 5000 accumulated
records, one History redraw went from **15.8 ms to 0.9 ms**; at the new cap
the width rescan alone accounted for 1.5 ms of every redraw.
- **The Jobs tab keeps its selection on the job, not on the row.** Selecting a
different jobs file in Settings replaces the whole job list; the details pane
then described whichever job happened to land on the previously selected row —
or went blank if the new list was shorter — while the highlight in the list
stayed where it was. The selection now follows the job itself, and the
highlight and the details pane always describe the same one.
- **Max log files and max log age days now accept 0, meaning "keep
everything."** Log cleanup already supported disabling either policy; the
Settings form and the Service validator rejected the value that would have
@@ -81,6 +87,12 @@ the app icon (experimental).**
released, in preparation order, so `jobs.json` still ends up matching the
in-memory list. Seeding statistics from logs also opens each log file once
instead of twice.
- The Jobs tab was split into `jobs_view.go` (construction, refresh, layout),
`jobs_view_state.go` (the job/runtime snapshot, folder filter, and selection),
`jobs_view_list.go`, and `jobs_view_toolbar.go`. What used to be one 330-line
constructor whose dozen closures shared seven mutable locals is now widgets
reading one named state object — which is what made the selection fix above a
change in one place instead of five.
## 1.0.1 - 2026-08-04