From 2040a4e30ba0e8ff511b9b4030a0788f2f765b44 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 30 Dec 2023 11:56:37 +0100 Subject: [PATCH 1/7] Expose a `page.content_title` attribute This is only a specific sub-case of `title` but may make more sense to directly use in some cases. --- mkdocs/structure/pages.py | 18 ++++++++++-------- mkdocs/tests/structure/page_tests.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index d2b79ac4a5..57c48e8dc4 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -52,9 +52,6 @@ def __init__(self, title: str | None, file: File, config: MkDocsConfig) -> None: ) # Placeholders to be filled in later in the build process. - self.markdown = None - self._title_from_render: str | None = None - self.content = None self.toc = [] # type: ignore self.meta = {} @@ -71,14 +68,19 @@ def __repr__(self): url = self.abs_url or self.file.url return f"{name}(title={title}, url={url!r})" - markdown: str | None + markdown: str | None = None """The original Markdown content from the file.""" - content: str | None + content: str | None = None """The rendered Markdown as HTML, this is the contents of the documentation. Populated after `.render()`.""" + content_title: str | None = None + """The title extracted from the first heading of the content. + + This is only a component of `title` and may be omitted. Populated after `.render()`.""" + toc: TableOfContents """An iterable object representing the Table of contents for a page. Each item in the `toc` is an [`AnchorLink`][mkdocs.structure.toc.AnchorLink].""" @@ -238,8 +240,8 @@ def title(self) -> str | None: # type: ignore[override] if 'title' in self.meta: return self.meta['title'] - if self._title_from_render: - return self._title_from_render + if self.content_title: + return self.content_title elif self.content is None: # Preserve legacy behavior only for edge cases in plugins. title_from_md = get_markdown_title(self.markdown) if title_from_md is not None: @@ -278,7 +280,7 @@ def render(self, config: MkDocsConfig, files: Files) -> None: self.content = md.convert(self.markdown) self.toc = get_toc(getattr(md, 'toc_tokens', [])) - self._title_from_render = extract_title_ext.title + self.content_title = extract_title_ext.title self.present_anchor_ids = ( extract_anchors_ext.present_anchor_ids | raw_html_ext.present_anchor_ids ) diff --git a/mkdocs/tests/structure/page_tests.py b/mkdocs/tests/structure/page_tests.py index 22b46941c4..6b4fb8974d 100644 --- a/mkdocs/tests/structure/page_tests.py +++ b/mkdocs/tests/structure/page_tests.py @@ -314,8 +314,10 @@ def test_page_title_from_markdown(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) + self.assertIsNone(pg.content_title) self.assertEqual(pg.title, 'Welcome to MkDocs') pg.render(cfg, fl) + self.assertEqual(pg.content_title, 'Welcome to MkDocs') self.assertEqual(pg.title, 'Welcome to MkDocs') _SETEXT_CONTENT = dedent( @@ -332,10 +334,13 @@ def test_page_title_from_setext_markdown(self, docs_dir): cfg = load_config() fl = File('testing_setext_title.md', docs_dir, docs_dir, use_directory_urls=True) pg = Page(None, fl, cfg) + self.assertIsNone(pg.content_title) self.assertIsNone(pg.title) pg.read_source(cfg) + self.assertIsNone(pg.content_title) self.assertEqual(pg.title, 'Testing setext title') pg.render(cfg, fl) + self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') self.assertEqual(pg.title, 'Welcome to MkDocs Setext') @tempdir(files={'testing_setext_title.md': _SETEXT_CONTENT}) @@ -348,6 +353,7 @@ def test_page_title_from_markdown_stripped_anchorlinks(self, docs_dir): pg = Page(None, fl, cfg) pg.read_source(cfg) pg.render(cfg, fl) + self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') self.assertEqual(pg.title, 'Welcome to MkDocs Setext') _FORMATTING_CONTENT = dedent( @@ -366,6 +372,7 @@ def test_page_title_from_markdown_strip_formatting(self, docs_dir): pg = Page(None, fl, cfg) pg.read_source(cfg) pg.render(cfg, fl) + self.assertEqual(pg.content_title, '*Hello — beautiful world') self.assertEqual(pg.title, '*Hello — beautiful world') _ATTRLIST_CONTENT = dedent( @@ -416,9 +423,11 @@ def test_page_title_from_meta(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) + self.assertIsNone(pg.content_title) self.assertEqual(pg.title, 'A Page Title') self.assertEqual(pg.toc, []) pg.render(cfg, fl) + self.assertEqual(pg.content_title, 'Welcome to MkDocs') self.assertEqual(pg.title, 'A Page Title') def test_page_title_from_filename(self): @@ -442,8 +451,10 @@ def test_page_title_from_filename(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) + self.assertIsNone(pg.content_title) self.assertEqual(pg.title, 'Page title') pg.render(cfg, fl) + self.assertIsNone(pg.content_title) self.assertEqual(pg.title, 'Page title') def test_page_title_from_capitalized_filename(self): From 5651524918f977162b0ea3de38dd99fa1b569332 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 3 Feb 2024 21:50:19 +0100 Subject: [PATCH 2/7] Rework --- mkdocs/structure/pages.py | 37 ++++++++++++++++++++++------ mkdocs/tests/structure/page_tests.py | 14 +++++------ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index 1ea00338e5..e14301a372 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -76,10 +76,7 @@ def __repr__(self): Populated after `.render()`.""" - content_title: str | None = None - """The title extracted from the first heading of the content. - - This is only a component of `title` and may be omitted. Populated after `.render()`.""" + _title_from_render: str | None = None toc: TableOfContents """An iterable object representing the Table of contents for a page. Each item in @@ -244,16 +241,40 @@ def title(self) -> str | None: # type: ignore[override] if 'title' in self.meta: return self.meta['title'] - if self.content_title: - return self.content_title + if self._title_from_render: + return self._title_from_render elif self.content is None: # Preserve legacy behavior only for edge cases in plugins. title_from_md = get_markdown_title(self.markdown) if title_from_md is not None: return title_from_md + return self._title_from_filename() + + @property + def content_title(self) -> str: + """ + Similar to `title` but prioritizes the title from the document itself over the title + specified in the `nav` config. + + Raises if called before `render()` was called. + + Check these in order and use the first that returns a valid title: + - value of metadata 'title' + - content of the first H1 in Markdown content + - value provided on init (passed in from config) + - convert filename to title + """ + if self.content is None: + raise RuntimeError("`content` field hasn't been set (via `render`)") + if 'title' in self.meta: + return self.meta['title'] + if self._title_from_render: + return self._title_from_render + return self._title_from_filename() + + def _title_from_filename(self) -> str: if self.is_homepage: return 'Home' - title = self.file.name.replace('-', ' ').replace('_', ' ') # Capitalize if the filename was all lowercase, otherwise leave it as-is. if title.lower() == title: @@ -284,7 +305,7 @@ def render(self, config: MkDocsConfig, files: Files) -> None: self.content = md.convert(self.markdown) self.toc = get_toc(getattr(md, 'toc_tokens', [])) - self.content_title = extract_title_ext.title + self._title_from_render = extract_title_ext.title self.present_anchor_ids = ( extract_anchors_ext.present_anchor_ids | raw_html_ext.present_anchor_ids ) diff --git a/mkdocs/tests/structure/page_tests.py b/mkdocs/tests/structure/page_tests.py index 8a3c193e89..0bbaf08018 100644 --- a/mkdocs/tests/structure/page_tests.py +++ b/mkdocs/tests/structure/page_tests.py @@ -314,7 +314,7 @@ def test_page_title_from_markdown(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) - self.assertIsNone(pg.content_title) + self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'Welcome to MkDocs') pg.render(cfg, fl) self.assertEqual(pg.content_title, 'Welcome to MkDocs') @@ -334,10 +334,10 @@ def test_page_title_from_setext_markdown(self, docs_dir): cfg = load_config() fl = File('testing_setext_title.md', docs_dir, docs_dir, use_directory_urls=True) pg = Page(None, fl, cfg) - self.assertIsNone(pg.content_title) + self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertIsNone(pg.title) pg.read_source(cfg) - self.assertIsNone(pg.content_title) + self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'Testing setext title') pg.render(cfg, fl) self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') @@ -423,11 +423,11 @@ def test_page_title_from_meta(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) - self.assertIsNone(pg.content_title) + self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'A Page Title') self.assertEqual(pg.toc, []) pg.render(cfg, fl) - self.assertEqual(pg.content_title, 'Welcome to MkDocs') + self.assertEqual(pg.content_title, 'A Page Title') self.assertEqual(pg.title, 'A Page Title') def test_page_title_from_filename(self): @@ -451,10 +451,10 @@ def test_page_title_from_filename(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) - self.assertIsNone(pg.content_title) + self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'Page title') pg.render(cfg, fl) - self.assertIsNone(pg.content_title) + self.assertEqual(pg.content_title, 'Page title') self.assertEqual(pg.title, 'Page title') def test_page_title_from_capitalized_filename(self): From 5cbb0417733afb88343e86efc686f099c970230b Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 3 Feb 2024 22:10:20 +0100 Subject: [PATCH 3/7] Update docs --- docs/dev-guide/themes.md | 9 +++++++-- mkdocs/structure/pages.py | 28 ++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/dev-guide/themes.md b/docs/dev-guide/themes.md index ea4b99111b..359505229f 100644 --- a/docs/dev-guide/themes.md +++ b/docs/dev-guide/themes.md @@ -86,7 +86,7 @@ The simplest `main.html` file is the following: - {% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }} + {{ page.content_title }} - {{ config.site_name }} {%- for path in config.extra_css %} {%- endfor %} @@ -354,6 +354,11 @@ All `page` objects contain the following attributes: show_root_full_path: false heading_level: 5 +::: mkdocs.structure.pages.Page.content_title + options: + show_root_full_path: false + heading_level: 5 + ::: mkdocs.structure.pages.Page.content options: show_root_full_path: false @@ -453,7 +458,7 @@ object to alter the behavior. For example, to display a different title on the homepage: ```django -{% if not page.is_homepage %}{{ page.title }} - {% endif %}{{ site_name }} +{% if not page.is_homepage %}{{ page.content_title }} - {% endif %}{{ site_name }}<title> ``` ::: mkdocs.structure.pages.Page.previous_page diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index e14301a372..41ee169431 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -225,14 +225,15 @@ def _set_title(self) -> None: @weak_property def title(self) -> str | None: # type: ignore[override] """ - Returns the title for the current page. + Returns the title for the page, in the context of the nav. Before calling `read_source()`, this value is empty. It can also be updated by `render()`. - Check these in order and use the first that returns a valid title: - - value provided on init (passed in from config) + Checks these in order and uses the first that returns a valid title: + + - value specified in the `nav` config in mkdocs.yml (or the value that was passed when creating the `Page` programmatically) - value of metadata 'title' - - content of the first H1 in Markdown content + - content of the first H1 heading in Markdown content - convert filename to title """ if self.markdown is None: @@ -253,16 +254,27 @@ def title(self) -> str | None: # type: ignore[override] @property def content_title(self) -> str: """ + Returns the title for the page, in the context of the current page's content. + + NEW: **New in MkDocs 1.6.** + Similar to `title` but prioritizes the title from the document itself over the title specified in the `nav` config. - Raises if called before `render()` was called. + For themes, this should be preferred within the `<title>` tag. To apply the preferred behavior but keep compatibility with older versions, you can use: + + ```jinja + <title>{{ page.content_title or page.title }} + ``` + + Checks these in order and uses the first that returns a valid title: - Check these in order and use the first that returns a valid title: - value of metadata 'title' - - content of the first H1 in Markdown content - - value provided on init (passed in from config) + - content of the first H1 heading in Markdown content + - value specified in the `nav` config in mkdocs.yml (or the value that was passed when creating the `Page` programmatically) - convert filename to title + + When using this property outside of themes, do not access it before `render()` was called on the content, or it will raise. """ if self.content is None: raise RuntimeError("`content` field hasn't been set (via `render`)") From 5409d11f4407dc9858f3141bcd86595bc8b38b3c Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 3 Feb 2024 22:10:25 +0100 Subject: [PATCH 4/7] Apply this to built-in themes --- mkdocs/themes/mkdocs/base.html | 2 +- mkdocs/themes/readthedocs/base.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/themes/mkdocs/base.html b/mkdocs/themes/mkdocs/base.html index 2b077bd016..4f6cb089cf 100644 --- a/mkdocs/themes/mkdocs/base.html +++ b/mkdocs/themes/mkdocs/base.html @@ -13,7 +13,7 @@ {%- endblock %} {%- block htmltitle %} - {% if page and page.title and not page.is_homepage %}{{ page.title }} - {% endif %}{{ config.site_name }} + {% if page and not page.is_homepage %}{{ page.content_title }} - {% endif %}{{ config.site_name }} {%- endblock %} {%- block styles %} diff --git a/mkdocs/themes/readthedocs/base.html b/mkdocs/themes/readthedocs/base.html index 4265d4a2ff..c8a6135719 100644 --- a/mkdocs/themes/readthedocs/base.html +++ b/mkdocs/themes/readthedocs/base.html @@ -16,7 +16,7 @@ {%- endblock %} {%- block htmltitle %} - {% if page and page.title and not page.is_homepage %}{{ page.title }} - {% endif %}{{ config.site_name }} + {% if page and not page.is_homepage %}{{ page.content_title }} - {% endif %}{{ config.site_name }} {%- endblock %} {%- block styles %} From c1d3520eea2dd15e2e3732cae43a00eecced40a5 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 3 Feb 2024 23:36:11 +0100 Subject: [PATCH 5/7] Editing the `title` should still be possible --- mkdocs/structure/pages.py | 19 ++++++++----------- mkdocs/tests/structure/page_tests.py | 26 +++++++++++++++++--------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index 41ee169431..5ad12a79f1 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -249,7 +249,13 @@ def title(self) -> str | None: # type: ignore[override] if title_from_md is not None: return title_from_md - return self._title_from_filename() + if self.is_homepage: + return 'Home' + title = self.file.name.replace('-', ' ').replace('_', ' ') + # Capitalize if the filename was all lowercase, otherwise leave it as-is. + if title.lower() == title: + title = title.capitalize() + return title @property def content_title(self) -> str: @@ -282,16 +288,7 @@ def content_title(self) -> str: return self.meta['title'] if self._title_from_render: return self._title_from_render - return self._title_from_filename() - - def _title_from_filename(self) -> str: - if self.is_homepage: - return 'Home' - title = self.file.name.replace('-', ' ').replace('_', ' ') - # Capitalize if the filename was all lowercase, otherwise leave it as-is. - if title.lower() == title: - title = title.capitalize() - return title + return self.title def render(self, config: MkDocsConfig, files: Files) -> None: """Convert the Markdown source file to HTML as per the config.""" diff --git a/mkdocs/tests/structure/page_tests.py b/mkdocs/tests/structure/page_tests.py index 0bbaf08018..311f693a09 100644 --- a/mkdocs/tests/structure/page_tests.py +++ b/mkdocs/tests/structure/page_tests.py @@ -314,11 +314,11 @@ def test_page_title_from_markdown(self): self.assertEqual(pg.next_page, None) self.assertEqual(pg.parent, None) self.assertEqual(pg.previous_page, None) - self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'Welcome to MkDocs') + self.assertRaises(RuntimeError, lambda: pg.content_title) pg.render(cfg, fl) - self.assertEqual(pg.content_title, 'Welcome to MkDocs') self.assertEqual(pg.title, 'Welcome to MkDocs') + self.assertEqual(pg.content_title, 'Welcome to MkDocs') _SETEXT_CONTENT = dedent( ''' @@ -334,14 +334,13 @@ def test_page_title_from_setext_markdown(self, docs_dir): cfg = load_config() fl = File('testing_setext_title.md', docs_dir, docs_dir, use_directory_urls=True) pg = Page(None, fl, cfg) - self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertIsNone(pg.title) pg.read_source(cfg) - self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'Testing setext title') + self.assertRaises(RuntimeError, lambda: pg.content_title) pg.render(cfg, fl) - self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') self.assertEqual(pg.title, 'Welcome to MkDocs Setext') + self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') @tempdir(files={'testing_setext_title.md': _SETEXT_CONTENT}) def test_page_title_from_markdown_stripped_anchorlinks(self, docs_dir): @@ -353,8 +352,8 @@ def test_page_title_from_markdown_stripped_anchorlinks(self, docs_dir): pg = Page(None, fl, cfg) pg.read_source(cfg) pg.render(cfg, fl) - self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') self.assertEqual(pg.title, 'Welcome to MkDocs Setext') + self.assertEqual(pg.content_title, 'Welcome to MkDocs Setext') _FORMATTING_CONTENT = dedent( ''' @@ -372,8 +371,8 @@ def test_page_title_from_markdown_strip_formatting(self, docs_dir): pg = Page(None, fl, cfg) pg.read_source(cfg) pg.render(cfg, fl) - self.assertEqual(pg.content_title, '*Hello — beautiful world') self.assertEqual(pg.title, '*Hello — beautiful world') + self.assertEqual(pg.content_title, '*Hello — beautiful world') _ATTRLIST_CONTENT = dedent( ''' @@ -427,8 +426,12 @@ def test_page_title_from_meta(self): self.assertEqual(pg.title, 'A Page Title') self.assertEqual(pg.toc, []) pg.render(cfg, fl) - self.assertEqual(pg.content_title, 'A Page Title') self.assertEqual(pg.title, 'A Page Title') + self.assertEqual(pg.content_title, 'A Page Title') + # Test setting the title directly (e.g. through a plugin): + pg.title = 'New title' + self.assertEqual(pg.title, 'New title') + self.assertEqual(pg.content_title, 'A Page Title') def test_page_title_from_filename(self): cfg = load_config(docs_dir=DOCS_DIR) @@ -454,8 +457,13 @@ def test_page_title_from_filename(self): self.assertRaises(RuntimeError, lambda: pg.content_title) self.assertEqual(pg.title, 'Page title') pg.render(cfg, fl) - self.assertEqual(pg.content_title, 'Page title') self.assertEqual(pg.title, 'Page title') + self.assertEqual(pg.content_title, 'Page title') + # Test setting the title directly (e.g. through a plugin): + pg.title = 'New title' + self.assertEqual(pg.title, 'New title') + # Content title is missing, title still takes precedence. + self.assertEqual(pg.content_title, 'New title') def test_page_title_from_capitalized_filename(self): cfg = load_config(docs_dir=DOCS_DIR) From 44f44947f2c517e485194914f8f85eb8486a4def Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 16 Mar 2024 16:10:37 +0100 Subject: [PATCH 6/7] Revert any usages of content_title - keep it as just a new attribute --- docs/dev-guide/themes.md | 4 ++-- mkdocs/themes/mkdocs/base.html | 2 +- mkdocs/themes/readthedocs/base.html | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/dev-guide/themes.md b/docs/dev-guide/themes.md index 359505229f..e147169fb4 100644 --- a/docs/dev-guide/themes.md +++ b/docs/dev-guide/themes.md @@ -86,7 +86,7 @@ The simplest `main.html` file is the following: - {{ page.content_title }} - {{ config.site_name }} + {% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }} {%- for path in config.extra_css %} {%- endfor %} @@ -458,7 +458,7 @@ object to alter the behavior. For example, to display a different title on the homepage: ```django -{% if not page.is_homepage %}{{ page.content_title }} - {% endif %}{{ site_name }}<title> +<title>{% if not page.is_homepage %}{{ page.title }} - {% endif %}{{ site_name }} ``` ::: mkdocs.structure.pages.Page.previous_page diff --git a/mkdocs/themes/mkdocs/base.html b/mkdocs/themes/mkdocs/base.html index 4f6cb089cf..2b077bd016 100644 --- a/mkdocs/themes/mkdocs/base.html +++ b/mkdocs/themes/mkdocs/base.html @@ -13,7 +13,7 @@ {%- endblock %} {%- block htmltitle %} - {% if page and not page.is_homepage %}{{ page.content_title }} - {% endif %}{{ config.site_name }} + {% if page and page.title and not page.is_homepage %}{{ page.title }} - {% endif %}{{ config.site_name }} {%- endblock %} {%- block styles %} diff --git a/mkdocs/themes/readthedocs/base.html b/mkdocs/themes/readthedocs/base.html index c8a6135719..4265d4a2ff 100644 --- a/mkdocs/themes/readthedocs/base.html +++ b/mkdocs/themes/readthedocs/base.html @@ -16,7 +16,7 @@ {%- endblock %} {%- block htmltitle %} - {% if page and not page.is_homepage %}{{ page.content_title }} - {% endif %}{{ config.site_name }} + {% if page and page.title and not page.is_homepage %}{{ page.title }} - {% endif %}{{ config.site_name }} {%- endblock %} {%- block styles %} From 054f97997069b990a1a0996de8b725be57311a77 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 16 Mar 2024 16:14:42 +0100 Subject: [PATCH 7/7] Fixup --- mkdocs/structure/pages.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index ec020beae8..277b99ca55 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -252,6 +252,7 @@ def title(self) -> str | None: # type: ignore[override] if self.is_homepage: return 'Home' + title = self.file.name.replace('-', ' ').replace('_', ' ') # Capitalize if the filename was all lowercase, otherwise leave it as-is. if title.lower() == title: