tools (common/template): expose theme settings to the template:

- `css`: additional sheets rendered after the regular css files (can be used for a dark
  theme)
- `html_data_theme`: copied to the <html> data-theme attribute
- `themes_items`: theme selector entries (theme, label, icon)
- fix missing parenthesis in `is_file`
This commit is contained in:
Goffi 2026-10-22 15:44:21 +02:00
commit 099b107c29

View file

@ -26,7 +26,7 @@ import os.path
from pathlib import Path
import re
import time
from typing import List, Optional, Tuple, Union
from typing import List, NotRequired, Optional, Tuple, TypedDict, Union
from xml.sax.saxutils import quoteattr
from babel import support
@ -374,6 +374,12 @@ class Environment(jinja2.Environment):
return super(Environment, self).get_template(name, parent, globals)
class ThemeItem(TypedDict):
theme: str
label: str
icon: NotRequired[str]
class Renderer:
def __init__(self, host, front_url_filter=None, trusted=False, private=False):
@ -440,7 +446,7 @@ class Renderer:
"settings": {},
}
theme_settings = p / "settings.json"
if theme_settings.is_file:
if theme_settings.is_file():
try:
with theme_settings.open() as f:
settings = json.load(f)
@ -656,6 +662,26 @@ class Renderer:
except KeyError:
raise exceptions.NotFound(f"no theme found for {site_name}")
def _get_themes_items(self, site: str) -> list[ThemeItem] | None:
"""Build the theme selector items from the site themes.
Get items for every theme with a "label" in its settings.
@param site: site name ("" for the default site)
@return: the items, sorted by label, None if empty
"""
themes = self.sites_themes.get(site, {})
items: list[ThemeItem] = []
for name, data in themes.items():
settings = data["settings"]
if "label" not in settings:
continue
item = ThemeItem(theme=name, label=settings["label"])
if "icon" in settings:
item["icon"] = settings["icon"]
items.append(item)
items.sort(key=lambda item: item["label"])
return items or None
def get_static_path(
self, template_data: TemplateData, filename: str, settings: Optional[dict] = None
) -> Optional[TemplateData]:
@ -1180,6 +1206,38 @@ class Renderer:
if css_contents:
kwargs["css_content" + suffix] = "\n".join(css_contents)
# extra css sheets from theme settings are rendered after the regular css files in
# base.html so they can override them
css_links = []
html_data_theme = None
themes_items = None
# template_data.site is None for absolute template paths
if template_data.site is not None:
theme_settings = self.sites_themes[template_data.site][template_data.theme][
"settings"
]
html_data_theme = theme_settings.get("html_data_theme")
themes_items = self._get_themes_items(template_data.site)
for entry in theme_settings.get("css", []):
css_path = self.get_static_path(
template_data, entry["file"], theme_settings
)
if css_path is None:
log.warning(
_("theme css file {file!r} not found, skipping").format(
file=entry["file"]
)
)
continue
link = {
"rel": "stylesheet",
"type": "text/css",
"href": self.get_front_url(css_path),
}
if entry.get("media"):
link["media"] = entry["media"]
css_links.append(link)
scripts_handler = ScriptsHandler(self, template_data)
self.set_locale(locale)
@ -1190,6 +1248,9 @@ class Renderer:
media_path=media_path,
css_files=css_files,
css_files_noscript=css_files_noscript,
css_links=css_links,
html_data_theme=html_data_theme,
themes_items=themes_items,
locale=self._locale,
locales=self.locales,
gidx=Indexer(),