feat(web): row-count selector on the Executions card

Adds a "Show" selector (10 / 25 / 50 / 100) to the card header and drops the
default row count from 15 to 10. The chosen value rides on the card's own
polling URL (`/executions?limit=N`), so it survives the 5s htmx refresh with no
client-side state; a config default outside those four is rendered as an extra
option instead of disappearing. The query is parsed permissively — a stale or
hand-edited polling URL clamps or falls back to the config default rather than
422-ing a card that would otherwise render.

Ordering is now explicit rather than inherited from whatever Gitea returns:
newest first, a not-yet-started (queued) run above everything, ties broken on
Gitea's run id so matrix runs sharing a timestamp keep a stable order.
This commit is contained in:
2026-07-25 15:36:21 +02:00
parent 30fd3120a6
commit 7d1f4e2784
9 changed files with 280 additions and 22 deletions
+17 -1
View File
@@ -350,6 +350,17 @@ def _cached_runs(
return runs, source, error, auth_failed, refused
def _recency_key(run: Execution) -> tuple[datetime, int]:
"""Sort key for "newest first" (used with ``sorted(..., reverse=True)``).
A run that has not started yet (queued) is the newest thing there is, so it
sorts above every started run. ``run_id`` breaks ties — Gitea hands ids out
in creation order — which keeps the order stable when several runs share a
timestamp, as matrix jobs routinely do.
"""
return (run.started or datetime.max.replace(tzinfo=UTC), run.run_id)
def _attach_vms(
runs: list[Execution], vms: list[LiveVm]
) -> tuple[list[Execution], list[LiveVm]]:
@@ -437,7 +448,12 @@ def list_executions(
)
runs, unmatched = _attach_vms(
[_to_execution(item, moment) for item in raw_runs], vms
sorted(
(_to_execution(item, moment) for item in raw_runs),
key=_recency_key,
reverse=True,
),
vms,
)
return ExecutionsView(
runs=runs, unmatched_vms=unmatched, source=source, error=None