Add app label to root header and auto-reload sexp templates in dev

Show current subdomain name (blog, cart, events, etc.) next to the site
title in the root header row. Remove the redundant second "cart" menu row
from cart overview and checkout error pages.

Add dev-mode hot-reload for sexp templates: track file mtimes and re-read
changed files per-request when RELOAD=true, so .sexp edits are picked up
without restarting services.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 19:33:00 +00:00
parent 6d43404b12
commit 6c44a5f3d0
7 changed files with 56 additions and 10 deletions

View File

@@ -53,11 +53,49 @@ def load_sexp_dir(directory: str) -> None:
register_components(f.read())
# ---------------------------------------------------------------------------
# Dev-mode auto-reload of sexp templates
# ---------------------------------------------------------------------------
_watched_dirs: list[str] = []
_file_mtimes: dict[str, float] = {}
def watch_sexp_dir(directory: str) -> None:
"""Register a directory for dev-mode file watching."""
_watched_dirs.append(directory)
# Seed mtimes
for fp in sorted(
glob.glob(os.path.join(directory, "*.sexp"))
+ glob.glob(os.path.join(directory, "*.sexpr"))
):
_file_mtimes[fp] = os.path.getmtime(fp)
def reload_if_changed() -> None:
"""Re-read sexp files if any have changed on disk. Called per-request in dev."""
changed = False
for directory in _watched_dirs:
for fp in sorted(
glob.glob(os.path.join(directory, "*.sexp"))
+ glob.glob(os.path.join(directory, "*.sexpr"))
):
mtime = os.path.getmtime(fp)
if fp not in _file_mtimes or _file_mtimes[fp] != mtime:
_file_mtimes[fp] = mtime
changed = True
if changed:
_COMPONENT_ENV.clear()
for directory in _watched_dirs:
load_sexp_dir(directory)
def load_service_components(service_dir: str) -> None:
"""Load service-specific s-expression components from {service_dir}/sexp/."""
sexp_dir = os.path.join(service_dir, "sexp")
if os.path.isdir(sexp_dir):
load_sexp_dir(sexp_dir)
watch_sexp_dir(sexp_dir)
def register_components(sexp_source: str) -> None: