From a4717eed4c9260438d516efc1f59e39d0f2ab791 Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Wed, 2 Jul 2025 14:39:27 -0400 Subject: [PATCH] WIP: template merging (paths) --- src/zona/builder.py | 4 +++- src/zona/layout.py | 9 ++++--- src/zona/templates.py | 55 ++++++++++++++++++++++++++++++++----------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/zona/builder.py b/src/zona/builder.py index 556baa1..b19c0c9 100644 --- a/src/zona/builder.py +++ b/src/zona/builder.py @@ -89,7 +89,9 @@ class ZonaBuilder: reverse=True, ) templater = Templater( - template_dir=self.layout.templates, post_list=post_list + root=self.layout.root, + template_dir=self.layout.templates, + post_list=post_list, ) self.item_map = { item.source.resolve(): item for item in self.items diff --git a/src/zona/layout.py b/src/zona/layout.py index 9ee3b00..f98aeab 100644 --- a/src/zona/layout.py +++ b/src/zona/layout.py @@ -9,7 +9,7 @@ import yaml class Layout: root: Path content: Path - templates: Path + templates: Path | None output: Path @classmethod @@ -24,10 +24,9 @@ class Layout: ) if validate: if not layout.content.is_dir(): - raise FileNotFoundError("Missing required content directory!") - if not layout.templates.is_dir(): - # use the included defaults - layout.templates = util.get_resource_dir("templates") + raise FileNotFoundError( + "Missing required content directory!" + ) return layout diff --git a/src/zona/templates.py b/src/zona/templates.py index 0147df3..e63fc1c 100644 --- a/src/zona/templates.py +++ b/src/zona/templates.py @@ -1,39 +1,66 @@ +from dataclasses import dataclass from pathlib import Path from typing import Literal from jinja2 import Environment, FileSystemLoader, select_autoescape +from jinja2.environment import Template from zona.models import Item from zona.markdown import md_to_html +from zona import util -def get_header(template_dir: Path) -> str | None: - md_header = template_dir / "header.md" - html_header = template_dir / "header.html" +def get_header(root: Path) -> str | None: + md_header = root / "header.md" + html_header = root / "header.html" if md_header.exists(): return md_to_html(md_header.read_text(), None) elif html_header.exists(): return html_header.read_text() -def get_footer(template_dir: Path) -> str | None: - md_footer = template_dir / "footer.md" - html_footer = template_dir / "footer.html" +def get_footer(root: Path) -> str | None: + md_footer = root / "footer.md" + html_footer = root / "footer.html" if md_footer.exists(): return md_to_html(md_footer.read_text(), None) elif html_footer.exists(): return html_footer.read_text() +class TemplateEnv: + def __init__(self, user_dir: Path | None): + default_path = util.get_resource_dir("templates") + self.default: Environment = Environment( + loader=FileSystemLoader(default_path), + autoescape=select_autoescape(["html", "xml"]), + ) + self.user: Environment | None = ( + Environment( + loader=FileSystemLoader(user_dir), + autoescape=select_autoescape(["html", "xml"]), + ) + if user_dir + else None + ) + + def get_template(self, name: str) -> Template: + for env in ( + [self.user, self.default] if self.user else [self.default] + ): + if name in env.list_templates(): + return env.get_template(name) + raise FileNotFoundError(f"Template {name} could not be found!") + + # TODO: add next/prev post button logic to posts # TODO: add a recent posts element that can be included elsewhere? class Templater: - def __init__(self, template_dir: Path, post_list: list[Item]): - self.env: Environment = Environment( - loader=FileSystemLoader(template_dir), - autoescape=select_autoescape(["html", "xml"]), - ) - self.template_dir: Path = template_dir - self.header: str | None = get_header(template_dir) - self.footer: str | None = get_footer(template_dir) + def __init__( + self, root: Path, template_dir: Path | None, post_list: list[Item] + ): + self.env: TemplateEnv = TemplateEnv(template_dir) + self.root: Path = root + self.header: str | None = get_header(self.root) + self.footer: str | None = get_footer(self.root) self.post_list: list[Item] = post_list def render_item(self, item: Item, content: str) -> str: