"""A small fast_app-style layout system.
Instead of Django template inheritance (`{% extends "base.html" %}`), views
build their page body with Python components and wrap it with `Page()` /
`render_page()`. `Page()` is the equivalent of FastHTML's document wrapper:
it hoists shared `
` content (the `_HEADERS` block, analogous to
`fast_app(hdrs=...)`), renders the navbar, and assembles the full document.
"""
import json
from django.contrib.messages import get_messages
from django.http import HttpRequest, HttpResponse
from django.templatetags.static import static
from django.urls import reverse
from django.utils.html import conditional_escape
from django.utils.safestring import SafeText, mark_safe
from django_htmx.jinja import django_htmx_script
from games.templatetags.version import version, version_date
# Static head script that sets the dark/light class before paint (avoids FOUC).
_THEME_FOUC_SCRIPT = """"""
# The main module script: crown icon mount + theme-toggle wiring.
# Split around the single dynamic value (game.mastered).
_MAIN_SCRIPT_A = """"""
# Toast notification region (Alpine.js). Verbatim from the old base.html.
_TOAST_CONTAINER = """
"""
def _main_script(mastered: bool) -> str:
return _MAIN_SCRIPT_A + ("true" if mastered else "false") + _MAIN_SCRIPT_B
def Navbar(*, today_played: str, last_7_played: str, current_year: int) -> SafeText:
"""Top navigation bar."""
logo = static("icons/schedule.png")
return mark_safe(f"""""")
def Page(
content: SafeText | str,
*,
request: HttpRequest,
title: str = "",
scripts: SafeText | str = "",
mastered: bool = False,
) -> SafeText:
"""Assemble a full HTML document around `content` (the fast_app equivalent)."""
from games.views.general import global_current_year, model_counts
counts = model_counts(request)
year = global_current_year(request)["global_current_year"]
navbar = Navbar(
today_played=counts["today_played"],
last_7_played=counts["last_7_played"],
current_year=year,
)
messages = [
{"message": str(m.message), "type": (m.tags or "info")}
for m in get_messages(request)
]
# Embed as JSON; guard against `` breaking out of the tag.
messages_json = json.dumps(messages).replace("", "<\\/")
head = (
'\n\n \n'
' \n'
' \n'
' \n'
' \n'
f" Timetracker - {conditional_escape(title)}\n"
f' \n'
" \n"
f' \n'
f" {django_htmx_script(nonce=None)}\n"
f' \n'
' \n'
' \n'
' \n'
f" {_THEME_FOUC_SCRIPT}\n"
" \n"
)
body = (
' \n'
f' \n'
f' \n'
'