summaryrefslogtreecommitdiff
path: root/sysret.org/themes/tabi-lean/templates/macros
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2025-09-13 15:01:06 -0600
committerAlejandro Soto <alejandro@34project.org>2025-09-13 15:01:06 -0600
commitf4fcda54638685899c730b3fa90a87d80d6dbef5 (patch)
tree0737e627cce304c3a9c4e757bc5f6571a7456091 /sysret.org/themes/tabi-lean/templates/macros
parentd8b9cf1f61cc07d625f1c37ccc28adfd58918416 (diff)
parent2c13119932765c6d788f08fb53abc244407c0d80 (diff)
Merge commit '6a7d3111b31e73fc66af5360149d41f690fbcaa4'
Diffstat (limited to 'sysret.org/themes/tabi-lean/templates/macros')
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/feed_utils.html17
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/format_date.html59
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/list_posts.html163
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/page_header.html18
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/rel_attributes.html19
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/series_page.html162
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/settings.html68
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/table_of_contents.html54
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/target_attribute.html11
-rw-r--r--sysret.org/themes/tabi-lean/templates/macros/translate.html72
10 files changed, 643 insertions, 0 deletions
diff --git a/sysret.org/themes/tabi-lean/templates/macros/feed_utils.html b/sysret.org/themes/tabi-lean/templates/macros/feed_utils.html
new file mode 100644
index 0000000..ff35194
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/feed_utils.html
@@ -0,0 +1,17 @@
+{#- Feed utility macros -#}
+
+{#- Zola 0.19.0 uses `generate_feeds`. Prior versions use `generate_feed` -#}
+{%- macro get_generate_feed() -%}
+ {{- config.generate_feeds | default(value=config.generate_feed) -}}
+{%- endmacro get_generate_feed -%}
+
+{%- macro get_feed_url() -%}
+ {{- config.feed_filenames[0] | default(value=(config.feed_filename)) -}}
+{%- endmacro get_feed_url -%}
+
+{#- Check footer feed icon conditions -#}
+{%- macro should_show_footer_feed_icon() -%}
+ {%- set generate_feed = feed_utils::get_generate_feed() == "true" -%}
+ {%- set feed_url = feed_utils::get_feed_url() -%}
+ {{- generate_feed and config.extra.feed_icon and feed_url -}}
+{%- endmacro should_show_footer_feed_icon -%}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/format_date.html b/sysret.org/themes/tabi-lean/templates/macros/format_date.html
new file mode 100644
index 0000000..f747fd1
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/format_date.html
@@ -0,0 +1,59 @@
+{%- macro format_date(date, short, language_strings="") -%}
+
+{#- Set locale -#}
+{%- set date_locale = macros_translate::translate(key="date_locale", default="en_GB", language_strings=language_strings) -%}
+
+{#- Check for language-specific date formats -#}
+{%- set language_format = "" -%}
+{%- if config.extra.date_formats -%}
+ {%- for format_config in config.extra.date_formats -%}
+ {%- if format_config.lang == lang -%}
+ {%- if short and format_config.short -%}
+ {%- set_global language_format = format_config.short -%}
+ {%- elif not short and format_config.long -%}
+ {%- set_global language_format = format_config.long -%}
+ {%- endif -%}
+ {%- endif -%}
+ {%- endfor -%}
+{%- endif -%}
+
+{%- if language_format -%}
+ {{ date | date(format=language_format, locale=date_locale) }}
+{%- elif config.extra.short_date_format and short -%}
+ {{ date | date(format=config.extra.short_date_format, locale=date_locale) }}
+{%- elif config.extra.long_date_format and not short -%}
+ {{ date | date(format=config.extra.long_date_format, locale=date_locale) }}
+{%- elif not config.extra.short_date_format and date_locale == "en_GB" -%}
+ {%- set day = date | date(format='%-d') | int -%}
+
+ {%- if day in [11, 12, 13] -%}
+ {%- set suffix = "th" -%}
+ {%- else -%}
+ {%- set last_digit = day % 10 -%}
+ {%- if last_digit == 1 -%}
+ {%- set suffix = "st" -%}
+ {%- elif last_digit == 2 -%}
+ {%- set suffix = "nd" -%}
+ {%- elif last_digit == 3 -%}
+ {%- set suffix = "rd" -%}
+ {%- else -%}
+ {%- set suffix = "th" -%}
+ {%- endif -%}
+ {%- endif -%}
+
+ {#- Return the date. -#}
+ {{ date | date(format="%-d") }}{{ suffix }}
+ {%- if short == true -%}
+ {{ date | date(format=" %b %Y") }}
+ {%- else -%}
+ {{ date | date(format=" %B %Y") }}
+ {%- endif -%}
+{%- else -%}
+ {%- if short -%}
+ {{ date | date(format="%-d %b %Y", locale=date_locale) }}
+ {%- else -%}
+ {{ date | date(format="%d %b %Y", locale=date_locale) }}
+ {%- endif -%}
+{%- endif -%}
+
+{%- endmacro -%}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/list_posts.html b/sysret.org/themes/tabi-lean/templates/macros/list_posts.html
new file mode 100644
index 0000000..2076194
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/list_posts.html
@@ -0,0 +1,163 @@
+{# `metadata` can be "dates", "indexes" or both (e.g. "dates indexes" or "indexes dates"). #}
+{# If both, the order doesn't matter and indexes will always be displayed before dates. #}
+{# It would also work with arrays (e.g. ["dates"] or ["indexes"] or even ["indexes","dates"]). #}
+{# Nevertheless, arrays cannot be used as a default value for a macro parameter in Tera (see https://github.com/Keats/tera/issues/710). #}
+{# `paginator` is only used to compute indexes metadata and can be let empty otherwise. #}
+{% macro list_posts(posts, all_posts="", max=999999, metadata="dates", language_strings="", section_path="", paginator="", pinned_first=false, current_page=1) %}
+
+{%- set separator = config.extra.separator | default(value="•") -%}
+
+{# Separate pinned and regular posts from all_posts if available, otherwise from posts #}
+{% if pinned_first %}
+ {% set source_posts = all_posts | default(value=posts) %}
+ {% set pinned_posts = [] %}
+ {% set regular_posts = [] %}
+ {% for post in source_posts %}
+ {% if post.extra.pinned %}
+ {% set_global pinned_posts = pinned_posts | concat(with=post) %}
+ {% else %}
+ {% set_global regular_posts = regular_posts | concat(with=post) %}
+ {% endif %}
+ {% endfor %}
+
+ {# On page 1 or when no pagination, show pinned then regular #}
+ {% if current_page == 1 %}
+ {% if paginator %}
+ {# With pagination: pinned + current page's posts #}
+ {% set display_posts = pinned_posts | concat(with=posts) %}
+ {% else %}
+ {# Without pagination: pinned + regular (no duplicates) #}
+ {% set display_posts = pinned_posts | concat(with=regular_posts) %}
+ {% endif %}
+ {% else %}
+ {% set display_posts = posts %}
+ {% endif %}
+{% else %}
+ {% set display_posts = posts %}
+{% endif %}
+
+<div class="bloglist-container">
+ {# Display posts #}
+ {% for post in display_posts %}
+ {% if loop.index <= max %}
+ {% if loop.index == max or loop.last %}
+ {% set bottom_divider = false %}
+ {% else %}
+ {% set bottom_divider = true %}
+ {% endif %}
+
+ <section class="bloglist-meta {% if bottom_divider -%}bottom-divider{%- endif -%}">
+ <ul>
+ {%- if "indexes" in metadata -%}
+ {%- set post_index = loop.index -%}
+ {%- set number_of_posts = posts | length -%}
+ {# in case we have a pager, the index has been computed for the current page. #}
+ {%- if paginator -%}
+ {%- set number_of_posts = paginator.total_pages -%}
+ {%- set number_of_other_pages = paginator.current_index - 1 -%}
+ {%- set posts_per_page = paginator.paginate_by -%}
+ {%- set posts_in_other_pages = number_of_other_pages * posts_per_page -%}
+ {%- set post_index = posts_in_other_pages + post_index -%}
+ {%- endif -%}
+ {%- if macros_settings::evaluate_setting_priority(setting="post_listing_index_reversed", page=section, default_global_value=false) == "true" -%}
+ {# index starts at 1 instead of 0 #}
+ {%- set post_index = number_of_posts + 1 - post_index -%}
+ {%- endif -%}
+ <li class="index-label">{{ post_index }}</li>
+ {%- endif -%}
+
+ {%- if "dates" in metadata -%}
+ {%- set allowed_post_listing_dates = ["date", "updated", "both"] -%}
+ {#- Calling the hierarchy macro here causes an error due to the "get parents" part of the macro. -#}
+ {#- This seems cleaner. -#}
+ {%- set post_listing_date = section.extra.post_listing_date | default(value=config.extra.post_listing_date) | default(value="date") -%}
+ {%- if post_listing_date not in allowed_post_listing_dates -%}
+ {{ throw(message="ERROR: Invalid value for config.extra.post_listing_date. Allowed values are 'date', 'updated', or 'both'.") }}
+ {%- endif -%}
+
+ {%- set show_date = post.date and post_listing_date == "date" or post.date and post_listing_date == "both" or post.date and post_listing_date == "updated" and not post.updated -%}
+ {%- set show_updated = post.updated and post_listing_date == "updated" or post.updated and post_listing_date == "both" -%}
+
+ {%- if show_date or show_updated -%}
+ {%- if show_date -%}
+ <li class="date">{{- macros_format_date::format_date(date=post.date, short=false, language_strings=language_strings) -}}</li>
+ {%- endif -%}
+ {%- if show_date and show_updated -%}
+ <li class="mobile-only separator">{{- separator -}}</li>
+ {%- endif -%}
+ {%- if show_updated -%}
+ {%- set last_updated_str = macros_translate::translate(key="last_updated_on", default="Updated on $DATE", language_strings=language_strings) -%}
+ {%- set formatted_date = macros_format_date::format_date(date=post.updated, short=true, language_strings=language_strings) -%}
+ {%- set updated_str = last_updated_str | replace(from="$DATE", to=formatted_date) -%}
+ <li class="date">{{ updated_str }}</li>
+ {%- endif -%}
+ {%- endif -%}
+ {%- endif -%}
+
+ {% if post.extra.local_image or post.extra.remote_image %}
+ <li class="post-thumbnail">
+ <a href="{{ post.permalink }}">
+ {% if post.extra.local_image %}
+ {% set meta = get_image_metadata(path=post.extra.local_image, allow_missing=true) %}
+ <img class="thumbnail-image"
+ alt="{{ post.extra.local_image }}"
+ src="{{ get_url(path=post.extra.local_image) }}"
+ {% if meta.width %}width="{{ meta.width }}"{% endif %}
+ {% if meta.height %}height="{{ meta.height }}"{% endif %}>
+ {% elif post.extra.remote_image %}
+ <img class="thumbnail-image"
+ alt="{{ post.extra.remote_image }}"
+ src="{{ post.extra.remote_image }}">
+ {% endif %}
+ </a>
+ </li>
+ {% endif %}
+
+ {% if post.draft %}
+ <li class="draft-label">{{ macros_translate::translate(key="draft", default="DRAFT", language_strings=language_strings) }}</li>
+ {% endif %}
+ </ul>
+ </section>
+
+ <section class="bloglist-content {% if bottom_divider -%}bottom-divider{%- endif -%}">
+ <div>
+ {% if pinned_first and post.extra.pinned %}
+ <div class="pinned-label">
+ <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M10.5 2.255v-.01c.003-.03.013-.157-.361-.35C9.703 1.668 8.967 1.5 8 1.5s-1.703.169-2.138.394c-.375.194-.365.32-.362.351v.01c-.003.03-.013.157.362.35C6.297 2.832 7.033 3 8 3s1.703-.169 2.139-.394c.374-.194.364-.32.361-.351M12 2.25c0 .738-.433 1.294-1.136 1.669l.825 2.31c1.553.48 2.561 1.32 2.561 2.52c0 1.854-2.402 2.848-5.5 2.985V15a.75.75 0 0 1-1.5 0v-3.266c-3.098-.136-5.5-1.131-5.5-2.984c0-1.2 1.008-2.04 2.561-2.52l.825-2.311C4.433 3.544 4 2.988 4 2.25C4 .75 5.79 0 8 0s4 .75 4 2.25" clip-rule="evenodd"/></svg>
+ <span>{{ macros_translate::translate(key="pinned", default="Pinned", language_strings=language_strings) }}</span>
+ </div>
+ {% endif %}
+
+ <h2 class="bloglist-title">
+ <a href="{{ post.permalink }}">{{ post.title }}</a>
+ </h2>
+
+ {% if post.taxonomies.tags %}
+ <div class="bloglist-tags">
+ {% for tag in post.taxonomies.tags %}
+ <a class="tag" href="{{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}">{{ tag }}</a>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ <div class="description">
+ {% if post.description %}
+ <p>{{ post.description | markdown(inline=true) | safe }}</p>
+ {% elif post.summary %}
+ <p>{{ post.summary | markdown(inline=true) | trim_end_matches(pat=".") | safe }}…</p>
+ {% endif %}
+ </div>
+ <a class="readmore" href="{{ post.permalink }}">{{ macros_translate::translate(key="read_more", default="Read more", language_strings=language_strings) }}&nbsp;<span class="arrow">→</span></a>
+ </div>
+ </section>
+ {% endif %}
+ {% if not loop.last %}
+ {% if loop.index == max %}
+ <div class="all-posts">
+ <a href="{{ get_url(path=section_path, lang=lang) }}/">{{ macros_translate::translate(key="all_posts", default="All posts", language_strings=language_strings) }}&nbsp;<span class="arrow">⟶</span></a>
+ </div>
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+</div>
+{% endmacro %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/page_header.html b/sysret.org/themes/tabi-lean/templates/macros/page_header.html
new file mode 100644
index 0000000..daa8d02
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/page_header.html
@@ -0,0 +1,18 @@
+{% macro page_header(title, show_feed_icon=false) %}
+
+{% set rel_attributes = macros_rel_attributes::rel_attributes() | trim %}
+
+
+{%- set blank_target = macros_target_attribute::target_attribute(new_tab=config.markdown.external_links_target_blank) -%}
+
+<h1 class="title-container section-title bottom-divider">
+ {{ title -}}
+ {% if show_feed_icon %}
+ {%- set feed_url = feed_utils::get_feed_url() -%}
+ <a class="no-hover-padding social" rel="{{ rel_attributes }}" {{ blank_target }} href="{{ get_url(path=term.path ~ feed_url, lang=lang, trailing_slash=false) | safe }}">
+ <img loading="lazy" alt="feed" title="feed" src="{{ get_url(path='/social_icons/rss.svg') }}">
+ </a>
+ {% endif %}
+</h1>
+
+{% endmacro page_header %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/rel_attributes.html b/sysret.org/themes/tabi-lean/templates/macros/rel_attributes.html
new file mode 100644
index 0000000..71672c7
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/rel_attributes.html
@@ -0,0 +1,19 @@
+{% macro rel_attributes() %}
+
+{%- set rel_attributes = [] -%}
+{%- if config.markdown.external_links_target_blank -%}
+ {%- set rel_attributes = rel_attributes | concat(with="noopener") -%}
+{%- endif -%}
+{# https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel#nofollow #}
+{# This is ignored, as it doesn't make sense to set `nofollow` on projects or social links. #}
+{# {%- if config.markdown.external_links_no_follow -%}
+ {%- set rel_attributes = rel_attributes | concat(with="nofollow") -%}
+{%- endif -%} #}
+{%- if config.markdown.external_links_no_referrer -%}
+ {%- set rel_attributes = rel_attributes | concat(with="noreferrer") -%}
+{%- endif -%}
+
+{# Return the array of rel attributes joined by a space #}
+{{- rel_attributes | join(sep=" ") -}}
+
+{% endmacro rel_attributes %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/series_page.html b/sysret.org/themes/tabi-lean/templates/macros/series_page.html
new file mode 100644
index 0000000..d5704a1
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/series_page.html
@@ -0,0 +1,162 @@
+{#
+Those macros deal with introduction and navigation for series pages.
+Using macros have been prefered over partial inclusion or inline code to make sure series_ordered_pages is forced to be used.
+A section's pages natural order is invalid in case of reversed pagination which would lead to invalid series' pages order.
+To prevent this, pages are ordered correctly in a separate variable which must be used instead of the series section pages.
+#}
+
+{#
+Computes the introduction of a series's page.
+
+Parameters:
+ - `page`: The page object being part of the series.
+ - `series_section`: The series' section the page belongs to.
+ - `series_ordered_pages`: The series' pages properly ordered (see at the top of this file for an explanation).
+ - `language_strings`: A dictionary containing the translation strings.
+#}
+{% macro process_series_template(template_type, page, series_section, series_ordered_pages, language_strings) %}
+ {%- if "series" in series_section.extra and series_section.extra.series -%}
+ {# Prepare variables for substitution #}
+ {%- set series_title = series_section.title -%}
+ {%- set series_permalink = series_section.permalink -%}
+ {%- set series_html_link = '<a href="' ~ series_section.permalink ~ '" aria_label="' ~ series_section.title ~ '">' ~ series_section.title ~ '</a>' -%}
+ {# Build series pages list #}
+ {%- set series_pages_list = [] -%}
+ {%- for series_page in series_ordered_pages -%}
+ {%- if series_page.relative_path == page.relative_path -%}
+ {%- set series_pages_list_item = '<li>' ~ series_page.title ~ '</li>' -%}
+ {%- else -%}
+ {%- set series_pages_list_item = '<li><a href="' ~ series_page.permalink ~ '" aria_label="' ~ series_page.title ~ '">' ~ series_page.title ~ '</a></li>' -%}
+ {%- endif -%}
+ {%- set_global series_pages_list = series_pages_list | concat(with=series_pages_list_item) -%}
+ {%- endfor -%}
+ {%- set series_pages_list = series_pages_list | join(sep="") -%}
+ {%- if macros_settings::evaluate_setting_priority(setting="post_listing_index_reversed", page=series_section, default_global_value=false) == "true" -%}
+ {%- set series_pages_ordered_list = '<ol reversed>' ~ series_pages_list ~ '</ol>' -%}
+ {%- else -%}
+ {%- set series_pages_ordered_list = '<ol>' ~ series_pages_list ~ '</ol>' -%}
+ {%- endif -%}
+ {%- set series_pages_unordered_list = '<ul>' ~ series_pages_list ~ '</ul>' -%}
+
+ {# Get page position and navigation info #}
+ {%- set series_pages_number = 0 -%}
+ {%- set series_page_index = 0 -%}
+ {%- set first_page = series_ordered_pages | first -%}
+ {%- set is_found = false -%}
+
+ {%- for series_page in series_ordered_pages -%}
+ {%- set_global series_pages_number = series_pages_number + 1 -%}
+ {%- if series_page.relative_path == page.relative_path -%}
+ {%- set_global series_page_index = series_pages_number -%}
+ {%- set_global is_found = true -%}
+ {%- else -%}
+ {%- if not is_found -%}
+ {%- set_global prev_page = series_page -%}
+ {%- elif not next_page is defined -%}
+ {%- set_global next_page = series_page -%}
+ {%- endif -%}
+ {%- endif -%}
+ {%- endfor -%}
+
+ {# Determine template to use based on available navigation #}
+ {%- set position = "middle" -%}
+ {%- if prev_page is defined and not next_page is defined -%}
+ {%- set_global position = "prev_only" -%}
+ {%- elif next_page is defined and not prev_page is defined -%}
+ {%- set_global position = "next_only" -%}
+ {%- endif -%}
+
+ {# Get template from config #}
+ {%- set templates_key = "series_" ~ template_type ~ "_templates" -%}
+ {%- set template = "" -%}
+ {%- if series_section.extra[templates_key] is defined -%}
+ {%- if series_section.extra[templates_key][position] is defined -%}
+ {%- set_global template = series_section.extra[templates_key][position] -%}
+ {%- elif series_section.extra[templates_key].default is defined -%}
+ {%- set_global template = series_section.extra[templates_key].default -%}
+ {%- endif -%}
+ {%- endif -%}
+
+ {# Prepare navigation variables #}
+ {%- if prev_page is defined -%}
+ {%- set prev_title = prev_page.title -%}
+ {%- set prev_permalink = prev_page.permalink -%}
+ {%- set prev_html_link = '<a href="' ~ prev_page.permalink ~ '" aria_label="' ~ prev_page.title ~ '">' ~ prev_page.title ~ '</a>' -%}
+ {%- set prev_description = prev_page.description | default(value="") -%}
+ {%- endif -%}
+ {%- if next_page is defined -%}
+ {%- set next_title = next_page.title -%}
+ {%- set next_permalink = next_page.permalink -%}
+ {%- set next_html_link = '<a href="' ~ next_page.permalink ~ '" aria_label="' ~ next_page.title ~ '">' ~ next_page.title ~ '</a>' -%}
+ {%- set next_description = next_page.description | default(value="") -%}
+ {%- endif -%}
+
+ {# Replace standard variables #}
+ {%- set template = template
+ | replace(from="$SERIES_TITLE", to=series_title)
+ | replace(from="$SERIES_PERMALINK", to=series_permalink)
+ | replace(from="$SERIES_HTML_LINK", to=series_html_link)
+ | replace(from="$FIRST_TITLE", to=first_page.title)
+ | replace(from="$FIRST_HTML_LINK", to='<a href="' ~ first_page.permalink ~ '">' ~ first_page.title ~ '</a>')
+ | replace(from="$SERIES_PAGES_NUMBER", to=series_pages_number | as_str)
+ | replace(from="$SERIES_PAGE_INDEX", to=series_page_index | as_str)
+ | replace(from="$SERIES_PAGES_OLIST", to=series_pages_ordered_list)
+ | replace(from="$SERIES_PAGES_ULIST", to=series_pages_unordered_list)
+ -%}
+
+ {# Replace navigation variables if they exist #}
+ {%- if prev_page is defined -%}
+ {%- set template = template
+ | replace(from="$PREV_TITLE", to=prev_title)
+ | replace(from="$PREV_PERMALINK", to=prev_permalink)
+ | replace(from="$PREV_HTML_LINK", to=prev_html_link)
+ | replace(from="$PREV_DESCRIPTION", to=prev_description)
+ -%}
+ {%- endif -%}
+
+ {%- if next_page is defined -%}
+ {%- set template = template
+ | replace(from="$NEXT_TITLE", to=next_title)
+ | replace(from="$NEXT_PERMALINK", to=next_permalink)
+ | replace(from="$NEXT_HTML_LINK", to=next_html_link)
+ | replace(from="$NEXT_DESCRIPTION", to=next_description)
+ -%}
+ {%- endif -%}
+
+ {# Custom placeholders #}
+ {%- if series_section.extra.series_template_placeholders is defined -%}
+ {%- set missing_vars = [] -%}
+ {%- for placeholder in series_section.extra.series_template_placeholders -%}
+ {%- if placeholder in template -%}
+ {%- set var_name = placeholder | replace(from="$", to="") | lower -%}
+ {%- if page.extra.series_template_variables is defined and page.extra.series_template_variables[var_name] is defined -%}
+ {%- set_global template = template | replace(from=placeholder, to=page.extra.series_template_variables[var_name]) -%}
+ {%- else -%}
+ {%- set_global missing_vars = missing_vars | concat(with=var_name) -%}
+ {%- endif -%}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- if missing_vars | length > 0 -%}
+ {%- set missing_vars_str = missing_vars | join(sep=", ") -%}
+ {{ throw(message="ERROR: The following variables are included in this page's series templates (`series_template_placeholders`) but have not been set in the `series_template_variables` of this page: " ~ missing_vars_str) }}
+ {%- endif -%}
+ {%- endif -%}
+
+ {# Output the processed template if not empty #}
+ {%- if template | length > 0 -%}
+ <section class="series-page-{{ template_type }}">
+ {{ template | markdown | safe }}
+ </section>
+ {%- endif -%}
+ {%- endif -%}
+{% endmacro %}
+
+{# Macro for series introduction #}
+{% macro get_introduction(page, series_section, series_ordered_pages, language_strings) %}
+ {{ macros_series_page::process_series_template(template_type="intro", page=page, series_section=series_section, series_ordered_pages=series_ordered_pages, language_strings=language_strings) }}
+{% endmacro %}
+
+{# Macro for series outro #}
+{% macro get_outro(page, series_section, series_ordered_pages, language_strings) %}
+ {{ macros_series_page::process_series_template(template_type="outro", page=page, series_section=series_section, series_ordered_pages=series_ordered_pages, language_strings=language_strings) }}
+{% endmacro %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/settings.html b/sysret.org/themes/tabi-lean/templates/macros/settings.html
new file mode 100644
index 0000000..d237d7a
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/settings.html
@@ -0,0 +1,68 @@
+{#
+Evaluates the priority of a particular setting across different scopes.
+
+The priority is as follows: page > section > config.
+
+Parameters:
+ - setting: The name of the setting to evaluate.
+ - page: The page object containing settings.
+ - default_global_value: The setting's default value.
+#}
+
+{% macro evaluate_setting_priority(setting, page, section="", default_global_value="") %}
+
+{%- if section -%}
+ {%- set current_section = section -%}
+{%- elif page -%}
+ {%- set current_section = "" -%}
+ {#- Retrieve last ancestor to determine current section, if applicable -#}
+ {%- if page.ancestors | length > 0 -%}
+ {%- set last_ancestor = page.ancestors | slice(start=-1) -%}
+ {%- set_global current_section = get_section(path=last_ancestor.0, metadata_only=true) -%}
+ {%- else -%}
+ {#- We're likely in a nested page. Try to find the parent page or nearest section. -#}
+ {%- set components = page.components -%}
+ {%- for i in range(start=1, end=components | length) -%}
+ {%- if lang == config.default_language -%}
+ {%- set potential_path = components | slice(end=components | length - i) | join(sep="/") -%}
+ {%- set potential_page = potential_path ~ "/index.md" -%}
+ {%- set potential_section = potential_path ~ "/_index.md" -%}
+ {%- else -%}
+ {%- set potential_path = components | slice(start=1, end=components | length - i) | join(sep="/") -%}
+ {%- set potential_page = potential_path ~ "/index." ~ lang ~ ".md" -%}
+ {%- set potential_section = potential_path ~ "/_index." ~ lang ~ ".md" -%}
+ {%- endif -%}
+ {#- Check for parent page first. -#}
+ {%- set page_data = load_data(path=potential_page, required=false) -%}
+ {%- if page_data -%}
+ {%- set_global current_section = get_page(path=potential_page) -%}
+ {%- break -%}
+ {%- endif -%}
+ {#- No parent page, check for section. -#}
+ {%- set section_data = load_data(path=potential_section, required=false) -%}
+ {%- if section_data -%}
+ {%- set_global current_section = get_section(path=potential_section, metadata_only=true) -%}
+ {%- break -%}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- endif -%}
+{%- endif -%}
+
+{%- set priority_order = [
+ page.extra[setting] | default(value=""),
+ current_section.extra[setting] | default(value=""),
+ config.extra[setting] | default(value="")
+] -%}
+
+{%- set output = default_global_value -%}
+
+{%- for value in priority_order -%}
+ {%- if value != "" -%}
+ {%- set_global output = value -%}
+ {%- break -%}
+ {%- endif -%}
+{%- endfor -%}
+
+{{- output -}}
+
+{% endmacro %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/table_of_contents.html b/sysret.org/themes/tabi-lean/templates/macros/table_of_contents.html
new file mode 100644
index 0000000..18a3ab6
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/table_of_contents.html
@@ -0,0 +1,54 @@
+{% macro toc(page, header, language_strings="") %}
+
+{%- set toc_levels = page.extra.toc_levels | default(value=3) -%}
+
+{% if page.extra.toc_ignore_pattern %}
+ {%- set toc_ignore_pattern = page.extra.toc_ignore_pattern -%}
+{% endif %}
+
+<div class="toc-container">
+ {% if header %}
+ <h3>{{ macros_translate::translate(key="table_of_contents", default="Table of Contents", language_strings=language_strings) }}</h3>
+ {% endif %}
+
+ <ul>
+ {% for h1 in page.toc %}
+ {# Only render headers if there's no ignore pattern, or if the header text doesn't match the pattern. #}
+ {% if not toc_ignore_pattern or not (h1.title is matching(toc_ignore_pattern)) %}
+ <li><a href="{{ h1.permalink | safe }}">{{ h1.title }}</a>
+ {% if h1.children and toc_levels > 1 %}
+ <ul>
+ {% for h2 in h1.children %}
+ {% if not toc_ignore_pattern or not (h2.title is matching(toc_ignore_pattern)) %}
+ <li><a href="{{ h2.permalink | safe }}">{{ h2.title }}</a>
+ {% if h2.children and toc_levels > 2 %}
+ <ul>
+ {% for h3 in h2.children %}
+ {% if not toc_ignore_pattern or not (h3.title is matching(toc_ignore_pattern)) %}
+ <li><a href="{{ h3.permalink | safe }}">{{ h3.title }}</a>
+ {% if h3.children and toc_levels > 3 %}
+ <ul>
+ {% for h4 in h3.children %}
+ {% if not toc_ignore_pattern or not (h4.title is matching(toc_ignore_pattern)) %}
+ <li><a href="{{ h4.permalink | safe }}">{{ h4.title }}</a></li>
+ {% endif %}
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </li>
+ {% endif %}
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </li>
+ {% endif %}
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </li>
+ {% endif %}
+ {% endfor %}
+ </ul>
+</div>
+
+{% endmacro toc %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/target_attribute.html b/sysret.org/themes/tabi-lean/templates/macros/target_attribute.html
new file mode 100644
index 0000000..2da5b9d
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/target_attribute.html
@@ -0,0 +1,11 @@
+{% macro target_attribute(new_tab) %}
+
+{%- set blank_target = "" -%}
+
+{%- if new_tab -%}
+ {%- set blank_target = "target=_blank" -%}
+{%- endif -%}
+
+{{ blank_target }}
+
+{% endmacro target_attribute %}
diff --git a/sysret.org/themes/tabi-lean/templates/macros/translate.html b/sysret.org/themes/tabi-lean/templates/macros/translate.html
new file mode 100644
index 0000000..1b138dd
--- /dev/null
+++ b/sysret.org/themes/tabi-lean/templates/macros/translate.html
@@ -0,0 +1,72 @@
+{#- Dynamically selects the appropriate translation key based on the provided `number` and `lang` context.
+If a `number` is provided, the macro will attempt to pluralize the translation key based on the language's rules.
+
+Parameters:
+- `key`: The base key for the translation string, matching the i18n files. Example: `results` to get `zero_results`, `one_results`, `many_results`, etc.
+- `number`: Optional. The numerical value associated with the key.
+- `language_strings`: A dictionary containing the translation strings.
+- `default`: A default string to use if no translation is found for the key.
+- `replace`: Optional. If `true`, the macro will replace the `$NUMBER` placeholder in the translation string with the provided `number`.
+
+The macro supports special pluralization rules for:
+- Arabic (`ar`): Has unique forms for zero, one, two, few, and many.
+- Slavic languages: Pluralization depends on the last digit of the number, with exceptions for numbers ending in 11-14.
+
+NOTE: If the logic for pluralization is modified, it needs to be replicated on the JavaScript search.
+Files: static/js/searchElasticlunr.js and its minified version at static/js/searchElasticlunr.min.js
+Function name: getPluralizationKey -#}
+{% macro translate(key, number=-1, language_strings="", default="", replace=true) %}
+ {%- set slavic_plural_languages = ["uk", "be", "bs", "hr", "ru", "sr"] -%}
+
+ {%- set key_prefix = "" -%}
+ {#- `zero_` and `one_` are common cases. We handle "many" (plural) later, after language-specific pluralization -#}
+ {%- if number == 0 -%}
+ {%- set key_prefix = "zero_" -%}
+ {%- elif number == 1 -%}
+ {%- set key_prefix = "one_" -%}
+ {%- endif -%}
+
+ {#- Pluralization -#}
+ {%- if number != -1 and key_prefix == "" -%}
+ {#- Arabic -#}
+ {%- if lang == "ar" -%}
+ {%- set modulo = number % 100 -%}
+ {%- if number == 2 -%}
+ {%- set key_prefix = "two_" -%}
+ {%- elif modulo >= 3 and modulo <= 10 -%}
+ {%- set key_prefix = "few_" -%}
+ {%- else -%}
+ {%- set key_prefix = "many_" -%}
+ {%- endif -%}
+ {#- Slavic languages like Russian or Ukrainian -#}
+ {%- elif lang in slavic_plural_languages -%}
+ {%- set modulo10 = number % 10 -%}
+ {%- set modulo100 = number % 100 -%}
+ {%- if modulo10 == 1 and modulo100 != 11 -%}
+ {%- set key_prefix = "one_" -%}
+ {%- elif modulo10 >= 2 and modulo10 <= 4 -%}
+ {%- if modulo100 >= 12 and modulo100 <= 14 -%}
+ {%- set key_prefix = "many_" -%}
+ {%- else -%}
+ {%- set key_prefix = "few_" -%}
+ {%- endif -%}
+ {%- else -%}
+ {%- set key_prefix = "many_" -%}
+ {%- endif -%}
+ {%- else -%}
+ {#- Default pluralization -#}
+ {#- Zero and one are already handled -#}
+ {%- set key_prefix = "many_" -%}
+ {%- endif -%}
+ {%- endif -%}
+
+ {#- Translated string -#}
+ {%- set final_key = key_prefix ~ key -%}
+ {%- set translated_text = language_strings[final_key] | default(value=default) | safe -%}
+
+ {#- Replace $NUMBER with the number -#}
+ {%- if replace -%}
+ {%- set translated_text = translated_text | replace(from="$NUMBER", to=number | as_str) -%}
+ {%- endif -%}
+ {{- translated_text -}}
+{% endmacro %}