- Detail panels: softer (color-mix --line-soft) border. h2 sub-section
spacing inside a single outer panel. admin and job_detail collapse to
one panel each.
- Color tokens: --color-button-primary / --color-button-danger stay
saturated in dark mode so white text on filled buttons stays readable.
- Site header: transparent, no full-width bar; aligned with panel-content
width. No more sticky.
- Page-level Delete: low-contrast outline button at the page footer
(left side, justify-content flex-start). Save buttons no longer
full-width (.stack > button { justify-self: end }).
- form-actions-inline helper for right-aligned button rows.
- New service: l4d2web.services.timeago.humanize_delta — used by the
upcoming server / overlay live-status partials.
- Server route: POST /servers/<id> renames the server (mirrors the
overlay update pattern, returns 409 on per-user duplicate).
- Overlay route: POST /overlays/<id>/script handles `action` form value
— `save_build` (default) or `save_reset_build` (wipes overlay dir
before queuing build). Redirect lands on /overlays/<id> instead of
the job page so users see the live status.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
29 lines
823 B
Python
29 lines
823 B
Python
from datetime import UTC, datetime
|
|
|
|
|
|
def humanize_delta(then: datetime, now: datetime | None = None) -> str:
|
|
if now is None:
|
|
now = datetime.now(UTC)
|
|
if then.tzinfo is None:
|
|
then = then.replace(tzinfo=UTC)
|
|
if now.tzinfo is None:
|
|
now = now.replace(tzinfo=UTC)
|
|
|
|
seconds = int((now - then).total_seconds())
|
|
if seconds < 0:
|
|
seconds = 0
|
|
|
|
if seconds < 45:
|
|
return "just now"
|
|
if seconds < 90:
|
|
return "1 minute ago"
|
|
minutes = seconds // 60
|
|
if minutes < 60:
|
|
return f"{minutes} minutes ago"
|
|
hours = minutes // 60
|
|
if hours < 24:
|
|
return "1 hour ago" if hours == 1 else f"{hours} hours ago"
|
|
days = hours // 24
|
|
if days < 7:
|
|
return "1 day ago" if days == 1 else f"{days} days ago"
|
|
return then.date().isoformat()
|