diff --git a/docs/superpowers/specs/2026-05-17-server-detail-page-redesign-design.md b/docs/superpowers/specs/2026-05-17-server-detail-page-redesign-design.md index f8bbf24..f5e7d2f 100644 --- a/docs/superpowers/specs/2026-05-17-server-detail-page-redesign-design.md +++ b/docs/superpowers/specs/2026-05-17-server-detail-page-redesign-design.md @@ -236,7 +236,7 @@ chrome, only the body content differs. | `l4d2web/l4d2web/static/css/components.css` | `.state-cluster`, `.inspection-strip`, player-grid/chip, tabbar | | `l4d2web/l4d2web/static/js/tabs.js` (new) | Tab activation + expand-to-modal handler | | `l4d2web/l4d2web/templates/base.html` | Include `tabs.js` if scripts are listed centrally | -| `l4d2web/l4d2web/routes/page_routes.py` (`server_detail`) | Add `recent_players_overview` (sliced to 10) + total count | +| `l4d2web/l4d2web/routes/server_routes.py` (`live_state_fragment`) | Add `recent_players_overview` (sliced to 10) + total count | ## Reused, do not modify diff --git a/l4d2web/l4d2web/routes/server_routes.py b/l4d2web/l4d2web/routes/server_routes.py index 0a6663d..f427fc3 100644 --- a/l4d2web/l4d2web/routes/server_routes.py +++ b/l4d2web/l4d2web/routes/server_routes.py @@ -263,7 +263,15 @@ def live_state_fragment(server_id: int) -> Response: .limit(50) ).all() - recent_total = len(recent_rows) + recent_total = db.scalar( + select(func.count(func.distinct(ServerPlayerSession.steam_id_64))) + .where( + ServerPlayerSession.server_id == server.id, + ServerPlayerSession.left_at.is_not(None), + ServerPlayerSession.left_at >= recent_cutoff, + ~ServerPlayerSession.steam_id_64.in_(current_ids) if current_ids else True, + ) + ) recent_overview = recent_rows[:10] if request.args.get("view") == "recent-modal": diff --git a/l4d2web/l4d2web/static/css/components.css b/l4d2web/l4d2web/static/css/components.css index 045df98..2c588ee 100644 --- a/l4d2web/l4d2web/static/css/components.css +++ b/l4d2web/l4d2web/static/css/components.css @@ -1131,3 +1131,12 @@ div.modal.modal-wide { max-height: 60vh; overflow: auto; } + +/* Modal-specific overrides — the log/console modals are meant to give + the user *more* room than the inline tab. The .tall modifier opts + into that extra height when the same element is rendered inside a + .modal. */ +.modal .log-stream.tall, +.modal .console-transcript.tall { + max-height: 60vh; +} diff --git a/l4d2web/l4d2web/static/js/modals.js b/l4d2web/l4d2web/static/js/modals.js index fdc2e85..499bcf5 100644 --- a/l4d2web/l4d2web/static/js/modals.js +++ b/l4d2web/l4d2web/static/js/modals.js @@ -32,6 +32,7 @@ const dialog = typeof idOrEl === "string" ? document.getElementById(idOrEl) : idOrEl; if (dialog && typeof dialog.showModal === "function" && !dialog.open) { dialog.showModal(); + dialog.dispatchEvent(new CustomEvent("modal:opened", { bubbles: true })); } } @@ -70,7 +71,10 @@ // this swap; the newer click will win. if (currentRoutedPath !== path) return; const dlg = document.getElementById("modal-container"); - if (dlg && !dlg.open) dlg.showModal(); + if (dlg && !dlg.open) { + dlg.showModal(); + dlg.dispatchEvent(new CustomEvent("modal:opened", { bubbles: true })); + } }).catch((err) => { console.error("[modals] routed fetch failed", err); }); diff --git a/l4d2web/l4d2web/templates/_macros.html b/l4d2web/l4d2web/templates/_macros.html index 5473387..5850a71 100644 --- a/l4d2web/l4d2web/templates/_macros.html +++ b/l4d2web/l4d2web/templates/_macros.html @@ -1,17 +1,6 @@ -{# Reusable field-cell for the server-detail config grid and any future - key/value layouts. `value` is rendered as-is (caller can pass safe - HTML via `|safe` if needed). `editable` is a flag the caller may - use to switch rendering — currently informational only. #} -{% macro config_field(label, value, editable=False) %} -