Merge commit '8be91ee3d9' as 'themes/tabi-lean'

This commit is contained in:
Fabian Montero 2025-09-14 16:00:14 -06:00
commit 2c6602e3b2
336 changed files with 25227 additions and 0 deletions

View file

@ -0,0 +1,38 @@
{% set analytics_service = config.extra.analytics.service %}
{% set analytics_id = config.extra.analytics.id | default(value="") %}
{% set self_hosted_url = config.extra.analytics.self_hosted_url | default(value="") %}
{% if analytics_service == "goatcounter" %}
{# Prevent non-demo sites from using the demo analytics account #}
{% if self_hosted_url == "https://tabi-stats.osc.garden" and config.base_url == "https://welpo.github.io/tabi" or self_hosted_url != "https://tabi-stats.osc.garden" %}
<script async
{% if self_hosted_url %}
data-goatcounter="{{ self_hosted_url ~ '/count' }}"
src="{{ self_hosted_url ~ '/count.js' }}"
{% else %}
data-goatcounter="https://{{ analytics_id }}.goatcounter.com/count"
src="https://gc.zgo.at/count.js"
{% endif %}
></script>
{% endif %}
{% elif analytics_service == "umami" %}
<script async defer
{% if self_hosted_url %}
data-website-id="{{ analytics_id }}"
src="{{ self_hosted_url ~ '/script.js' }}"
{% else %}
data-website-id="{{ analytics_id }}"
src="https://cloud.umami.is/script.js"
{% endif %}
{% if config.extra.analytics.do_not_track %}data-do-not-track="true"{% endif %}>
</script>
{% elif analytics_service == "plausible" %}
<script
defer
data-domain="{{ analytics_id }}"
src="{% if self_hosted_url %}{{ self_hosted_url ~ '/js/plausible.js' }}{% else %}https://plausible.io/js/script.js{% endif %}"
></script>
{% endif %}

View file

@ -0,0 +1,46 @@
{%- set rel_attributes = macros_rel_attributes::rel_attributes() | trim -%}
{%- set max_projects = max_projects | default(value=999999) -%}
<div class="cards">
{%- for page in show_pages %}
{# Used only in main page #}
{% if loop.index > max_projects %}
{% break %}
{% endif %}
{# Determine which URL to use, default is page.permalink #}
{%- set blank_target = macros_target_attribute::target_attribute(new_tab=config.markdown.external_links_target_blank and page.extra.link_to) -%}
{% set target_url = page.extra.link_to | default(value=page.permalink) %}
<a rel="{{ rel_attributes }}"
{{ blank_target }}
href="{{ target_url }}"
class="card"
{% if page.taxonomies %}
data-tags="{% for tax_name, terms in page.taxonomies %}{% for term in terms | unique %}{{ term | lower }}{% if not loop.last %},{% endif %}{% endfor %}{% endfor %}"
{% endif %}>
{% if page.extra.local_image %}
{% set meta = get_image_metadata(path=page.extra.local_image, allow_missing=true) %}
<img class="card-image"
alt="{{ page.extra.local_image }}"
src="{{ get_url(path=page.extra.local_image) }}"
{% if meta.width %}width="{{ meta.width }}"{% endif %}
{% if meta.height %}height="{{ meta.height }}"{% endif %}>
{% elif page.extra.remote_image %}
<img class="card-image"
alt="{{ page.extra.remote_image }}"
src="{{ page.extra.remote_image }}">
{% else %}
<div class="card-image-placeholder"></div>
{% endif %}
<div class="card-info">
<h2 class="card-title">{{ page.title | markdown(inline=true) | safe }}</h2>
<div class="card-description">
{% if page.description %}
{{ page.description | markdown(inline=true) | safe }}
{% endif %}
</div>
</div>
</a>
{% endfor -%}
</div>

View file

@ -0,0 +1,95 @@
{% set automatic_loading = config.extra[comment_system].automatic_loading %}
<div id="comments" class="comments"
{% if comment_system == "giscus" %}
data-repo="{{ config.extra.giscus.repo }}"
data-repo-id="{{ config.extra.giscus.repo_id }}"
data-category="{{ config.extra.giscus.category }}"
data-category-id="{{ config.extra.giscus.category_id }}"
{% if config.extra.giscus.mapping == "slug" %}
data-mapping="specific"
data-term="{{ page.slug }}"
{% else %}
data-mapping="{{ config.extra.giscus.mapping }}"
{% endif %}
data-strict="{{ config.extra.giscus.strict_title_matching }}"
data-reactions-enabled="{{ config.extra.giscus.enable_reactions }}"
{% if config.extra.giscus.comment_box_above_comments %}
data-input-position="top"
{% else %}
data-input-position="bottom"
{% endif %}
data-light-theme="{{ config.extra.giscus.light_theme }}"
data-dark-theme="{{ config.extra.giscus.dark_theme }}"
{% if config.extra.giscus.lang %}
data-lang="{{ config.extra.giscus.lang }}"
{% else %}
data-lang="{{ lang }}"
{% endif %}
data-lazy-loading="{{ config.extra.giscus.lazy_loading }}"
>
{% elif comment_system == "utterances" %}
data-repo="{{ config.extra.utterances.repo }}"
{% if config.extra.utterances.issue_term == "slug" %}
data-issue-term="{{ page.slug }}"
{% else %}
data-issue-term="{{ config.extra.utterances.issue_term }}"
{% endif %}
data-label="{{ config.extra.utterances.label }}"
data-light-theme="{{ config.extra.utterances.light_theme }}"
data-dark-theme="{{ config.extra.utterances.dark_theme }}"
data-lazy-loading="{{ config.extra.utterances.lazy_loading }}"
>
{% elif comment_system == "hyvortalk" %}
data-website-id="{{ config.extra.hyvortalk.website_id }}"
{% if config.extra.hyvortalk.page_id_is_slug %}
data-page-id="{{ page.slug }}"
{% else %}
data-page-id="{{ current_url }}"
{% endif %}
{% if config.extra.hyvortalk.lang %}
data-page-language="{{ config.extra.hyvortalk.lang }}"
{% else %}
data-page-language="{{ lang }}"
{% endif %}
data-page-author="{{ config.extra.hyvortalk.page_author }}"
{% if config.extra.hyvortalk.lazy_loading %}
data-loading="lazy"
{% else %}
data-loading="default"
{% endif %}
>
{% elif comment_system == "isso" %}
data-endpoint-url="{{ config.extra.isso.endpoint_url }}"
{% if config.extra.isso.page_id_is_slug %}
{%- set default_lang_url = current_path | replace(from='/' ~ lang ~ '/', to = '/') -%}
data-isso-id="{{ default_lang_url }}"
data-title="{{ default_lang_url }}"
{% endif %}
{% if config.extra.isso.lang %}
data-page-language="{{ config.extra.isso.lang }}"
{% else %}
data-page-language="{{ lang }}"
{% endif %}
data-max-comments-top="{{ config.extra.isso.max_comments_top }}"
data-max-comments-nested="{{ config.extra.isso.max_comments_nested }}"
data-avatar="{{ config.extra.isso.avatar }}"
data-voting="{{ config.extra.isso.voting }}"
data-page-author-hashes="{{ config.extra.isso.page_author_hashes }}"
data-lazy-loading="{{ config.extra.isso.lazy_loading }}"
>
{% endif %}
{% if automatic_loading %}
<script src="{{ get_url(path='js/' ~ comment_system ~ '.min.js', trailing_slash=false) | safe }}" async></script>
{% else %}
<button id="load-comments" class="load-comments-button" data-script-src="{{ get_url(path='js/' ~ comment_system ~ '.min.js', trailing_slash=false) | safe }}">{{ macros_translate::translate(key="load_comments", default="Load comments", language_strings=language_strings) }}</button>
<script src="{{ get_url(path='js/loadComments.min.js', trailing_slash=false) | safe }}" async></script>
{% endif %}
<noscript>You need JavaScript to view the comments.</noscript>
</div>

View file

@ -0,0 +1,119 @@
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'
{%- if config.extra.allowed_domains -%}
;
{#- Check if a comment system is enabled to allow the necessary domains and directives -#}
{%- set utterances_enabled = config.extra.utterances.enabled_for_all_posts or page.extra.utterances -%}
{%- set giscus_enabled = config.extra.giscus.enabled_for_all_posts or page.extra.giscus -%}
{%- set hyvortalk_enabled = config.extra.hyvortalk.enabled_for_all_posts or page.extra.hyvortalk -%}
{%- set isso_enabled = config.extra.isso.enabled_for_all_posts or page.extra.isso -%}
{%- if page -%}
{%- set iine_enabled = macros_settings::evaluate_setting_priority(setting="iine", page=page, default_global_value=false) == "true" -%}
{%- endif -%}
{%- if page -%}
{%- set mermaid_enabled = macros_settings::evaluate_setting_priority(setting="mermaid", page=page, default_global_value=false) == "true" -%}
{%- endif -%}
{%- set serve_local_mermaid = config.extra.serve_local_mermaid | default(value=true) -%}
{#- Initialise a base script-src directive -#}
{%- set script_src = "script-src 'self'" -%}
{#- Initialise a base connect-src directive -#}
{%- set connect_src = "connect-src 'self'" -%}
{# Base logic for appending analytics domains #}
{%- set analytics_url = config.extra.analytics.self_hosted_url | default(value="") %}
{%- if analytics_url -%}
{%- set script_src = script_src ~ " " ~ analytics_url -%}
{%- set connect_src = connect_src ~ " " ~ analytics_url -%}
{%- else -%}
{%- if config.extra.analytics.service -%}
{%- if config.extra.analytics.service == "goatcounter" -%}
{%- set script_src = script_src ~ " gc.zgo.at" -%}
{%- set connect_src = connect_src ~ " " ~ config.extra.analytics.id ~ ".goatcounter.com/count" -%}
{%- elif config.extra.analytics.service == "umami" -%}
{%- set script_src = script_src ~ " cloud.umami.is" -%}
{%- set connect_src = connect_src ~ " *.umami.dev" ~ " cloud.umami.is" -%}
{%- elif config.extra.analytics.service == "plausible" -%}
{%- set script_src = script_src ~ " plausible.io" -%}
{%- set connect_src = connect_src ~ " plausible.io" -%}
{%- endif -%}
{%- endif -%}
{%- endif -%}
{%- if hyvortalk_enabled -%}
{%- set connect_src = connect_src ~ " talk.hyvor.com" -%}
{%- set script_src = script_src ~ " talk.hyvor.com" -%}
{%- elif isso_enabled -%}
{%- set connect_src = connect_src ~ " " ~ config.extra.isso.endpoint_url -%}
{%- set script_src = script_src ~ " " ~ config.extra.isso.endpoint_url -%}
{%- elif giscus_enabled -%}
{%- set script_src = script_src ~ " " ~ " giscus.app" -%}
{%- elif utterances_enabled -%}
{%- set script_src = script_src ~ " " ~ " utteranc.es" -%}
{%- endif -%}
{%- if (mermaid_enabled and not serve_local_mermaid) or iine_enabled -%}
{%- set script_src = script_src ~ " " ~ " cdn.jsdelivr.net" -%}
{%- endif -%}
{#- Check if a webmention system is enabled to allow the necessary domains and directives -#}
{%- if config.extra.webmentions.enable -%}
{%- set connect_src = connect_src ~ " webmention.io" -%}
{%- endif -%}
{#- Check if iine like buttons are enabled to allow the necessary domains -#}
{%- if iine_enabled -%}
{%- set connect_src = connect_src ~ " vhiweeypifbwacashxjz.supabase.co" -%}
{%- endif -%}
{#- Append WebSocket for Zola serve mode -#}
{%- if config.mode == "serve" -%}
{%- set connect_src = connect_src ~ " ws:" -%}
{%- endif -%}
{%- for domain in config.extra.allowed_domains -%}
{%- if domain.directive == "connect-src" -%}
{%- set configured_connect_src = domain.domains | join(sep=' ') | safe -%}
{%- set_global connect_src = connect_src ~ " " ~ configured_connect_src -%}
{%- continue -%}
{%- endif -%}
{%- if domain.directive == "script-src" -%}
{%- set configured_script_src = domain.domains | join(sep=' ') | safe -%}
{%- set_global script_src = script_src ~ " " ~ configured_script_src -%}
{%- continue -%}
{%- endif -%}
{#- Handle directives that are not connect-src -#}
{{ domain.directive }} {{ domain.domains | join(sep=' ') | safe -}}
{%- if domain.directive == "style-src" -%}
{%- if utterances_enabled or hyvortalk_enabled or mermaid_enabled %} 'unsafe-inline'
{%- endif -%}
{%- endif -%}
{%- if domain.directive == "font-src" -%}
{%- if mermaid_enabled %} 'self'
{%- endif -%}
{%- endif -%}
{%- if domain.directive == "frame-src" -%}
{%- if giscus_enabled %} giscus.app
{%- elif utterances_enabled %} utteranc.es
{%- elif hyvortalk_enabled %} talk.hyvor.com
{%- endif %}
{%- endif -%}
{%- if not loop.last -%}
;
{%- endif -%}
{%- endfor -%}
{#- Insert the generated connect-src -#}
{{ ";" ~ connect_src }}
{#- Insert the generated script-src -#}
{{ ";" ~ script_src }}
{%- endif -%}">

View file

@ -0,0 +1,39 @@
{%- if config.extra.copyright -%}
{% set copyright = config.extra.copyright %}
{# Try to look for a language-specific copyright notice in the new config setup #}
{%- if config.extra.copyright_translations -%}
{%- if lang in config.extra.copyright_translations -%}
{% set copyright = config.extra.copyright_translations[lang] %}
{%- endif -%}
{%- elif config.extra.translate_copyright -%}
{# Old way to translate the copyright through toml files #}
{{ throw(message="ERROR: The 'translate_copyright' feature has been deprecated. Please set 'copyright_translations' in config.toml. See the documentation: https://welpo.github.io/tabi/blog/mastering-tabi-settings/#copyright") }}
{%- endif -%}
{# Check for missing variables in the notice #}
{% set copyright_placeholders = ["$AUTHOR", "$TITLE"] %}
{% set missing_vars = [] %}
{% for placeholder in copyright_placeholders %}
{% if placeholder in copyright %}
{# Attempt to retrieve the corresponding variable by trimming the $ sign and converting to lowercase #}
{% set var_name = placeholder | replace(from="$", to="") | lower %}
{% if not config[var_name] %}
{# Append the variable name to the list of missing variables #}
{% 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 `copyright` but have not been set in the config.toml: " ~ missing_vars_str) }}
{% endif %}
{# At this point, we know that all variables needed defined, so we can safely override the missing ones #}
{% set author = config.author | default(value="") %}
{% set title = config.title | default(value="") %}
{# Render the copyright notice, replacing the variables #}
{% set current_year = now() | date(format="%Y") %}
<p>{{ copyright | replace(from="$AUTHOR", to=author) | replace(from="$TITLE", to=title) | replace(from="$CURRENT_YEAR", to=current_year) | replace(from="$SEPARATOR", to=separator) | markdown | safe }}</p>
{%- endif -%}

View file

@ -0,0 +1,88 @@
{%- set page_or_section = page | default(value=section) -%}
{# prepare parameters for evaluate_setting_priority macro #}
{%- set page_s = page | default(value="") -%}
{%- set section_s = section | default(value="") -%}
{# Quick navigation buttons #}
{% if macros_settings::evaluate_setting_priority(setting="quick_navigation_buttons", page=page_s, section=section_s, default_global_value=false) == "true" %}
<div id="button-container">
{# Button to go show a floating Table of Contents #}
{% if page_or_section.toc %}
<div id="toc-floating-container">
<input type="checkbox" id="toc-toggle" class="toggle"/>
<label for="toc-toggle" class="overlay"></label>
<label for="toc-toggle" id="toc-button" class="button" title="{{ macros_translate::translate(key="toggle_toc", default="Toggle Table of Contents", language_strings=language_strings) }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M414.82-193.094q-18.044 0-30.497-12.32-12.453-12.319-12.453-30.036t12.453-30.086q12.453-12.37 30.497-12.37h392.767q17.237 0 29.927 12.487 12.69 12.486 12.69 30.203 0 17.716-12.69 29.919t-29.927 12.203H414.82Zm0-244.833q-18.044 0-30.497-12.487Q371.87-462.9 371.87-480.45t12.453-29.92q12.453-12.369 30.497-12.369h392.767q17.237 0 29.927 12.511 12.69 12.512 12.69 29.845 0 17.716-12.69 30.086-12.69 12.37-29.927 12.37H414.82Zm0-245.167q-18.044 0-30.497-12.32t-12.453-30.037q0-17.716 12.453-30.086 12.453-12.369 30.497-12.369h392.767q17.237 0 29.927 12.486 12.69 12.487 12.69 30.203 0 17.717-12.69 29.92-12.69 12.203-29.927 12.203H414.82ZM189.379-156.681q-32.652 0-55.878-22.829t-23.226-55.731q0-32.549 23.15-55.647 23.151-23.097 55.95-23.097 32.799 0 55.313 23.484 22.515 23.484 22.515 56.246 0 32.212-22.861 54.893-22.861 22.681-54.963 22.681Zm0-245.167q-32.652 0-55.878-23.134-23.226-23.135-23.226-55.623 0-32.487 23.467-55.517t56.12-23.03q32.102 0 54.721 23.288 22.62 23.288 22.62 55.775 0 32.488-22.861 55.364-22.861 22.877-54.963 22.877Zm-.82-244.833q-32.224 0-55.254-23.288-23.03-23.289-23.03-55.623 0-32.333 23.271-55.364 23.272-23.03 55.495-23.03 32.224 0 55.193 23.288 22.969 23.289 22.969 55.622 0 32.334-23.21 55.364-23.21 23.031-55.434 23.031Z"/></svg>
</label>
<div class="toc-content">
{{ macros_toc::toc(page=page_or_section, header=false, language_strings=language_strings) }}
</div>
</div>
{% endif %}
{# Button to go to the comment/webmentions section #}
{% if comment_system or config.extra.webmentions.enable %}
{%- if comment_system -%}
{#- Comments are shown above webmentions -#}
{%- set comments_id = "comments" -%}
{%- else -%}
{%- set comments_id = "webmentions" -%}
{%- endif -%}
<a href="#{{- comments_id -}}" id="comments-button" class="no-hover-padding" title="{{ macros_translate::translate(key="go_to_comments", default="Go to comments section", language_strings=language_strings) }}">
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd" fill-rule="evenodd"/></svg>
</a>
{% endif %}
{# Button to go to the top of the page #}
<a href="#" id="top-button" class="no-hover-padding" title="{{ macros_translate::translate(key="go_to_top", default="Go to the top of the page", language_strings=language_strings) }}">
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"/></svg>
</a>
</div>
{% endif %}
{# Add KaTeX functionality #}
{%- if macros_settings::evaluate_setting_priority(setting="katex", page=page_s, section=section_s, default_global_value=false) == "true" -%}
<link rel="stylesheet" href="{{ get_url(path='katex.min.css', trailing_slash=false) | safe }}">
<script defer src="{{ get_url(path='js/katex.min.js', trailing_slash=false) | safe }}"></script>
{%- endif -%}
{# Load mermaid.js #}
{%- if macros_settings::evaluate_setting_priority(setting="mermaid", page=page_s, section=section_s, default_global_value=false) == "true" -%}
{%- if config.extra.serve_local_mermaid | default(value=true) -%}
<script defer src="{{ get_url(path='js/mermaid.min.js', trailing_slash=false) | safe }}"></script>
{%- else -%}
<script defer src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
{%- endif -%}
{%- endif -%}
{# Add copy button to code blocks #}
{%- if macros_settings::evaluate_setting_priority(setting="copy_button", page=page_s, section=section_s, default_global_value=true) == "true" -%}
{#- Add hidden HTML elements with the translated strings for the button's interactions -#}
<span id="copy-success" class="hidden">
{{ macros_translate::translate(key="copied", default="Copied!", language_strings=language_strings) }}
</span>
<span id="copy-init" class="hidden">
{{ macros_translate::translate(key="copy_code_to_clipboard", default="Copy code to clipboard", language_strings=language_strings) }}
</span>
<script defer src="{{ get_url(path='js/copyCodeToClipboard.min.js', trailing_slash=false) | safe }}"></script>
{%- endif -%}
{# JavaScript to make code block names clickable when they are URLs: https://welpo.github.io/tabi/blog/shortcodes/#show-source-or-path #}
{# Note: "add_src_to_code_block" is deprecated in favor of "code_block_name_links". It will be removed in a future release. #}
{# See https://github.com/welpo/tabi/pull/489 #}
{%- if macros_settings::evaluate_setting_priority(setting="code_block_name_links", page=page_s, section=section_s, default_global_value=false) == "true"
or macros_settings::evaluate_setting_priority(setting="add_src_to_code_block", page=page_s, section=section_s, default_global_value=false) == "true" -%}
<script defer src="{{ get_url(path='js/codeBlockNameLinks.min.js', trailing_slash=false) | safe }}"></script>
{%- endif -%}
{# Add backlinks to footnotes #}
{%- if macros_settings::evaluate_setting_priority(setting="footnote_backlinks", page=page_s, section=section_s, default_global_value=false) == "true" -%}
<script defer src="{{ get_url(path='js/footnoteBacklinks.min.js', trailing_slash=false | safe )}}"></script>
{%- endif -%}
{# Add iine.js for the like button #}
{%- if macros_settings::evaluate_setting_priority(setting="iine", page=page_s, section=section_s, default_global_value=false) == "true" -%}
<script defer src="https://cdn.jsdelivr.net/gh/welpo/iine@main/iine.mini.js"></script>
{%- endif -%}

View file

@ -0,0 +1,35 @@
{#- Collect all terms. -#}
{#- We don't use `get_taxonomy` so users aren't forced to use 'tags' -#}
{% set all_terms = [] %}
{% for page in show_pages %}
{% if page.taxonomies %}
{% for tax_name, terms in page.taxonomies %}
{% for term in terms %}
{% set_global all_terms = all_terms | concat(with=term) %}
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
{#- Display unique terms -#}
{% set unique_terms = all_terms | unique | sort %}
{%- if unique_terms | length >= 2 -%}
<ul class="filter-controls" role="group" aria-label="{{ macros_translate::translate(key='project_filters', default='Project filters', language_strings=language_strings) }}">
<li class="taxonomy-item no-hover-padding">
<a id="all-projects-filter" class="no-hover-padding active"
href="{{ get_url(path="projects", lang=lang) }}"
data-filter="all">
{{- macros_translate::translate(key="all_projects", default="All projects", language_strings=language_strings) -}}
</a>
</li>
{% for term in unique_terms %}
<li class="taxonomy-item no-hover-padding">
<a class="no-hover-padding"
href="{{ get_taxonomy_url(kind="tags", name=term, lang=lang) }}"
data-filter="{{ term | lower }}">{{ term }}</a>
</li>
{% endfor %}
</ul>
{#- Load the script -#}
<script src="{{ get_url(path='js/filterCards.min.js', trailing_slash=false, cachebust=true) | safe }}" defer></script>
{% endif %}

View file

@ -0,0 +1,127 @@
{%- set separator = config.extra.separator | default(value="•") -%}
{%- 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) -%}
{#- Feed icon -#}
{%- set feed_url = feed_utils::get_feed_url() -%}
{%- set should_show_feed = feed_utils::should_show_footer_feed_icon() == "true" -%}
{%- set should_show_footer_icons = should_show_feed or config.extra.socials or config.extra.email -%}
<footer>
<section>
<nav class="socials nav-navs">
{%- if should_show_footer_icons -%}
<ul>
{%- if should_show_feed -%}
<li>
<a class="nav-links no-hover-padding social" rel="{{ rel_attributes }}" {{ blank_target }} href="{{ get_url(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>
</li>
{%- endif -%}
{# Mail icon #}
{%- if config.extra.email -%}
{%- set email_already_encoded = (config.extra.email is not containing("@")) -%}
{%- set email_needs_decoding = email_already_encoded or config.extra.encode_plaintext_email -%}
{%- if email_already_encoded -%}
{%- set encoded_email = config.extra.email -%}
{# Verify the pre-encoded e-mail is valid (i.e. contains an '@') #}
{%- set decoded_email = encoded_email | base64_decode -%}
{%- if '@' not in decoded_email -%}
{{ throw(message="ERROR: The provided e-mail appears to be base64-encoded, but does not decode to a valid e-mail address.")}}
{%- endif -%}
{%- elif config.extra.encode_plaintext_email -%}
{%- set encoded_email = config.extra.email | base64_encode -%}
{%- endif -%}
<li class="{% if email_needs_decoding %}js{% endif %}">
{%- if email_needs_decoding -%}
<a class="nav-links no-hover-padding social" href="#" data-encoded-email="{{ encoded_email | safe }}">
{%- else -%}
<a class="nav-links no-hover-padding social" href="mailto:{{ config.extra.email | safe }}">
{%- endif -%}
<img loading="lazy" alt="email" title="email" src="{{ get_url(path='social_icons/email.svg') }}">
</a>
</li>
{%- endif -%}
{%- if config.extra.socials %}
{% for social in config.extra.socials %}
<li>
<a class="nav-links no-hover-padding social" rel="{{ rel_attributes }} me" {{ blank_target }} href="{{ social.url | safe }}">
<img loading="lazy" alt="{{ social.name }}" title="{{ social.name }}" src="{{ get_url(path='social_icons/' ~ social.icon ~ '.svg') }}">
</a>
</li>
{% endfor %}
{% endif %}
</ul>
{% endif %}
</nav>
{# Footer menu #}
<nav class="nav-navs">
{%- if config.extra.footer_menu %}
<small>
<ul>
{% for menu in config.extra.footer_menu %}
<li>
{%- set trailing_slash = menu.trailing_slash | default(value=true) -%}
{%- if menu.url == "sitemap.xml" -%}
{%- set url = get_url(path=menu.url, trailing_slash=trailing_slash) -%}
{%- elif menu.url is starting_with("http") -%}
{%- if menu.trailing_slash -%}
{%- set url = menu.url ~ "/" -%}
{%- else -%}
{%- set url = menu.url -%}
{%- endif -%}
{%- else -%}
{%- set url = get_url(path=menu.url, lang=lang, trailing_slash=trailing_slash) -%}
{%- endif -%}
<a class="nav-links no-hover-padding" href="{{ url }}">
{{ macros_translate::translate(key=menu.name, default=menu.name, language_strings=language_strings) }}
</a>
</li>
{% endfor %}
</ul>
</small>
{% endif %}
</nav>
<div class="credits">
<small>
{% include "partials/copyright.html" %}
{# Shows "Powered by Zola & tabi" notice #}
{{ macros_translate::translate(key="powered_by", default="Powered by", language_strings=language_strings) }}
<a rel="{{ rel_attributes }}" {{ blank_target }} href="https://www.getzola.org">Zola</a>
{{ macros_translate::translate(key="and", default="&", language_strings=language_strings) }}
<a rel="{{ rel_attributes }}" {{ blank_target }} href="https://github.com/welpo/tabi">tabi</a>
{# Shows link to remote repository #}
{%- if config.extra.remote_repository_url and config.extra.show_remote_source | default(value=true) -%}
{{ separator }}
<a rel="{{ rel_attributes }}" {{ blank_target }} href="{{ config.extra.remote_repository_url }}">
{{ macros_translate::translate(key="site_source", default="Site source", language_strings=language_strings) }}
</a>
{%- endif -%}
</small>
</div>
</section>
{# Load the decoding script if email is encoded #}
{%- if email_needs_decoding -%}
<script src="{{ get_url(path='js/decodeMail.min.js') }}" async></script>
{%- endif -%}
{# Modal structure for search #}
{%- if config.build_search_index -%}
{% include "partials/search_modal.html" %}
{%- endif -%}
</footer>

View file

@ -0,0 +1,75 @@
{%- set hcard = config.extra.hcard -%}
{% set full_name = config.author %}
{% if hcard.full_name %}
{% set full_name = hcard.full_name %}
{% endif %}
{%- set homepage = config.base_url -%}
{% if hcard.homepage %}
{%- set homepage = hcard.homepage -%}
{% endif %}
{% if hcard.enable %}
<div class="h-card hidden">
<div>
{%- if hcard.avatar -%}
<img
class="u-photo"
src="{{ get_url(path=hcard.avatar, cachebust=true) }}"
width="200"
height="200"
alt="{{ full_name }}"
/>
{%- endif -%}
<span class="p-name" rel="me">{{ full_name }}</span>
{% if hcard.p_nickname %}
( <span class="p-nickname">{{ hcard.p_nickname }}</span> )
{% endif %}
</div>
{% if hcard.biography %}
<p class="p-note">{{ hcard.biography }}</p>
{% endif %}
{# links #}
<div>
{%- if hcard.with_mail and config.extra.email and not config.extra.encode_plaintext_email -%}
<span>
<a class="u-email" href="mailto:{{ config.extra.email | safe }}">email</a>
</span> -
{%- endif -%}
<span>
<a class="u-url u-id" href="{{ homepage }}">homepage</a>
</span> -
{%- if hcard.with_social_links and config.extra.socials %}
{% for social in config.extra.socials %}
<span>
<a class="p-url" rel="me" href="{{ social.url | safe }}">{{ social.name }}</a>
</span> -
{% endfor %}
{% endif %}
</div>
{# additional properties #}
{% set dl_started = false %}
{% for key, value in hcard %}
{% if key not in ['enable', 'with_mail', 'with_social_links', 'homepage', 'full_name', 'avatar', 'biography', 'p_nickname'] %}
{% if not dl_started %}
<dl>
{% set_global dl_started = true %}
{% endif %}
<dt>{{ key | replace(from="p_", to="") | replace(from="u_", to="") | replace(from="dt_", to="") | replace(from="_", to=" ") | capitalize }}</dt>
<dd class="{{ key | replace(from="_", to="-") }}">{{ value }}</dd>
{% endif %}
{% endfor %}
{% if dl_started %}
</dl>
{% endif %}
</div>
{% endif %}

View file

@ -0,0 +1,26 @@
{%- set hcard = config.extra.hcard -%}
{%- set full_name = config.author -%}
{%- if hcard.full_name -%}
{%- set full_name = hcard.full_name -%}
{%- endif -%}
{%- set homepage = config.base_url -%}
{%- if hcard.homepage -%}
{%- set homepage = hcard.homepage -%}
{%- endif -%}
{%- set icon_attr = "" -%}
{%- if hcard.avatar -%}
{%- set icon_attr = "author-icon" -%}
{%- endif -%}
<span class="hidden p-author h-card">
<a rel="author" href="{{ homepage }}" class="u-url {{ icon_attr }}" title="{{ full_name }}">
{%- if hcard.avatar -%}
<img class="u-photo" src="{{ get_url(path=hcard.avatar, cachebust=true) }}" alt="{{ full_name }}" />
{%- else -%}
{{ full_name }}
{%- endif -%}
</a>
</span>

View file

@ -0,0 +1,182 @@
<head>
<meta charset="UTF-8">
{%- if macros_settings::evaluate_setting_priority(setting="enable_csp", page=page | default(value=""), section=section | default(value=""), default_global_value="true") == "true" -%}
{%- include "partials/content_security_policy.html" -%}
{%- endif -%}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="base" content="{{ config.base_url | safe }}">
{# Site title #}
<title>{%- include "partials/title.html" -%}</title>
{# Favicon #}
{% if config.extra.favicon %}
<link rel="icon" type="image/png" href="{{ get_url(path=config.extra.favicon) }}"/>
{% endif %}
{% if config.extra.favicon_emoji %}
<link rel=icon href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y="50%" x="50%" dominant-baseline="central" text-anchor="middle" font-size="88">{{ config.extra.favicon_emoji }}</text></svg>'>
{% endif %}
{# Feeds #}
{% if config.generate_feeds | default(value=config.generate_feed) %}
{% if config.feed_filenames %}
{# Zola 0.19 and newer #}
{% for feed in config.feed_filenames %}
{% if feed == "atom.xml" %}
<link rel="alternate" type="application/atom+xml" title="{{ config.title | safe }} - Atom Feed" href="{{ get_url(path=feed, trailing_slash=false) | safe }}">
{% elif feed == "rss.xml" %}
<link rel="alternate" type="application/rss+xml" title="{{ config.title | safe }} - RSS Feed" href="{{ get_url(path=feed, trailing_slash=false) | safe }}">
{% else %}
<link rel="alternate" href="{{ get_url(path=feed, trailing_slash=false) | safe }}">
{% endif %}
{% endfor %}
{% else %}
{# Older Zola versions #}
{% set feed_url = config.feed_filename | default(value="atom.xml") %}
<link rel="alternate" type="application/atom+xml" title="{{ config.title | safe }}" href="{{ get_url(path=feed_url, trailing_slash=false) | safe }}">
{% endif %}
{% endif %}
{# CSS #}
{# Load subset of glyphs for header. Avoids flashing issue in Firefox #}
{% if config.extra.enable_subset %}
{% if config.extra.custom_subset == true %}
<link rel="stylesheet" href="{{ get_url(path="custom_subset.css" , cachebust=true) }}">
{% elif lang == 'en' %}
<link rel="stylesheet" href="{{ get_url(path="inter_subset_en.css", cachebust=true ) }}">
{% elif lang == 'es' %}
<link rel="stylesheet" href="{{ get_url(path="inter_subset_es.css", cachebust=true ) }}">
{% endif %}
{% endif %}
{# Define array of CSS files to load. main.css is always loaded. #}
{%- set stylesheets = [ "main.css" ] -%}
{# Load extra CSS files from config.toml #}
{%- if config.extra.stylesheets -%}
{%- set stylesheets = stylesheets | concat(with=config.extra.stylesheets) -%}
{%- endif -%}
{# Load extra CSS files from page metadata #}
{%- if page.extra.stylesheets -%}
{%- set stylesheets = stylesheets | concat(with=page.extra.stylesheets) -%}
{%- endif -%}
{# Load extra CSS for custom skin #}
{%- if config.extra.skin -%}
{%- set stylesheets = stylesheets | concat(with='skins/' ~ config.extra.skin ~ '.css') -%}
{%- endif -%}
{# Load all stylesheets #}
{%- for stylesheet in stylesheets %}
<link rel="stylesheet" href="{{ get_url(path=stylesheet, cachebust=true) | safe }}" />
{%- endfor %}
<meta name="color-scheme" content="{%- if config.extra.theme_switcher -%}light dark{%- elif config.extra.default_theme -%}{{config.extra.default_theme}}{%- else -%}light{%- endif -%}" />
{%- if config.extra.browser_theme_color and config.extra.browser_theme_color is iterable -%}
{# Handle array values: theme_color[0] for light mode, theme_color[1] for dark mode #}
<meta name="theme-color" media="(prefers-color-scheme: light)" content="{{ config.extra.browser_theme_color[0] }}" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="{{ config.extra.browser_theme_color[1] }}" />
{%- elif config.extra.browser_theme_color -%}
{# Handle single value #}
<meta name="theme-color" content="{{ config.extra.browser_theme_color }}" />
{%- endif -%}
{%- if page.description %}
<meta name="description" content="{{ page.description }}" />
<meta property="og:description" content="{{ page.description }}" />
{%- elif section.description %}
<meta name="description" content="{{ section.description }}" />
<meta property="og:description" content="{{ section.description }}" />
{%- elif page.summary %}
<meta name="description" content="{{ page.summary | striptags | trim_end_matches(pat=".") | safe }}" />
<meta property="og:description" content="{{ page.summary | striptags | trim_end_matches(pat=".") | safe }}" />
{%- else %}
<meta name="description" content="{{ config.description }}" />
<meta property="og:description" content="{{ config.description }}" />
{%- endif %}
{% if is_404 %}
<meta name="robots" content="noindex, follow" />
{% endif %}
<meta property="og:title" content="{{ page.title | default(value=config.title) | safe }}" />
<meta property="og:type" content="article" />
{# Image for social media sharing #}
{%- include "partials/social_media_images.html" -%}
{# Add og:locale and hreflang tags for multilingual sites #}
{%- if config.languages | length > 0 and current_url %}
{%- include "partials/multilingual_tags.html" -%}
{%- else -%}
<meta property="og:locale" content="{{ macros_translate::translate(key="date_locale", default="en_GB", language_strings=language_strings) }}" />
{%- endif %}
{# Set canonical URL #}
{%- if current_url -%}
{%- if page.extra.canonical_url or section.extra.canonical_url -%}
{%- set canonical_url = page.extra.canonical_url | default(value=section.extra.canonical_url) -%}
{%- elif config.extra.base_canonical_url -%}
{%- set canonical_url = current_url | replace(from=config.base_url, to=config.extra.base_canonical_url) -%}
{%- endif -%}
{%- endif -%}
{# Add canonical URL, if set #}
{%- if canonical_url -%}
<link rel="canonical" href="{{ canonical_url }}" />
<meta property="og:url" content="{{ canonical_url }}" />
{%- elif current_url -%}
<meta property="og:url" content="{{ current_url }}" />
{%- endif -%}
<meta property="og:site_name" content="{{ config.title }}">
{%- if config.extra.theme_switcher and config.extra.theme_switcher == true -%}
{# If JavaScript is disabled, hide the button. #}
<noscript><link rel="stylesheet" href="{{ get_url(path='no_js.css') | safe }}"/></noscript>
<script type="text/javascript" src="{{ get_url(path='js/initializeTheme.min.js') | safe }}"></script>
<script defer src="{{ get_url(path='js/themeSwitcher.min.js', trailing_slash=false) | safe }}"></script>
{%- endif -%}
{%- if config.extra.analytics.service -%}
{%- include "partials/analytics.html" -%}
{%- endif -%}
{# Socials #}
{%- if config.extra.fediverse_creator -%}
<meta name="fediverse:creator" content="@{{ config.extra.fediverse_creator["handle"] }}@{{ config.extra.fediverse_creator["domain"]}}" />
{%- endif -%}
{# Search #}
{%- if config.build_search_index -%}
{%- if config.search.index_format -%}
{%- set search_index_format = config.search.index_format -%}
{%- elif config.extra.index_format -%}
{# Necessary to support Zola 0.17.X, as it doesn't have access to config.search.index_format #}
{# See: https://github.com/getzola/zola/issues/2165 #}
{%- set search_index_format = config.extra.index_format -%}
{%- else -%}
{%- set search_index_format = "elasticlunr_json" -%}
{%- endif -%}
{%- if search_index_format == "elasticlunr_javascript" -%}
<script defer src="{{ get_url(path='search_index.' ~ lang ~ '.js', cachebust=true) | safe }}"></script>
{%- endif -%}
{# Main search script #}
<script defer src="{{ get_url(path='js/searchElasticlunr.min.js', cachebust=true) | safe }}"></script>
{# Support correct stemming and stop word filtering in non-English search #}
{%- if lang != "en" -%}
<script defer src="{{ get_url(path='js/lunr/lunrStemmerSupport.min.js') | safe }}"></script>
<script defer src="{{ get_url(path='js/lunr/lunr.' ~ lang ~ '.min.js') | safe }}"></script>
{%- endif -%}
{%- endif -%}
{# Users can optionally provide this template to add content to the head element. #}
{% include "tabi/extend_head.html" ignore missing %}
</head>

View file

@ -0,0 +1,26 @@
{%- set relative_path = page.relative_path -%}
{%- set repository_url = config.extra.remote_repository_url | trim_end_matches(pat='/') -%}
{%- set branch = config.extra.remote_repository_branch | default(value="main") -%}
{%- set git_platform = config.extra.remote_repository_git_platform | default(value="auto") -%}
{# Auto-detect the git platform based on the URL#}
{%- if git_platform == "auto" %}
{%- if repository_url is containing("github.") -%}
{%- set git_platform = "github" -%}
{%- elif repository_url is containing("gitlab.") -%}
{%- set git_platform = "gitlab" -%}
{%- elif repository_url is matching("(gitea\.|codeberg\.)") -%}
{%- set git_platform = "gitea" -%}
{%- endif -%}
{%- endif -%}
{# Generate the commit history URL based on the git platform #}
{%- if git_platform == "github" -%}
{{ repository_url ~ '/commits/' ~ branch ~ '/content/' }}{{ relative_path | urlencode }}
{%- elif git_platform == "gitlab" -%}
{{ repository_url ~ '/-/commits/' ~ branch ~ '/content/' }}{{ relative_path | urlencode }}
{%- elif git_platform in ["gitea", "codeberg"] -%}
{{ repository_url ~ '/commits/branch/' ~ branch ~ '/content/' }}{{ relative_path | urlencode }}
{%- else -%}
{{ throw(message="ERROR: Unknown, unsupported, or unspecified git platform. If you're using a custom domain, please specify the 'git_platform' in the config. If you think this is a bug, please report it: https://github.com/welpo/tabi/issues/new?assignees=&labels=bug&template=bug_report.md&title=Unsupported%20Git%20Platform%20Detected") }}
{%- endif -%}

View file

@ -0,0 +1,20 @@
{%- set header = section.extra.header-%}
<div id="banner-container-home">
<div id="home-banner-text">
<h1 id="home-banner-header">{{ header.title }}</h1>
<section id="banner-home-subtitle">
{{ section.content | safe }}
</section>
</div>
{%- if header.img -%}
{%- if header.img is containing("$BASE_URL") -%}
{# Conversion no longer supported in favour of proper path. #}
{{ throw(message="ERROR: The image path for the header should not contain '$BASE_URL'. Please remove it and use the proper image path.") }}
{%- else -%}
{%- set image_path = get_url(path=header.img, trailing_slash=false) | safe -%}
{%- endif -%}
<div id="image-container-home">
<img alt="{{ header.img_alt | default(value="the owner") }}" id="banner-home-img" src="{{ image_path }}" />
</div>
{%- endif -%}
</div>

View file

@ -0,0 +1,33 @@
{% import "macros/settings.html" as macros_settings %}
{%- set button_icon = button_icon | default(value=macros_settings::evaluate_setting_priority(setting="iine_icon", page=page | default(value=""), section=section | default(value=""), default_global_value="heart")) -%}
{%- if config.extra.iine_unified_languages and lang != config.default_language -%}
{%- set unified_slug = page.path | replace(from='/' ~ lang ~ '/', to='/') -%}
{%- set slug = slug | default(value=unified_slug) -%}
{%- else -%}
{%- set slug = slug | default(value=page.path) -%}
{%- endif -%}
{%- if label -%}
{%- set final_label = label -%}
{%- elif language_strings -%}
{%- set final_label = macros_translate::translate(key="like_this_post", default="Like this post", language_strings=language_strings) -%}
{%- else -%}
{%- set final_label = "Like this post" -%}
{%- endif -%}
{%- if button_icon == "heart" -%}
{%- set icon_display = "♥️" -%}
{%- elif button_icon == "thumbs_up" -%}
{%- set icon_display = "👍" -%}
{%- elif button_icon == "upvote" -%}
{%- set icon_display = "⬆️" -%}
{%- else -%}
{%- set icon_display = button_icon -%}
{%- endif -%}
<form method="post" action="https://vhiweeypifbwacashxjz.supabase.co/rest/v1/rpc/increment_hits?apikey=sb_publishable_EoB7MFJhCmb6PiAk-GPJ4w_PGhQ44Ru" class="iine-form">
<input type="hidden" name="page_slug" value="{%- if slug -%}{{ slug }}{%- else -%}{{ current_url | default(value=page.path) }}{%- endif -%}">
<button class="iine-button" type="submit"
{%- if slug %} data-slug="{{ slug }}"{% endif %}
{%- if button_icon %} data-icon="{{ button_icon }}"{% endif %}
aria-label="{{ final_label }}" title="{{ final_label }}">{{ icon_display }}</button>
</form>

View file

@ -0,0 +1,38 @@
<li class="language-switcher">
<details class="dropdown">
<summary role="button" aria-haspopup="true" title="{{ macros_translate::translate(key="language_selection", default="Language selection", language_strings=language_strings) }}" aria-label="{{ macros_translate::translate(key="language_selection", default="Language selection", language_strings=language_strings) }}">
<div class="language-switcher-icon"></div>
</summary>
<div class="dropdown-content" role="menu">
{#- Display the current language first in the dropdown -#}
{{ macros_translate::translate(key="language_name", default=lang, language_strings=language_strings) }}
{#- Loop through all the available languages in the config -#}
{%- for lcode, ldetails in config.languages -%}
{#- Skip the current language to avoid linking to the current page -#}
{%- if lang == lcode -%}
{%- continue -%}
{%- endif -%}
{#- Dynamically load the language strings for each language -#}
{%- set other_language_strings = load_data(path="i18n/" ~ lcode ~ ".toml", required=false) -%}
{%- if not other_language_strings -%}
{%- set other_language_strings = load_data(path="themes/tabi/i18n/" ~ lcode ~ ".toml", required=false) -%}
{%- endif -%}
{#- Use the loaded language strings to get the language name -#}
{% set language_name = macros_translate::translate(key="language_name", default=lcode,
language_strings=other_language_strings) %}
{#- Check if the language code matches the default language -#}
{%- if lcode == config.default_language -%}
{#- If it does, link to the root path (no language code in URL) -#}
<a role="menuitem" lang="{{ lcode }}" aria-label="{{ language_name }}" href="{{ current_url | replace(from='/' ~ lang ~ '/', to = '/') }}">{{ language_name }}</a>
{#- Check if the current language is the default language -#}
{#- If it is, append the language code to the base URL -#}
{%- elif lang == config.default_language -%}
<a role="menuitem" lang="{{ lcode }}" aria-label="{{ language_name }}" href="{{ config.base_url }}/{{ lcode }}{{ current_path | default(value="/") | safe }}">{{ language_name }}</a>
{%- else -%}
{#- If it's not, replace the current language code in the URL with the new one -#}
<a role="menuitem" lang="{{ lcode }}" aria-label="{{ language_name }}" href="{{ current_url | replace(from='/' ~ lang ~ '/', to='/' ~ lcode ~ '/') }}">{{ language_name }}</a>
{%- endif -%}
{%- endfor -%}
</div>
</details>
</li>

View file

@ -0,0 +1,61 @@
{%- if paginator or extra_section -%}
<div id="posts-list">
<div>
{{ macros_page_header::page_header(title=section.title) }}
</div>
{# Check if both paginate_by and section_path are set #}
{%- set both_settings_set = paginator and extra_section -%}
{%- set paginator_has_no_pages = paginator and paginator.pages | length == 0 -%}
{%- set extra_section_has_pages = extra_section and extra_section.pages | length > 0 -%}
{# Display warning if both settings are set #}
{%- if both_settings_set and paginator_has_no_pages and extra_section_has_pages -%}
<div class="admonition warning">
<div class="admonition-icon admonition-icon-warning"></div>
<div class="admonition-content">
<strong class="admonition-title">WARNING: Conflicting Configuration</strong>
<p>
No posts are displayed due to conflicting settings in your <code>_index.md</code>:
</p>
<ul>
<li><code>paginate_by</code> is set, but there are no posts to paginate in the current section.</li>
<li><code>section_path</code> is set, and posts are available in that section.</li>
</ul>
<p>
<strong>Solution:</strong> Remove <code>paginate_by</code> from your <code>_index.md</code>.
To limit the number of displayed posts, use <code>max_posts</code> in the <code>[extra]</code> section instead.
</p>
</div>
</div>
{%- endif -%}
{# Get all posts for pinning if we're in root section with pagination #}
{%- if paginator and is_root_section -%}
{%- set root_section = get_section(path="_index.md") -%}
{%- set all_posts = root_section.pages -%}
{%- set pages = paginator.pages -%}
{%- elif paginator -%}
{%- set all_posts = paginator.pages -%}
{%- set pages = paginator.pages -%}
{%- else -%}
{%- set all_posts = extra_section.pages -%}
{%- set pages = extra_section.pages -%}
{%- endif -%}
{% set max_posts = section.extra.max_posts | default(value=999999) %}
{{ macros_list_posts::list_posts(
posts=pages,
all_posts=all_posts,
max=max_posts,
language_strings=language_strings,
section_path=extra_section.path | default(value="blog"),
pinned_first=is_root_section,
current_page=paginator.current_index | default(value=1)
) }}
</div>
{% if paginator and paginator.pages | length > 0 %}
{%- include "partials/paginate.html" -%}
{% endif %}
{%- endif -%}

View file

@ -0,0 +1,16 @@
{% if section.extra.projects_path %}
{%- set projects_section = get_section(path=section.extra.projects_path) -%}
{%- if projects_section -%}
<div id="featured-projects" class="list">
{{ macros_page_header::page_header(title=macros_translate::translate(key="featured_projects", default="Featured projects", language_strings=language_strings)) }}
</div>
{%- set show_pages = projects_section.pages -%}
{%- set max_projects = section.extra.max_projects | default(value=3) -%}
{%- include "partials/cards_pages.html" -%}
{%- endif -%}
{%- if show_pages | length > max_projects -%}
<div class="all-posts" id="all-projects">
<a href="{{ get_url(path=projects_section.path, lang=lang) }}/">{{ macros_translate::translate(key="all_projects", default="All projects", language_strings=language_strings) }}&nbsp;<span class="arrow"></span></a>
</div>
{%- endif -%}
{% endif %}

View file

@ -0,0 +1,29 @@
{%- if section.translations -%}
{%- set current_translations = section.translations -%}
{%- elif page.translations -%}
{%- set current_translations = page.translations -%}
{%- endif -%}
{%- if current_translations -%}
{%- for translation in current_translations -%}
{%- set lcode = translation.lang | default(value = config.default_language) -%}
{#- Dynamically load the language strings for each language -#}
{%- set other_language_strings = load_data(path="i18n/" ~ lcode ~ ".toml", required=false) -%}
{%- if not other_language_strings -%}
{%- set other_language_strings = load_data(path="themes/tabi/i18n/" ~ lcode ~ ".toml", required=false) -%}
{%- endif -%}
<meta property="og:locale:alternate" content="{{ macros_translate::translate(key="date_locale", default="en_GB", language_strings=other_language_strings) }}" />
{# Construct href for hreflang #}
{%- set href = translation.permalink -%}
{%- if lcode == config.default_language -%}
{%- set href = href | replace(from='/' ~ lang ~ '/', to = '/') -%}
{%- else -%}
{%- set href = href | replace(from='/' ~ lang ~ '/', to='/' ~ lcode ~ '/') -%}
{%- endif -%}
<link rel="alternate" hreflang="{{ lcode }}" href="{{ href | safe }}" />
{%- endfor -%}
{%- endif -%}

View file

@ -0,0 +1,60 @@
<header>
<nav class="navbar">
<div class="nav-title">
<a class="home-title" href="{{ get_url(path='/', lang=lang, trailing_slash=(lang == config.default_language)) }}">{{ config.title }}</a>
</div>
{%- if config.extra.menu %}
<div class="nav-navs">
<ul>
{%- if config.extra.menu %}
{% for menu in config.extra.menu %}
<li>
{% set trailing_slash = menu.trailing_slash | default(value=true) %}
{%- if menu.url is starting_with("http") -%}
{%- if trailing_slash -%}
<a class="nav-links no-hover-padding" href="{{ menu.url }}/">
{%- else -%}
<a class="nav-links no-hover-padding" href="{{ menu.url }}">
{%- endif -%}
{%- else -%}
<a class="nav-links no-hover-padding" href="{{ get_url(path=menu.url, lang=lang, trailing_slash=trailing_slash) }}">
{%- endif -%}
{{ macros_translate::translate(key=menu.name, default=menu.name, language_strings=language_strings) }}
</a>
</li>
{% endfor %}
{%- endif -%}
{#- Wrap the icons to keep them all together -#}
<li class="menu-icons-container">
<ul class="menu-icons-group">
{# Search #}
{%- if config.build_search_index %}
{%- set search_icon_title = macros_translate::translate(key='search_icon_title', default='Press $SHORTCUT to open search', language_strings=language_strings) -%}
<li class="js menu-icon">
<div role="button" tabindex="0" id="search-button" class="search-icon interactive-icon" title="{{ search_icon_title }}" aria-label="{{ search_icon_title }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
</svg>
</div>
</li>
{%- endif %}
{# Language switcher #}
{# Displayed only if more than one language is available #}
{%- if config.languages | length > 0 %}
{% include "partials/language_switcher.html" %}
{%- endif %}
{# Theme switcher #}
{%- if config.extra.theme_switcher and config.extra.theme_switcher == true -%}
{%- include "partials/theme_switcher.html" -%}
{%- endif -%}
</ul>
</li>
</ul>
</div>
{% endif %}
</nav>
</header>

View file

@ -0,0 +1,27 @@
{% if paginator and paginator.number_pagers > 1 %}
<ul class="pagination">
{% if paginator.previous %}
<li class="page-item page-prev">
<a href="{{ paginator.previous }}" class="page-link" aria-label="{{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }}"><span class="arrow"></span> {{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }}</a>
</li>
{% else %}
<li class="page-item page-prev">
<span class="page-link disabled" aria-disabled="true" aria-label="{{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }} (disabled)"><span class="arrow"></span> {{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }}</span>
</li>
{% endif %}
<li class="page-item page-numbers">
{{ paginator.current_index }} {{ macros_translate::translate(key="of", default="of", language_strings=language_strings) }} {{ paginator.number_pagers }}
</li>
{% if paginator.next %}
<li class="page-item page-next">
<a href="{{ paginator.next }}" class="page-link" aria-label="{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }}">{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }} <span class="arrow"></span></a>
</li>
{% else %}
<li class="page-item page-next">
<span class="page-link disabled" aria-disabled="true" aria-label="{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }} (disabled)">{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }} <span class="arrow"></span></span>
</li>
{% endif %}
</ul>
{% endif %}

View file

@ -0,0 +1,31 @@
<div id="searchModal" class="search-modal js" role="dialog" aria-labelledby="modalTitle">
<h1 id="modalTitle" class="visually-hidden">{{ macros_translate::translate(key='search', default='Search', language_strings=language_strings) }}</h1>
<div id="modal-content">
<div id="searchBar">
<div class="search-icon" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
</svg>
</div>
<input id="searchInput" role="combobox" autocomplete="off" spellcheck="false" aria-expanded="false" aria-controls="results-container" placeholder="{{ macros_translate::translate(key='search', default='Search', language_strings=language_strings) }}…"/>
<div id="clear-search" class="close-icon interactive-icon" tabindex="0" role="button" title="{{ macros_translate::translate(key='clear_search', default='Clear search', language_strings=language_strings) }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/>
</svg>
</div>
</div>
<div id="results-container">
<div id="results-info">
{#- Add the strings here so JavaScript can grab them -#}
{#- These are used in all languages -#}
<span id="zero_results"> {{ macros_translate::translate(key='results', number=0, default='No results', language_strings=language_strings, replace=false) }}</span>
<span id="one_results"> {{ macros_translate::translate(key='results', number=1, default='1 result', language_strings=language_strings, replace=false) }}</span>
<span id="many_results"> {{ macros_translate::translate(key='results', number=11, default='$NUMBER results', language_strings=language_strings, replace=false) }}</span>
{#- Strings for specific languages -#}
<span id="two_results"> {{ macros_translate::translate(key='results', number=2, default='$NUMBER results', language_strings=language_strings, replace=false) }}</span>
<span id="few_results"> {{ macros_translate::translate(key='results', number=2, default='$NUMBER results', language_strings=language_strings, replace=false) }}</span>
</div>
<div id="results" role="listbox"></div>
</div>
</div>
</div>

View file

@ -0,0 +1,50 @@
{%- set social_media_card = macros_settings::evaluate_setting_priority(setting="social_media_card", page=page | default(value=""), section=section | default(value=""), default_global_value="") -%}
{% if social_media_card %}
{# Get base path from page/section #}
{% set base_path = "" %}
{% if section and section.path %}
{% set base_path = section.path | trim_end_matches(pat="/_index.md") %}
{% if base_path and not social_media_card is starting_with("/") %}
{% set base_path = base_path ~ "/" %}
{% endif %}
{% else %}
{% set base_path = page.colocated_path | default(value="") %}
{% endif %}
{% set current_path = base_path ~ social_media_card | trim_start_matches(pat="/") %}
{# Try parent path by removing the last directory component #}
{% set parent_path = base_path | split(pat="/") | slice(end=-2) | join(sep="/") %}
{% if parent_path and not social_media_card is starting_with("/") %}
{% set parent_path = parent_path ~ "/" %}
{% endif %}
{% set parent_relative_path = parent_path ~ social_media_card | trim_start_matches(pat="/") %}
{# Check all possible locations #}
{%- set current_meta = get_image_metadata(path=current_path, allow_missing=true) -%}
{%- set parent_meta = get_image_metadata(path=parent_relative_path, allow_missing=true) -%}
{%- set absolute_meta = get_image_metadata(path=social_media_card, allow_missing=true) -%}
{% if current_meta %}
{% set final_path = current_path %}
{% set meta = current_meta %}
{% elif parent_meta %}
{% set final_path = parent_relative_path %}
{% set meta = parent_meta %}
{% elif absolute_meta %}
{% set final_path = social_media_card %}
{% set meta = absolute_meta %}
{% else %}
{{ throw(message="Could not find social media card image. Tried:
1. Current page path: '" ~ current_path ~ "'
2. Parent page path: '" ~ parent_relative_path ~ "'
3. Absolute path: '" ~ social_media_card ~ "'
Please ensure the file exists at one of these locations.") }}
{% endif %}
<meta property="og:image" content="{{ get_url(path=final_path, cachebust=true) }}" />
<meta property="og:image:width" content="{{ meta.width }}" />
<meta property="og:image:height" content="{{ meta.height }}" />
<meta name="twitter:image" content="{{ get_url(path=final_path, cachebust=true) }}" />
<meta name="twitter:card" content="summary_large_image" />
{% endif %}

View file

@ -0,0 +1,31 @@
<li class="theme-switcher-wrapper js">
{#- Create the localised strings for the title and aria-label attributes -#}
{%- set toggle_str = macros_translate::translate(key='toggle_mode', default='Toggle $MODE mode', language_strings=language_strings) -%}
{%- set dark_str = macros_translate::translate(key='dark', default='dark', language_strings=language_strings) -%}
{%- set light_str = macros_translate::translate(key='light', default='light', language_strings=language_strings) -%}
{%- set combined_mode_str = dark_str ~ "/" ~ light_str -%}
{%- set title_label = toggle_str | replace(from="$MODE", to=combined_mode_str) -%}
{%- set aria_label = toggle_str | replace(from="$MODE", to=dark_str) -%}
<div
title="{{ title_label }}"
class="theme-switcher"
tabindex="0"
role="button"
aria-label="{{ aria_label }}"
aria-pressed="false">
</div>
{%- set reset_str = macros_translate::translate(key='reset_mode', default='Reset mode to default', language_strings=language_strings) -%}
<div
title="{{ reset_str }}"
class="theme-resetter arrow"
tabindex="0"
role="button"
aria-hidden="true"
aria-label="{{ reset_str }}">
</div>
</li>

View file

@ -0,0 +1,40 @@
{#- Setup -#}
{% if not config.title %}
{{ throw(message="ERROR: No `title` set in `config.toml`. tabi requires a title to function.") }}
{% endif %}
{%- set prefix = config.title | safe -%}
{%- set custom_separator = config.extra.separator | default(value="•") -%}
{%- set separator = " " ~ custom_separator ~ " " -%}
{#- Get the base path for the current language -#}
{%- if lang != config.default_language %}
{%- set base_path = "/" ~ lang ~ "/" %}
{%- else -%}
{%- set base_path = "/" %}
{%- endif %}
{%- if current_path and current_path == base_path -%}
{%- set suffix = "" -%}
{%- set separator = "" -%}
{% elif title %}
{%- set suffix = title -%}
{% elif section.title -%}
{%- set suffix = section.title -%}
{% elif page.title %}
{%- set suffix = page.title -%}
{% elif term.name %}
{#- Individual tags -#}
{%- set suffix = term.name -%}
{% elif taxonomy.name %}
{#- List of tags -#}
{%- set suffix = macros_translate::translate(key=taxonomy.name, language_strings=language_strings) | capitalize -%}
{% else %}
{%- set suffix = "404" %}
{%- endif -%}
{#- Return the final concatenated string -#}
{%- if config.extra.invert_title_order -%}
{{- suffix ~ separator ~ prefix -}}
{%- else -%}
{{- prefix ~ separator ~ suffix -}}
{%- endif -%}

View file

@ -0,0 +1,51 @@
{# Incorporate webmention.io links and script into the page head.
1. Provide the link to the webmention data in the at webmention.io.
2. Link to the stylesheet for styling webmentions on a page.
3. Add and configure the javascript to fetch and display the webmentions collected at webmention.io. #}
<link rel="webmention" href="https://webmention.io/{{ config.extra.webmentions.domain }}/webmention" />
{# Calculate the configured data for the script, if any #}
{% set script_data = "" %}
{% if config.extra.webmentions.id %}
{% set script_data = script_data ~ "data-id=" ~ config.extra.webmentions.id %}
{% endif %}
{% if config.extra.webmentions.page_url %}
{% set script_data = script_data ~ " data-page-url=" ~ config.extra.webmentions.page_url %}
{% endif %}
{% if config.extra.webmentions.add_urls %}
{% set script_data = script_data ~ "data-add-urls=" ~ config.extra.webmentions.add_urls %}
{% endif %}
{% if config.extra.webmentions.wordcount %}
{% set script_data = script_data ~ " data-wordcount=" ~ config.extra.webmentions.wordcount %}
{% endif %}
{% if config.extra.webmentions.max_webmentions %}
{% set script_data = script_data ~ "data-max-webmentions=" ~ config.extra.webmentions.max_webmentions %}
{% endif %}
{% if config.extra.webmentions.prevent_spoofing %}
{% set script_data = script_data ~ "data-prevent-spoofing=" ~ config.extra.webmentions.prevent_spoofing %}
{% endif %}
{% if config.extra.webmentions.sort_by %}
{% set script_data = script_data ~ "data-sort-by=" ~ config.extra.webmentions.sort_by %}
{% endif %}
{% if config.extra.webmentions.sort_dir %}
{% set script_data = script_data ~ "data-sort-dir=" ~ config.extra.webmentions.sort_dir %}
{% endif %}
{% if config.extra.webmentions.comments_are_reactions %}
{% set script_data = script_data ~ " data-comments-are-reactions=" ~ config.extra.webmentions.comments_are_reactions %}
{% endif %}
<script async src="{{ get_url(path='js/webmention.min.js', trailing_slash=false, cachebust=true) | safe }}" {{ script_data }}>
</script>
<div class="webmentions-container" id="webmentions"></div>