From 98c3b44f3fef2df91d46ce5735b5492fc92d920b Mon Sep 17 00:00:00 2001 From: penguinencounter <49845522+penguinencounter@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:12:14 -0700 Subject: [PATCH 1/2] Add `patchouli:entity` template --- noxfile.py | 20 ++++++++++++ .../pages/patchouli/entity.html.jinja | 21 ++++++++++++ src/hexdoc/patchouli/page/pages.py | 32 ++++++++++++++++++- test/tree.py | 10 ++++-- 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/hexdoc/_templates/pages/patchouli/entity.html.jinja diff --git a/noxfile.py b/noxfile.py index cd46195b..1daf382a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -271,12 +271,27 @@ def dummy_setup(session: nox.Session): "--data=multiloader=false", ) + # Example image: + # PNG image, 16x16, 1-bit indexed color + # Palette: 0 = #202020 1 = #50ff50 (green) + # No compression, filter 0 on all scanlines (none) + image = ( + b"\x89PNG\r\n\x1a\n" + b"\0\0\0\x0dIHDR\0\0\0\x10\0\0\0\x10\x01\x03\0\0\0\x25\x3d\x6d\x22" + b"\0\0\0\x06PLTE\x22\x22\x22\x50\xff\x50\xca\xca\x84\x15" + b"\0\0\0\x3bIDAT\x78\x01\x01\x30\0\xcf\xff\0\0\0\0\x7f\xfe\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x40\x02\0\x7f\xfe\0\0\0\x92\xd5\x06\x13\xec\x45\xbf\x6a" + b"\0\0\0\0IEND\xae\x42\x60\x82" + ) + session.log(f"write_file_tree({DUMMY_PATH}, ...)") write_file_tree( DUMMY_PATH, { "doc": { "resources": { + "assets/minecraft/textures/entities": { + "chicken.png": ("wb", image) + }, "assets/dummy/patchouli_books/dummybook": { "en_us": { "categories/foo.json": { @@ -391,6 +406,11 @@ def dummy_setup(session: nox.Session): ], "text": "have a look at these related entries!!", }, + { + "type": "patchouli:entity", + "entity": "minecraft:chicken", + "text": "ah yes, the chicken. it lays eggs and stuff", + }, { "type": "patchouli:link", "url": "https://github.com/hexdoc-dev/hexdoc", diff --git a/src/hexdoc/_templates/pages/patchouli/entity.html.jinja b/src/hexdoc/_templates/pages/patchouli/entity.html.jinja new file mode 100644 index 00000000..df092cb9 --- /dev/null +++ b/src/hexdoc/_templates/pages/patchouli/entity.html.jinja @@ -0,0 +1,21 @@ +{% extends "pages/patchouli/page.html.jinja" %} + +{% import "macros/formatting.html.jinja" as fmt with context %} +{% import "macros/textures.html.jinja" as texture_macros with context %} + +{% block body %} +
+ {{ texture_macros.render_texture( + name=page.get_name(), + texture=page.texture, + class_names=["entity-display"] + ) }} +
+{{ fmt.styled(page.text) }} +{% endblock %} diff --git a/src/hexdoc/patchouli/page/pages.py b/src/hexdoc/patchouli/page/pages.py index 03cad805..89409b46 100644 --- a/src/hexdoc/patchouli/page/pages.py +++ b/src/hexdoc/patchouli/page/pages.py @@ -3,7 +3,13 @@ from jinja2 import pass_context from jinja2.runtime import Context -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ( + Field, + PrivateAttr, + ValidationInfo, + field_validator, + model_validator, +) from hexdoc.core import Entity, ItemStack, ResourceLocation from hexdoc.minecraft import I18n, LocalizedStr @@ -47,6 +53,9 @@ class EmptyPage(Page, type="patchouli:empty", template_type="patchouli:page"): class EntityPage(PageWithText, type="patchouli:entity"): + _default_name: LocalizedStr = PrivateAttr() + _texture: PNGTexture = PrivateAttr() + entity: Entity scale: float = 1 offset: float = 0 @@ -54,6 +63,27 @@ class EntityPage(PageWithText, type="patchouli:entity"): default_rotation: float = -45 name: LocalizedStr | None = None + def get_name(self): + if self.name is None: + return self._default_name + return self.name + + @property + def texture(self): + return self._texture + + @model_validator(mode="after") + def _get_texture(self, info: ValidationInfo) -> Self: + # can't be on Entity's validator because it's frozen and + # causes circular references with the PNGTexture + assert info.context is not None + i18n = I18n.of(info) + self._default_name = i18n.localize_entity(self.entity.id) + self._texture = PNGTexture.load_id( + id="textures/entities" / self.entity.id + ".png", context=info.context + ) + return self + class ImagePage(PageWithTitle, type="patchouli:image"): images: list[Texture] diff --git a/test/tree.py b/test/tree.py index cec3b558..c2974531 100644 --- a/test/tree.py +++ b/test/tree.py @@ -8,7 +8,8 @@ else: from typing import Any as JSONValue -FileValue = JSONValue | tuple[Literal["a"], str] | Callable[[str | None], str] +OpenMode = Literal["w", "a", "wb", "ab"] +FileValue = JSONValue | tuple[OpenMode, str | bytes] | Callable[[str | None], str] FileTree = dict[str, "FileTree | FileValue"] @@ -28,7 +29,12 @@ def write_file_tree(root: str | Path, tree: FileTree): case tuple((mode, text)): # append to existing file with path.open(mode) as f: - f.write(dedent(text)) + if isinstance(text, bytes): + f.write(text) + elif isinstance(text, str): + f.write(dedent(text)) + else: + raise TypeError() case str() as text: # anything else - usually just text path.parent.mkdir(parents=True, exist_ok=True) From 9960f722d5cf8a4ff38d5390b2444b30f5f04a28 Mon Sep 17 00:00:00 2001 From: PenguinEncounter <49845522+penguinencounter@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:12:20 -0700 Subject: [PATCH 2/2] Formatting, alt text, empty string edge case, names --- .../pages/patchouli/entity.html.jinja | 30 ++++++++++--------- src/hexdoc/patchouli/page/pages.py | 19 +++++++----- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/hexdoc/_templates/pages/patchouli/entity.html.jinja b/src/hexdoc/_templates/pages/patchouli/entity.html.jinja index df092cb9..3dc512fc 100644 --- a/src/hexdoc/_templates/pages/patchouli/entity.html.jinja +++ b/src/hexdoc/_templates/pages/patchouli/entity.html.jinja @@ -4,18 +4,20 @@ {% import "macros/textures.html.jinja" as texture_macros with context %} {% block body %} -- {{ texture_macros.render_texture( - name=page.get_name(), - texture=page.texture, - class_names=["entity-display"] - ) }} -
-{{ fmt.styled(page.text) }} ++ {{ texture_macros.render_texture( + name=page.entity_name, + texture=page.texture, + class_names=["entity-display"] + ) }} +
+ {% endblock image %} + {{ fmt.styled(page.text) }} {% endblock %} diff --git a/src/hexdoc/patchouli/page/pages.py b/src/hexdoc/patchouli/page/pages.py index 89409b46..b08b771f 100644 --- a/src/hexdoc/patchouli/page/pages.py +++ b/src/hexdoc/patchouli/page/pages.py @@ -53,7 +53,7 @@ class EmptyPage(Page, type="patchouli:empty", template_type="patchouli:page"): class EntityPage(PageWithText, type="patchouli:entity"): - _default_name: LocalizedStr = PrivateAttr() + _entity_name: LocalizedStr = PrivateAttr() _texture: PNGTexture = PrivateAttr() entity: Entity @@ -61,12 +61,17 @@ class EntityPage(PageWithText, type="patchouli:entity"): offset: float = 0 rotate: bool = True default_rotation: float = -45 - name: LocalizedStr | None = None + name_field: LocalizedStr | None = Field(default=None, serialization_alias="name") - def get_name(self): - if self.name is None: - return self._default_name - return self.name + @property + def entity_name(self): + return self._entity_name + + @property + def name(self): + if self.name_field is None or not self.name_field.value: + return self._entity_name + return self.name_field @property def texture(self): @@ -78,7 +83,7 @@ def _get_texture(self, info: ValidationInfo) -> Self: # causes circular references with the PNGTexture assert info.context is not None i18n = I18n.of(info) - self._default_name = i18n.localize_entity(self.entity.id) + self._entity_name = i18n.localize_entity(self.entity.id) self._texture = PNGTexture.load_id( id="textures/entities" / self.entity.id + ".png", context=info.context )