forked from deepStateMirrors/tabi
✨ feat: add support for webmentions (#485)
Co-authored-by: Jeremiah Russell <47631109+jerusdp@users.noreply.github.com> Co-authored-by: Henri Bourcereau <henri.bourcereau@gmail.com> Co-authored-by: welpo <welpo@users.noreply.github.com>
This commit is contained in:
parent
84c67ab2b2
commit
c325267bd1
33 changed files with 907 additions and 31 deletions
|
@ -51,6 +51,7 @@ tabi has a perfect score on Google's Lighthouse audit:
|
|||
- [X] Perfect Lighthouse score (Performance, Accessibility, Best Practices and SEO).
|
||||
- [X] [Comprehensive multi-language support](https://welpo.github.io/tabi/blog/faq-languages/#how-does-tabi-handle-multilingual-support). Add as many languages as you wish.
|
||||
- [X] Support for [comments using giscus, utterances, Hyvor Talk, or Isso](https://welpo.github.io/tabi/blog/comments/).
|
||||
- [X] [Indieweb](https://indieweb.org/) ready with microformats, [hcard](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#representative-h-card) and [webmentions](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#webmentions) support.
|
||||
- [X] Code syntax highlighting with colours based on [Catppuccin](https://github.com/catppuccin/catppuccin) Frappé.
|
||||
- [X] [Mermaid support](https://welpo.github.io/tabi/blog/shortcodes/#mermaid-diagrams) to create diagrams and charts with text.
|
||||
- [X] [Local search](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#search) with an accessible, multi-lingual interface.
|
||||
|
|
44
config.toml
44
config.toml
|
@ -28,7 +28,7 @@ include_content = true
|
|||
# At which character to truncate the content to. Useful if you have a lot of pages and the index would
|
||||
# become too big to load on the site. Defaults to not being set.
|
||||
# truncate_content_length = 100
|
||||
# Wether to produce the search index as a javascript file or as a JSON file.
|
||||
# Whether to produce the search index as a javascript file or as a JSON file.
|
||||
# Accepted value "elasticlunr_javascript" or "elasticlunr_json".
|
||||
index_format = "elasticlunr_json"
|
||||
|
||||
|
@ -428,6 +428,48 @@ voting = true
|
|||
page_author_hashes = "" # hash (or list of hashes) of the author.
|
||||
lazy_loading = true # Loads when the comments are in the viewport (using the Intersection Observer API).
|
||||
|
||||
[extra.webmentions]
|
||||
# To disable for a specific section or page, set webmentions = false in that page/section's front matter's [extra] section.
|
||||
enable = false
|
||||
# Specify the domain registered with webmention.io.
|
||||
domain = ""
|
||||
|
||||
# The HTML ID for the object to fill in with the webmention data.
|
||||
# Defaults to "webmentions"
|
||||
# id = "webmentions"
|
||||
|
||||
# data configuration for the webmention.min.js script
|
||||
# The base URL to use for this page. Defaults to window.location
|
||||
# page_url =
|
||||
|
||||
# Additional URLs to check, separated by |s
|
||||
# add_urls
|
||||
|
||||
# The maximum number of words to render in reply mentions.
|
||||
# wordcount = 20
|
||||
|
||||
# The maximum number of mentions to retrieve. Defaults to 30.
|
||||
# max_webmentions = 30
|
||||
|
||||
# By default, Webmentions render using the mf2 'url' element, which plays
|
||||
# nicely with webmention bridges (such as brid.gy and telegraph)
|
||||
# but allows certain spoofing attacks. If you would like to prevent
|
||||
# spoofing, set this to a non-empty string (e.g. "true").
|
||||
# prevent_spoofing
|
||||
|
||||
# What to order the responses by; defaults to 'published'. See
|
||||
# https://github.com/aaronpk/webmention.io#api
|
||||
# sort_by
|
||||
|
||||
# The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||
# first). See https://github.com/aaronpk/webmention.io#api
|
||||
# sort_dir
|
||||
|
||||
# If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||
# (replies/mentions/etc.) as being part of the reactions
|
||||
# (favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||
# comments_are_reactions = "true"
|
||||
|
||||
# h-card configuration
|
||||
# Will identify you on the indieweb (see https://microformats.org/wiki/h-card)
|
||||
[extra.hcard]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Sense JavaScript obligatori"
|
||||
date = 2023-01-06
|
||||
updated = 2025-02-15
|
||||
updated = 2025-02-21
|
||||
description = "JavaScript només s'utilitza quan HTML i CSS no són suficients."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Sin JavaScript obligatorio"
|
||||
date = 2023-01-06
|
||||
updated = 2025-02-15
|
||||
updated = 2025-02-21
|
||||
description = "JavaScript solo se utiliza cuando HTML y CSS no son suficientes."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "No mandatory JavaScript"
|
||||
date = 2023-01-06
|
||||
updated = 2025-02-15
|
||||
updated = 2025-02-21
|
||||
description = "JavaScript is only used when HTML and CSS aren't enough."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Exemples de Markdown"
|
||||
date = 2023-01-31
|
||||
updated = 2024-11-23
|
||||
updated = 2025-02-21
|
||||
description = "Aquesta publicació mostra alguns exemples de format en Markdown, incloent-hi una taula, blocs de codi i etiquetes, citacions, taules i notes a peu de pàgina."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Ejemplos de Markdown"
|
||||
date = 2023-01-31
|
||||
updated = 2024-11-23
|
||||
updated = 2025-02-21
|
||||
description = "Esta publicación muestra algunos ejemplos de formato Markdown, incluyendo una tabla, bloques de código y etiquetas, citas, tablas y notas al pie de página."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Markdown examples"
|
||||
date = 2023-01-31
|
||||
updated = 2024-11-23
|
||||
updated = 2025-02-21
|
||||
description = "This post showcases some examples of Markdown formatting, including a table, code blocks and tags, quotes, tables, and footnotes."
|
||||
|
||||
[taxonomies]
|
||||
|
|
BIN
content/blog/mastering-tabi-settings/img/webmention_dark.webp
Normal file
BIN
content/blog/mastering-tabi-settings/img/webmention_dark.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
BIN
content/blog/mastering-tabi-settings/img/webmention_light.webp
Normal file
BIN
content/blog/mastering-tabi-settings/img/webmention_light.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Domina la configuració de tabi: guia completa"
|
||||
date = 2023-09-18
|
||||
updated = 2025-06-01
|
||||
updated = 2025-06-08
|
||||
description = "Descobreix les múltiples maneres en què pots personalitzar tabi."
|
||||
|
||||
[taxonomies]
|
||||
|
@ -1009,6 +1009,31 @@ Per a més informació, consulta la [pàgina de documentació de CSP](@/blog/sec
|
|||
|
||||
## Indieweb
|
||||
|
||||
### Webmentions
|
||||
|
||||
| Pàgina | Secció | `config.toml` | Segueix jerarquia | Requereix JavaScript |
|
||||
|:------:|:------:|:-------------:|:-----------------:|:--------------------:|
|
||||
| ❓ | ❓ | ✅ | ❓ | ✅ |
|
||||
|
||||
Com es descriu en l'estàndard W3C recomanat, [Webmention](https://www.w3.org/TR/webmention/#abstract-p-1) és una manera senzilla de notificar qualsevol URL quan la menciones al teu lloc web. Des de la perspectiva del receptor, és una manera de sol·licitar notificacions quan altres llocs web la mencionen.
|
||||
|
||||
Per a llocs web estàtics, [webmention.io](https://webmention.io/) allotja un punt final de webmention que es pot utilitzar per rebre webmentions. Aquesta funcionalitat recupera les webmentions emmagatzemades a webmention.io i les mostra per a una pàgina. Hauràs de configurar un compte per al teu lloc web a webmention.io. Quan habilitis la funcionalitat de webmention, anunciarà el teu punt final de webmention.io i mostrarà les webmentions per a qualsevol pàgina.
|
||||
|
||||
Habilita les webmentions per al teu lloc web afegint el següent al teu fitxer `config.toml`.
|
||||
|
||||
```toml
|
||||
[extra.webmentions]
|
||||
enable = true
|
||||
# Especifica el domini registrat amb webmention.io.
|
||||
domain = "www.example.com"
|
||||
```
|
||||
|
||||
❓: Per desactivar les webmentions per a una secció o pàgina específica, estableix `webmentions = false` a la secció `[extra]` del front matter d'aquesta secció o pàgina.
|
||||
|
||||
La secció de webmentions es veu així:
|
||||
|
||||
{{ dual_theme_image(light_src="blog/mastering-tabi-settings/img/webmention_light.webp", dark_src="blog/mastering-tabi-settings/img/webmention_dark.webp" alt="Captura de pantalla de webmentions mostrant republications, m'agrada, marcadors i comentaris", full_width=true) }}
|
||||
|
||||
### h-card representativa
|
||||
|
||||
| Pàgina | Secció | `config.toml` | Segueix la jerarquia | Requereix JavaScript |
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Domina la configuración de tabi: guía completa"
|
||||
date = 2023-09-18
|
||||
updated = 2025-06-01
|
||||
updated = 2025-06-08
|
||||
description = "Descubre las múltiples maneras en que puedes personalizar tabi."
|
||||
|
||||
[taxonomies]
|
||||
|
@ -1010,6 +1010,31 @@ Para obtener más información, consulta la [página de documentación de CSP](@
|
|||
|
||||
## Indieweb
|
||||
|
||||
### Webmentions
|
||||
|
||||
| Página | Sección | `config.toml` | Sigue la jerarquía | Requiere JavaScript |
|
||||
|:------:|:-------:|:-------------:|:---------------:|:-------------------:|
|
||||
| ❓ | ❓ | ✅ | ❓ | ✅ |
|
||||
|
||||
Como se describe en el estándar W3C recomendado, [Webmention](https://www.w3.org/TR/webmention/#abstract-p-1) es una manera sencilla de notificar cualquier URL cuando la mencionas en tu sitio web. Desde la perspectiva del receptor, es una forma de solicitar notificaciones cuando otros sitios web la mencionan.
|
||||
|
||||
Para sitios web estáticos, [webmention.io](https://webmention.io/) aloja un punto final de webmention que se puede utilizar para recibir webmentions. Esta función recupera las webmentions almacenadas en webmention.io y las muestra para una página. Necesitarás configurar una cuenta para tu sitio web en webmention.io. Cuando habilites la función, anunciará tu punto final de webmention.io y mostrará las webmentions para cualquier página.
|
||||
|
||||
Habilita las webmentions para tu sitio web agregando lo siguiente a tu archivo `config.toml`.
|
||||
|
||||
```toml
|
||||
[extra.webmentions]
|
||||
enable = true
|
||||
# Especifica el dominio registrado con webmention.io.
|
||||
domain = "www.example.com"
|
||||
```
|
||||
|
||||
❓: Para desactivar las webmentions para una sección o página específica, establece `webmentions = false` en la sección `[extra]` del front matter de esa sección o página.
|
||||
|
||||
La sección de webmentions se ve así:
|
||||
|
||||
{{ dual_theme_image(light_src="blog/mastering-tabi-settings/img/webmention_light.webp", dark_src="blog/mastering-tabi-settings/img/webmention_dark.webp" alt="Captura de pantalla de webmentions mostrando reposts, me gusta, marcadores y comentarios", full_width=true) }}
|
||||
|
||||
### h-card representativa
|
||||
|
||||
| Página | Sección | `config.toml` | Sigue Jerarquía | Requiere JavaScript |
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Mastering tabi Settings: A Comprehensive Guide"
|
||||
date = 2023-09-18
|
||||
updated = 2025-06-01
|
||||
updated = 2025-06-08
|
||||
description = "Discover the many ways you can customise your tabi site."
|
||||
|
||||
[taxonomies]
|
||||
|
@ -1020,6 +1020,31 @@ See the [CSP documentation page](@/blog/security/index.md) for more information.
|
|||
|
||||
## Indieweb
|
||||
|
||||
### Webmentions
|
||||
|
||||
| Page | Section | `config.toml` | Follows Hierarchy | Requires JavaScript |
|
||||
|:----:|:-------:|:-------------:|:-----------------:|:-------------------:|
|
||||
| ❓ | ❓ | ✅ | ❓ | ✅ |
|
||||
|
||||
As described by the recommended W3C standard [Webmention](https://www.w3.org/TR/webmention/#abstract-p-1) is a simple way to notify any URL when you mention it on your site. From the receiver's perspective, it's a way to request notifications when other sites mention it.
|
||||
|
||||
For static sites [webmention.io](https://webmention.io/) hosts a webmention endpoint that can be used to receive webmentions. This feature fetches the webmentions stored at webmention.io and displays them for a page. You will need to have setup an account for your website at webmention.io. When you enable the webmention feature it will advertise your webmention.io endpoint and display the webmentions for all posts.
|
||||
|
||||
Enable webmentions for your site by adding the following to your `config.toml` file.
|
||||
|
||||
```toml
|
||||
[extra.webmentions]
|
||||
enable = true
|
||||
# Specify the domain registered with webmention.io.
|
||||
domain = "www.example.com"
|
||||
```
|
||||
|
||||
❓: To disable webmentions for a specific section or page, set `webmentions = false` in the `[extra]` section of that section or page's front matter.
|
||||
|
||||
The webmentions section looks like this:
|
||||
|
||||
{{ dual_theme_image(light_src="blog/mastering-tabi-settings/img/webmention_light.webp", dark_src="blog/mastering-tabi-settings/img/webmention_dark.webp" alt="Webmentions screenshot showing reposts, likes, bookmarks, and comments", full_width=true) }}
|
||||
|
||||
### Representative h-card
|
||||
|
||||
| Page | Section | `config.toml` | Follows Hierarchy | Requires JavaScript |
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Seguretat per defecte"
|
||||
date = 2023-02-22
|
||||
updated = 2024-08-28
|
||||
updated = 2025-02-21
|
||||
description = "tabi té una Política de Seguretat de Contingut (CSP) fàcilment personalitzable amb valors segurs per defecte. Obtingues tranquil·litat i un A+ en l'Observatori de Mozilla."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Seguro por defecto"
|
||||
date = 2023-02-22
|
||||
updated = 2024-08-28
|
||||
updated = 2025-02-21
|
||||
description = "tabi tiene una Política de Seguridad de Contenido (CSP) fácilmente personalizable con configuraciones seguras. Obtén tranquilidad y una calificación de A+ en Mozilla Observatory."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Secure by default"
|
||||
date = 2023-02-22
|
||||
updated = 2024-08-28
|
||||
updated = 2025-02-21
|
||||
description = "tabi has an easily customizable Content Security Policy (CSP) with safe defaults. Get peace of mind and an A+ on Mozilla Observatory."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Guia completa sobre sèries"
|
||||
date = 2024-11-08
|
||||
updated = 2025-02-15
|
||||
updated = 2025-02-21
|
||||
description = "Aprèn a organitzar les teves publicacions en sèries seqüencials, perfectes per a tutorials, cursos i històries de diverses parts."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Guía completa sobre series"
|
||||
date = 2024-11-08
|
||||
updated = 2025-02-15
|
||||
updated = 2025-02-21
|
||||
description = "Aprende a organizar tus publicaciones en series secuenciales, perfectas para tutoriales, cursos e historias de varias partes."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "A Complete Guide to Series"
|
||||
date = 2024-11-08
|
||||
updated = 2025-02-15
|
||||
updated = 2025-02-21
|
||||
description = "Learn how to organize your posts into sequential series, perfect for tutorials, courses, and multi-part stories."
|
||||
|
||||
[taxonomies]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Custom shortcodes"
|
||||
date = 2023-02-19
|
||||
updated = 2025-02-15
|
||||
updated = 2025-05-18
|
||||
description = "This theme includes some useful custom shortcodes that you can use to enhance your posts. Whether you want to display images that adapt to light and dark themes, or format a professional-looking reference section, these custom shortcodes have got you covered."
|
||||
|
||||
[taxonomies]
|
||||
|
@ -121,6 +121,7 @@ Useful if you want to use a different image for the light and dark themes:
|
|||
{{ dual_theme_image(light_src="img/paris_day.webp", dark_src="img/paris_night.webp" alt="The Eiffel tower") }}
|
||||
|
||||
#### Usage
|
||||
|
||||
```
|
||||
{{/* dual_theme_image(light_src="img/paris_day.webp", dark_src="img/paris_night.webp" alt="The Eiffel tower") */}}
|
||||
```
|
||||
|
@ -151,7 +152,7 @@ Images with too much brightness or contrast can be jarring against a dark backgr
|
|||
|
||||
### Swap image on hover
|
||||
|
||||
Povides an interaction where the image displayed changes as the user hovers over it. Useful for before-after comparisons, for example.
|
||||
Provides an interaction where the image displayed changes as the user hovers over it. Useful for before-after comparisons, for example.
|
||||
|
||||
{{ image_hover(default_src="img/edited.webp", hovered_src="img/raw.webp", default_alt="Edited picture", hovered_alt="Original shot") }}
|
||||
|
||||
|
@ -264,7 +265,7 @@ Using the content body and setting the position to right:
|
|||
A longer note that
|
||||
can span multiple lines.
|
||||
|
||||
*Markdown* is supported.
|
||||
_Markdown_ is supported.
|
||||
{%/* end */%}
|
||||
```
|
||||
|
||||
|
@ -352,12 +353,12 @@ Both methods support the same parameters (`type`, `icon`, and `title`), with the
|
|||
|
||||
This shortcode allows you to display both the translated and original text for a quote. The quotation marks will be added automatically:
|
||||
|
||||
{{ multilingual_quote(original="Qué sosiego, ir por la vida en silencio, saludando sólo a los amigos.", translated="What tranquility, to go through life in silence, greeting only friends.", author="Francisco Umbral") }}
|
||||
{{ multilingual_quote(original="Qué sosiego, ir por la vida en silencio, saludando sólo a los amigos.", translated="What tranquillity, to go through life in silence, greeting only friends.", author="Francisco Umbral") }}
|
||||
|
||||
#### Usage
|
||||
|
||||
```
|
||||
{{/* multilingual_quote(original="Qué sosiego, ir por la vida en silencio, saludando sólo a los amigos.", translated="What tranquility, to go through life in silence, greeting only friends.", author="Francisco Umbral") */}}
|
||||
{{/* multilingual_quote(original="Qué sosiego, ir por la vida en silencio, saludando sólo a los amigos.", translated="What tranquillity, to go through life in silence, greeting only friends.", author="Francisco Umbral") */}}
|
||||
```
|
||||
|
||||
### References with hanging indent
|
||||
|
@ -439,10 +440,12 @@ Force the text direction of a content block. Overrides both the global `force_co
|
|||
Accepts the parameter `direction`: the desired text direction. This can be either "ltr" (left-to-right) or "rtl" (right-to-left). Defaults to "ltr".
|
||||
|
||||
{% force_text_direction(direction="rtl") %}
|
||||
|
||||
```python
|
||||
def مرحبا_بالعالم():
|
||||
print("مرحبا بالعالم!")
|
||||
```
|
||||
|
||||
{% end %}
|
||||
|
||||
#### Usage
|
||||
|
|
|
@ -23,6 +23,7 @@ local_image = "projects/tabi/tabi.webp"
|
|||
- سمتان داكنة وفاتحة، مع التبديل التلقائي حسب إعدادات النظام
|
||||
- [دعم التعليقات](https://welpo.github.io/tabi/blog/comments/) باستخدام giscus أو utterances أو Hyvor Talk أو Isso
|
||||
- [دعم KaTeX](https://katex.org/) للمعادلات الرياضية
|
||||
- [دعم Indieweb](https://indieweb.org/) مع microformats وh-card وwebmentions
|
||||
- [دعم Mermaid](https://welpo.github.io/tabi/blog/shortcodes/#mermaid-diagrams) لإنشاء المخططات
|
||||
- [بحث محلي](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#search) متعدد اللغات
|
||||
- تصميم متجاوب يعمل على جميع الأجهزة
|
||||
|
|
|
@ -26,6 +26,7 @@ social_media_card = "social_cards/ca_projects_tabi.jpg"
|
|||
- [Suport multilingüe complet](https://welpo.github.io/tabi/ca/blog/faq-languages/#com-gestiona-tabi-el-suport-multilingue). Afegeix tants idiomes com vulguis i deixa que els teus usuaris triin amb el selector d'idioma.
|
||||
- [Suport per a sèries](https://welpo.github.io/tabi/ca/blog/series/) per crear contingut seqüencial com tutorials, cursos i històries multipart.
|
||||
- Puntuació perfecta en Lighthouse (Rendiment, Accessibilitat, Millors Pràctiques i SEO).
|
||||
- Suport per a [Indieweb](https://indieweb.org/) amb microformats, suport per a [hcard](https://welpo.github.io/tabi/ca/blog/mastering-tabi-settings/#h-card-representativa) i [webmentions](https://welpo.github.io/tabi/blog/ca/mastering-tabi-settings/#webmentions).
|
||||
- Suport per a [diagrames de Mermaid](https://welpo.github.io/tabi/ca/blog/shortcodes/#diagrames-de-mermaid) per a crear diagrames i gràfics amb text.
|
||||
- Ressaltat de sintaxi de codi amb colors basats en [Catppuccin](https://github.com/catppuccin/catppuccin) Frappé.
|
||||
- Suport per a [comentaris usant giscus, utterances, Hyvor Talk o Isso](https://welpo.github.io/tabi/ca/blog/comments/).
|
||||
|
|
|
@ -26,6 +26,7 @@ social_media_card = "social_cards/es_projects_tabi.jpg"
|
|||
- Tema claro y oscuro. Se adapta a la configuración del sistema operativo, con un interruptor en la barra de navegación.
|
||||
- [Soporte para series](https://welpo.github.io/tabi/es/blog/series/) para crear contenido secuencial como tutoriales, cursos e historias en varias partes.
|
||||
- Puntuación perfecta en Lighthouse (Rendimiento, Accesibilidad, Mejores Prácticas y SEO).
|
||||
- Soporte para [Indieweb](https://indieweb.org/) con microformatos, soporte para [hcard](https://welpo.github.io/tabi/es/blog/mastering-tabi-settings/#h-card-representativa) y [webmentions](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#webmentions).
|
||||
- Soporte para [diagramas de Mermaid](https://welpo.github.io/tabi/es/blog/shortcodes/#diagramas-de-mermaid) para crear diagramas y gráficos con texto.
|
||||
- Resaltado de sintaxis de código con colores basados en [Catppuccin](https://github.com/catppuccin/catppuccin) Frappé.
|
||||
- Soporte para [comentarios usando giscus, utterances, Hyvor Talk o Isso](https://welpo.github.io/tabi/es/blog/comments/).
|
||||
|
|
|
@ -27,6 +27,7 @@ social_media_card = "social_cards/projects_tabi.jpg"
|
|||
- Perfect Lighthouse score (Performance, Accessibility, Best Practices and SEO).
|
||||
- [Comprehensive multi-language support](https://welpo.github.io/tabi/blog/faq-languages/#how-does-tabi-handle-multilingual-support). Add as many languages as you wish.
|
||||
- Support for [comments using giscus, utterances, Hyvor Talk, or Isso](https://welpo.github.io/tabi/blog/comments/).
|
||||
- [Indieweb](https://indieweb.org/) ready with microformats, [hcard](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#representative-h-card) and [webmentions](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#webmentions) support.
|
||||
- Code syntax highlighting with colours based on [Catppuccin](https://github.com/catppuccin/catppuccin) Frappé.
|
||||
- [Mermaid support](https://welpo.github.io/tabi/blog/shortcodes/#mermaid-diagrams) to create diagrams and charts with text.
|
||||
- [Local search](https://welpo.github.io/tabi/blog/mastering-tabi-settings/#search) with an accessible, multi-lingual interface.
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
@use 'parts/_tags.scss';
|
||||
@use 'parts/_theme-switch.scss';
|
||||
@use 'parts/_zola-error.scss';
|
||||
@use 'parts/_webmention.scss';
|
||||
|
||||
@font-face {
|
||||
src: local('Inter'),
|
||||
|
|
149
sass/parts/_webmention.scss
Normal file
149
sass/parts/_webmention.scss
Normal file
|
@ -0,0 +1,149 @@
|
|||
#webmentions {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
margin: 0;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.2em;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1.5em;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.9em;
|
||||
|
||||
svg {
|
||||
margin-inline-end: 0.2rem;
|
||||
}
|
||||
|
||||
.svg-icon,
|
||||
span {
|
||||
margin-inline-end: .3rem;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li,
|
||||
p {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
|
||||
.likes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
transition: transform 0.8s ease-out, z-index 0s linear 0.4s;
|
||||
margin-bottom: .375rem;
|
||||
margin-inline-start: -.75rem;
|
||||
|
||||
&:first-child {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.3) translateY(-4px);
|
||||
z-index: 10;
|
||||
transition: transform 0.05s ease-out, z-index 0s linear 0s;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
border: 2px solid var(--background-color, white);
|
||||
border-radius: 50%;
|
||||
aspect-ratio: 1/1;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comment {
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 10px;
|
||||
background: var(--bg-0);
|
||||
padding: 1rem;
|
||||
overflow: hidden;
|
||||
font-size: 80%;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.p-author {
|
||||
font-style: bold;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.u-url {
|
||||
font-style: italic;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.u-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin-inline-end: .625rem;
|
||||
width: 2rem;
|
||||
max-width: 100%;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
input {
|
||||
flex: 1;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 20px 0px 0px 20px;
|
||||
background-color: var(--input-background-color);
|
||||
padding-inline: 1rem 1rem;
|
||||
padding-block: .75rem;
|
||||
width: calc(60% - 2rem);
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 0px 20px 20px 0px;
|
||||
background-color: var(--input-background-color);
|
||||
padding-inline: 0.7rem 0.7rem;
|
||||
padding-block: .75rem;
|
||||
width: 7rem;
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--hover-color);
|
||||
}
|
||||
}
|
||||
}
|
412
static/js/webmention.js
Normal file
412
static/js/webmention.js
Normal file
|
@ -0,0 +1,412 @@
|
|||
/* webmention.js
|
||||
|
||||
Simple thing for embedding webmentions from webmention.io into a page, client-side.
|
||||
|
||||
(c)2018-2022 fluffy (http://beesbuzz.biz)
|
||||
2025 mmai (https://misc.rhumbs.fr)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Basic usage:
|
||||
|
||||
<script src="/path/to/webmention.js" data-param="val" ... async />
|
||||
<div id="webmentions"></div>
|
||||
|
||||
Allowed parameters:
|
||||
|
||||
page-url:
|
||||
|
||||
The base URL to use for this page. Defaults to window.location
|
||||
|
||||
add-urls:
|
||||
|
||||
Additional URLs to check, separated by |s
|
||||
|
||||
id:
|
||||
|
||||
The HTML ID for the object to fill in with the webmention data.
|
||||
Defaults to "webmentions"
|
||||
|
||||
wordcount:
|
||||
|
||||
The maximum number of words to render in reply mentions.
|
||||
|
||||
max-webmentions:
|
||||
|
||||
The maximum number of mentions to retrieve. Defaults to 30.
|
||||
|
||||
prevent-spoofing:
|
||||
|
||||
By default, Webmentions render using the mf2 'url' element, which plays
|
||||
nicely with webmention bridges (such as brid.gy and telegraph)
|
||||
but allows certain spoofing attacks. If you would like to prevent
|
||||
spoofing, set this to a non-empty string (e.g. "true").
|
||||
|
||||
sort-by:
|
||||
|
||||
What to order the responses by; defaults to 'published'. See
|
||||
https://github.com/aaronpk/webmention.io#api
|
||||
|
||||
sort-dir:
|
||||
|
||||
The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||
first). See https://github.com/aaronpk/webmention.io#api
|
||||
|
||||
comments-are-reactions:
|
||||
|
||||
If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||
(replies/mentions/etc.) as being part of the reactions
|
||||
(favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||
|
||||
A more detailed example:
|
||||
|
||||
<!-- If you want to translate the UI -->
|
||||
<script src="/path/to/umd/i18next.js"></script>
|
||||
<script>
|
||||
// Setup i18next as described in https://www.i18next.com/overview/getting-started#basic-sample
|
||||
</script>
|
||||
<!-- Otherwise, only using the following is fine -->
|
||||
<script src="/path/to/webmention.min.js"
|
||||
data-id="webmentionContainer"
|
||||
data-wordcount="30"
|
||||
data-prevent-spoofing="true"
|
||||
data-comments-are-reactions="true"
|
||||
/>
|
||||
|
||||
*/
|
||||
|
||||
// Begin LibreJS code licensing
|
||||
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Shim i18next
|
||||
window.i18next = window.i18next || {
|
||||
t: function t(/** @type {string} */key) { return key; }
|
||||
}
|
||||
const t = window.i18next.t.bind(window.i18next);
|
||||
|
||||
/**
|
||||
* Read the configuration value.
|
||||
*
|
||||
* @param {string} key The configuration key.
|
||||
* @param {string} dfl The default value.
|
||||
* @returns {string}
|
||||
*/
|
||||
function getCfg(key, dfl) {
|
||||
return document.currentScript.getAttribute("data-" + key) || dfl;
|
||||
}
|
||||
|
||||
const refurl = getCfg("page-url", window.location.href.replace(/#.*$/, ""));
|
||||
const addurls = getCfg("add-urls", undefined);
|
||||
const containerID = getCfg("id", "webmentions");
|
||||
/** @type {Number} */
|
||||
const textMaxWords = getCfg("wordcount");
|
||||
const maxWebmentions = getCfg("max-webmentions", 30);
|
||||
const mentionSource = getCfg("prevent-spoofing") ? "wm-source" : "url";
|
||||
const sortBy = getCfg("sort-by", "published");
|
||||
const sortDir = getCfg("sort-dir", "up");
|
||||
|
||||
/**
|
||||
* Strip the protocol off a URL.
|
||||
*
|
||||
* @param {string} url The URL to strip protocol off.
|
||||
* @returns {string}
|
||||
*/
|
||||
function stripurl(url) {
|
||||
return url.substr(url.indexOf('//'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduplicate multiple mentions from the same source URL.
|
||||
*
|
||||
* @param {Array<Reaction>} mentions Mentions of the source URL.
|
||||
* @return {Array<Reaction>}
|
||||
*/
|
||||
function dedupe(mentions) {
|
||||
/** @type {Array<Reaction>} */
|
||||
const filtered = [];
|
||||
/** @type {Record<string, boolean>} */
|
||||
const seen = {};
|
||||
|
||||
mentions.forEach(function (r) {
|
||||
// Strip off the protocol (i.e. treat http and https the same)
|
||||
const source = stripurl(r.url);
|
||||
if (!seen[source]) {
|
||||
filtered.push(r);
|
||||
seen[source] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format comments as HTML.
|
||||
*
|
||||
* @param {Array<Reaction>} comments The comments to format.
|
||||
* @returns string
|
||||
*/
|
||||
function formatComments(type, comments) {
|
||||
|
||||
let html = `
|
||||
<div class="webcomments">
|
||||
<h3 id="replies-header">` + getIcon('comment') + ` <span>` + comments.length + `</span> ` + type + `s </h3>
|
||||
<ol aria-labelledby="replies-header" role="list">`;
|
||||
comments.forEach(function (comment) {
|
||||
let content = '';
|
||||
if (comment.hasOwnProperty('content')) {
|
||||
if (comment.content.hasOwnProperty('html')) {
|
||||
content = comment.content.html;
|
||||
} else if (comment.content.hasOwnProperty('text')) {
|
||||
content = comment.content.text;
|
||||
}
|
||||
}
|
||||
|
||||
html += `
|
||||
<li class="comment h-entry">
|
||||
<div>
|
||||
<a class="comment_author u-author"
|
||||
href="`+ comment.author.url + `"
|
||||
target="_blank"
|
||||
title="`+ comment.author.name + `"
|
||||
rel="noreferrer">
|
||||
<img
|
||||
src="`+ comment.author.photo + `"
|
||||
alt=""
|
||||
class="u-photo"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
width="48"
|
||||
height="48"
|
||||
>
|
||||
<span class="p-author">`+ comment.author.name + `</span>
|
||||
</a>`;
|
||||
if (comment.published) {
|
||||
const published = new Date(comment.published);
|
||||
html += `
|
||||
<time class="dt-published" datetime="`+ comment.published + `">` + published.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }) + `</time>`;
|
||||
}
|
||||
html += `
|
||||
</div>
|
||||
|
||||
<p class="e-entry">`+ content + `
|
||||
<a class="u-url"
|
||||
href="`+ comment.url + `"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
source
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
`;
|
||||
});
|
||||
html += `
|
||||
</ol >
|
||||
</div >
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Reaction
|
||||
* @property {string} url
|
||||
* @property {Object?} author
|
||||
* @property {string?} author.name
|
||||
* @property {string?} author.photo
|
||||
* @property {Object?} content
|
||||
* @property {string?} content.text
|
||||
* @property {RSVPEmoji?} rsvp
|
||||
* @property {MentionType?} wm-property
|
||||
* @property {string?} wm-source
|
||||
*/
|
||||
|
||||
function getIcon(name) {
|
||||
|
||||
if (name == 'like') {
|
||||
return `<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M95.997 41.986l-.026-.035C85.746 28.36 68.428 21.423 51.165 24.881 30.138 29.094 15.004 47.558 15 69.003c0 24.413 14.906 47.964 39.486 70.086 8.43 7.586 17.437 14.468 26.444 20.533.728.49 1.444.967 2.148 1.43l1.39.909 1.355.872 1.317.835.645.403 1.259.78 1.194.726 1.032.619 1.38.807.418.236a6 6 0 005.864 0l1.138-.654 1.154-.684 1.118-.675.614-.376 1.26-.779a212 212 0 00.644-.403l1.317-.835 1.355-.872 1.39-.909c.704-.463 1.42-.94 2.148-1.43 9.007-6.065 18.015-12.947 26.444-20.533C162.094 116.967 177 93.416 177 69.004c-.004-21.446-15.138-39.91-36.165-44.123-17.07-3.42-34.174 3.323-44.43 16.568l-.408.537zm42.48-5.338c15.421 3.09 26.52 16.63 26.523 32.357 0 19.607-12.438 39.847-33.532 59.357l-1.316 1.205c-.22.201-.443.402-.666.603-7.977 7.18-16.548 13.727-25.118 19.498l-.745.5c-.74.494-1.466.973-2.177 1.437l-1.402.906-1.359.864-.662.416-1.292.8-.732.446-.73-.446-1.292-.8-.662-.416-1.36-.864-1.4-.906a235.406 235.406 0 01-2.923-1.937c-8.57-5.77-17.14-12.319-25.118-19.498l-.666-.603-1.316-1.205C39.438 108.852 27 88.612 27 69.004c.003-15.726 11.102-29.267 26.523-32.356 15.253-3.056 30.565 4.954 36.756 19.208l.204.478c2.084 4.878 9.009 4.85 11.053-.045 6.062-14.511 21.52-22.73 36.941-19.641z" fill="currentColor" /></svg> `;
|
||||
} else if (name == 'repost') {
|
||||
return `<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M18.472 146.335l-.075-.184a5.968 5.968 0 01-.216-.684l-.014-.056a5.643 5.643 0 01-.082-.397l-.013-.083a5.886 5.886 0 01-.072-.96V144c0-.157.006-.313.018-.467l.006-.075c.012-.132.028-.261.048-.39l.016-.095c.008-.05.017-.1.027-.149.005-.019.008-.038.012-.058.028-.133.06-.264.096-.393l.026-.088a5.86 5.86 0 01.482-1.159l.043-.077a5.642 5.642 0 01.31-.49l.015-.022.076-.104.044-.059a3.856 3.856 0 01.165-.208l.052-.061c.102-.12.21-.236.321-.348l18-18a6 6 0 018.661 8.303l-.175.183L38.484 138H120c23.196 0 42-18.804 42-42a6 6 0 0112 0c0 29.525-23.696 53.516-53.107 53.993L120 150H38.486l7.757 7.757a6 6 0 01.175 8.303l-.175.183a6 6 0 01-8.303.175l-.183-.175-18-18-.145-.151a6.036 6.036 0 01-.829-1.125l-.058-.105a4.08 4.08 0 01-.06-.114l-.04-.077a4.409 4.409 0 01-.139-.3l-.014-.036zM154.06 25.582l.183.175 18 18a6.036 6.036 0 01.974 1.276l.058.105c.02.035.038.07.056.105l.043.086a4.411 4.411 0 01.14.3l.014.036a5.965 5.965 0 01.291.868l.014.056c.032.13.059.263.082.397l.013.083a5.886 5.886 0 01.067.692v.014a6.11 6.11 0 01-.013.692l-.006.075a5.856 5.856 0 01-.048.39l-.016.095c-.008.05-.017.1-.027.149-.005.019-.008.038-.012.058-.028.133-.06.264-.096.393l-.026.088a5.86 5.86 0 01-.482 1.159l-.043.077-.052.09-.029.048a6.006 6.006 0 01-.32.478l-.044.059a3.857 3.857 0 01-.165.208l-.052.061a6.34 6.34 0 01-.176.197l-.145.15-18 18a6 6 0 01-8.661-8.302l.175-.183L153.514 54H72c-23.196 0-42 18.804-42 42a6 6 0 11-12 0c0-29.525 23.696-53.516 53.107-53.993L72 42h81.516l-7.759-7.757a6 6 0 01-.175-8.303l.175-.183a6 6 0 018.303-.175z" fill="currentColor" /></svg> `;
|
||||
} else if (name == 'comment') {
|
||||
return `<svg width = "24" height = "24" viewBox = "0 0 150 150" xmlns = "http://www.w3.org/2000/svg" > <path d="M75-.006a75 75 0 0174.997 74.31l.003.69c0 41.422-33.579 75-75 75H11.75c-6.49 0-11.75-5.26-11.75-11.75v-63.25a75 75 0 0175-75zm0 12a63 63 0 00-63 63v63h63c34.446 0 62.435-27.645 62.992-61.93l.008-1.041-.003-.633A63 63 0 0075 11.994zm21 72a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45zm0-24a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45z" fill="currentColor" /></svg> `;
|
||||
} else if (name == 'bookmark') {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M 6.0097656 2 C 4.9143111 2 4.0097656 2.9025988 4.0097656 3.9980469 L 4 22 L 12 19 L 20 22 L 20 20.556641 L 20 4 C 20 2.9069372 19.093063 2 18 2 L 6.0097656 2 z M 6.0097656 4 L 18 4 L 18 19.113281 L 12 16.863281 L 6.0019531 19.113281 L 6.0097656 4 z" fill="currentColor"></path>
|
||||
</svg>`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a list of reactions as HTML.
|
||||
*
|
||||
* @param {Array<Reaction>} reacts List of reactions to format
|
||||
* @returns string
|
||||
*/
|
||||
function formatReactions(type, reacts) {
|
||||
let html = `
|
||||
<div class="color--primary" >
|
||||
<h3 id=`+ type + ` - header"> ` + getIcon(type) + ` <span>` + reacts.length + `</span> ` + type + `s </h3>
|
||||
|
||||
<ol class="likes" role = "list" aria - labelledby="`+ type + `-header"> `;
|
||||
|
||||
reacts.forEach(function (react) {
|
||||
html += `
|
||||
<li class="h-card">
|
||||
<a class="u-url"
|
||||
href="`+ react.author.url + `
|
||||
target="_blank"
|
||||
rel = "noreferrer"
|
||||
title = "`+ react.author.name + `" >
|
||||
<img
|
||||
alt=""
|
||||
class="lazy mentions__image u-photo"
|
||||
src="`+ react.author.photo + `"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
width="48"
|
||||
height="48"
|
||||
>
|
||||
<span class="p-author visually-hidden" aria-hidden="true">{{ author }}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
});
|
||||
|
||||
html += `
|
||||
</ol >
|
||||
</div >
|
||||
`;
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef WebmentionResponse
|
||||
* @type {Object}
|
||||
* @property {Array<Reaction>} children
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register event listener.
|
||||
*/
|
||||
window.addEventListener("load", async function () {
|
||||
const container = document.getElementById(containerID);
|
||||
if (!container) {
|
||||
// no container, so do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
const pages = [stripurl(refurl)];
|
||||
if (!!addurls) {
|
||||
addurls.split('|').forEach(function (url) {
|
||||
pages.push(stripurl(url));
|
||||
});
|
||||
}
|
||||
|
||||
let apiURL = `https://webmention.io/api/mentions.jf2?per-page=${maxWebmentions}&sort-by=${sortBy}&sort-dir=${sortDir}`;
|
||||
|
||||
pages.forEach(function (path) {
|
||||
apiURL += `&target[]=${encodeURIComponent('http:' + path)}&target[]=${encodeURIComponent('https:' + path)}`;
|
||||
});
|
||||
|
||||
// apiURL = 'http://127.0.0.1:1111/test_webmentions.jf2';
|
||||
/** @type {WebmentionResponse} */
|
||||
let json = {};
|
||||
try {
|
||||
// const response = await window.fetch(apiURL);
|
||||
const response = await window.fetch(apiURL);
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
json = await response.json();
|
||||
} else {
|
||||
console.error("Could not parse response");
|
||||
new Error(response.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
// Purposefully not escalate further, i.e. no UI update
|
||||
console.error("Request failed", error);
|
||||
}
|
||||
|
||||
/** @type {Array<Reaction>} */
|
||||
let comments = [];
|
||||
/** @type {Array<Reaction>} */
|
||||
let mentions = [];
|
||||
/** @type {Array<Reaction>} */
|
||||
const bookmarks = [];
|
||||
/** @type {Array<Reaction>} */
|
||||
const likes = [];
|
||||
/** @type {Array<Reaction>} */
|
||||
const reposts = [];
|
||||
/** @type {Array<Reaction>} */
|
||||
const follows = [];
|
||||
|
||||
/** @type {Record<MentionType, Array<Reaction>>} */
|
||||
const mapping = {
|
||||
"in-reply-to": comments,
|
||||
"like-of": likes,
|
||||
"repost-of": reposts,
|
||||
"bookmark-of": bookmarks,
|
||||
"follow-of": follows,
|
||||
"mention-of": mentions,
|
||||
"rsvp": comments
|
||||
};
|
||||
|
||||
json.children.forEach(function (child) {
|
||||
// Map each mention into its respective container
|
||||
const store = mapping[child['wm-property']];
|
||||
if (store) {
|
||||
store.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
// format the comment-type things
|
||||
let formattedMentions = '';
|
||||
if (mentions.length > 0) {
|
||||
formattedMentions = formatComments('mention', dedupe(mentions));
|
||||
}
|
||||
|
||||
let formattedComments = '';
|
||||
if (comments.length > 0) {
|
||||
formattedComments = formatComments('comment', dedupe(comments));
|
||||
}
|
||||
|
||||
// format likes
|
||||
let likesStr = '';
|
||||
if (likes.length > 0) {
|
||||
likesStr = formatReactions('like', dedupe(likes));
|
||||
}
|
||||
|
||||
// format reposts
|
||||
let repostsStr = '';
|
||||
if (reposts.length > 0) {
|
||||
repostsStr = formatReactions('repost', dedupe(reposts));
|
||||
}
|
||||
|
||||
// format bookmarks
|
||||
let bookmarksStr = '';
|
||||
if (bookmarks.length > 0) {
|
||||
bookmarksStr = formatReactions('bookmark', dedupe(bookmarks));
|
||||
}
|
||||
|
||||
container.innerHTML = `<div id="webmentions">${repostsStr}${likesStr}${bookmarksStr}${formattedComments}${formattedMentions}</div>`;
|
||||
});
|
||||
}());
|
||||
|
||||
// End-of-file marker for LibreJS
|
||||
// @license-end
|
66
static/js/webmention.min.js
vendored
Normal file
66
static/js/webmention.min.js
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
(()=>{function t(t,e){return document.currentScript.getAttribute("data-"+t)||e}window.i18next=window.i18next||{t:function(t){return t}},window.i18next.t.bind(window.i18next);let m=t("page-url",window.location.href.replace(/#.*$/,"")),f=t("add-urls",void 0),e=t("id","webmentions"),g=(t("wordcount"),t("max-webmentions",30)),v=(t("prevent-spoofing"),t("sort-by","published")),b=t("sort-dir","up");function y(t){return t.substr(t.indexOf("//"))}function x(t){let l=[],a={};return t.forEach(function(t){var e=y(t.url);a[e]||(l.push(t),a[e]=!0)}),l}function L(t,e){let a=`
|
||||
<div class="webcomments">
|
||||
<h3 id="replies-header">`+o("comment")+" <span>"+e.length+"</span> "+t+`s </h3>
|
||||
<ol aria-labelledby="replies-header" role="list">`;return e.forEach(function(t){let e="";var l;t.hasOwnProperty("content")&&(t.content.hasOwnProperty("html")?e=t.content.html:t.content.hasOwnProperty("text")&&(e=t.content.text)),a+=`
|
||||
<li class="comment h-entry">
|
||||
<div>
|
||||
<a class="comment_author u-author"
|
||||
href="`+t.author.url+`"
|
||||
target="_blank"
|
||||
title="`+t.author.name+`"
|
||||
rel="noreferrer">
|
||||
<img
|
||||
src="`+t.author.photo+`"
|
||||
alt=""
|
||||
class="u-photo"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
width="48"
|
||||
height="48"
|
||||
>
|
||||
<span class="p-author">`+t.author.name+`</span>
|
||||
</a>`,t.published&&(l=new Date(t.published),a+=`
|
||||
<time class="dt-published" datetime="`+t.published+'">'+l.toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"})+"</time>"),a+=`
|
||||
</div>
|
||||
|
||||
<p class="e-entry">`+e+`
|
||||
<a class="u-url"
|
||||
href="`+t.url+`"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
source
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
`}),a+=`
|
||||
</ol >
|
||||
</div >
|
||||
`}function o(t){return"like"==t?'<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M95.997 41.986l-.026-.035C85.746 28.36 68.428 21.423 51.165 24.881 30.138 29.094 15.004 47.558 15 69.003c0 24.413 14.906 47.964 39.486 70.086 8.43 7.586 17.437 14.468 26.444 20.533.728.49 1.444.967 2.148 1.43l1.39.909 1.355.872 1.317.835.645.403 1.259.78 1.194.726 1.032.619 1.38.807.418.236a6 6 0 005.864 0l1.138-.654 1.154-.684 1.118-.675.614-.376 1.26-.779a212 212 0 00.644-.403l1.317-.835 1.355-.872 1.39-.909c.704-.463 1.42-.94 2.148-1.43 9.007-6.065 18.015-12.947 26.444-20.533C162.094 116.967 177 93.416 177 69.004c-.004-21.446-15.138-39.91-36.165-44.123-17.07-3.42-34.174 3.323-44.43 16.568l-.408.537zm42.48-5.338c15.421 3.09 26.52 16.63 26.523 32.357 0 19.607-12.438 39.847-33.532 59.357l-1.316 1.205c-.22.201-.443.402-.666.603-7.977 7.18-16.548 13.727-25.118 19.498l-.745.5c-.74.494-1.466.973-2.177 1.437l-1.402.906-1.359.864-.662.416-1.292.8-.732.446-.73-.446-1.292-.8-.662-.416-1.36-.864-1.4-.906a235.406 235.406 0 01-2.923-1.937c-8.57-5.77-17.14-12.319-25.118-19.498l-.666-.603-1.316-1.205C39.438 108.852 27 88.612 27 69.004c.003-15.726 11.102-29.267 26.523-32.356 15.253-3.056 30.565 4.954 36.756 19.208l.204.478c2.084 4.878 9.009 4.85 11.053-.045 6.062-14.511 21.52-22.73 36.941-19.641z" fill="currentColor" /></svg> ':"repost"==t?'<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M18.472 146.335l-.075-.184a5.968 5.968 0 01-.216-.684l-.014-.056a5.643 5.643 0 01-.082-.397l-.013-.083a5.886 5.886 0 01-.072-.96V144c0-.157.006-.313.018-.467l.006-.075c.012-.132.028-.261.048-.39l.016-.095c.008-.05.017-.1.027-.149.005-.019.008-.038.012-.058.028-.133.06-.264.096-.393l.026-.088a5.86 5.86 0 01.482-1.159l.043-.077a5.642 5.642 0 01.31-.49l.015-.022.076-.104.044-.059a3.856 3.856 0 01.165-.208l.052-.061c.102-.12.21-.236.321-.348l18-18a6 6 0 018.661 8.303l-.175.183L38.484 138H120c23.196 0 42-18.804 42-42a6 6 0 0112 0c0 29.525-23.696 53.516-53.107 53.993L120 150H38.486l7.757 7.757a6 6 0 01.175 8.303l-.175.183a6 6 0 01-8.303.175l-.183-.175-18-18-.145-.151a6.036 6.036 0 01-.829-1.125l-.058-.105a4.08 4.08 0 01-.06-.114l-.04-.077a4.409 4.409 0 01-.139-.3l-.014-.036zM154.06 25.582l.183.175 18 18a6.036 6.036 0 01.974 1.276l.058.105c.02.035.038.07.056.105l.043.086a4.411 4.411 0 01.14.3l.014.036a5.965 5.965 0 01.291.868l.014.056c.032.13.059.263.082.397l.013.083a5.886 5.886 0 01.067.692v.014a6.11 6.11 0 01-.013.692l-.006.075a5.856 5.856 0 01-.048.39l-.016.095c-.008.05-.017.1-.027.149-.005.019-.008.038-.012.058-.028.133-.06.264-.096.393l-.026.088a5.86 5.86 0 01-.482 1.159l-.043.077-.052.09-.029.048a6.006 6.006 0 01-.32.478l-.044.059a3.857 3.857 0 01-.165.208l-.052.061a6.34 6.34 0 01-.176.197l-.145.15-18 18a6 6 0 01-8.661-8.302l.175-.183L153.514 54H72c-23.196 0-42 18.804-42 42a6 6 0 11-12 0c0-29.525 23.696-53.516 53.107-53.993L72 42h81.516l-7.759-7.757a6 6 0 01-.175-8.303l.175-.183a6 6 0 018.303-.175z" fill="currentColor" /></svg> ':"comment"==t?'<svg width = "24" height = "24" viewBox = "0 0 150 150" xmlns = "http://www.w3.org/2000/svg" > <path d="M75-.006a75 75 0 0174.997 74.31l.003.69c0 41.422-33.579 75-75 75H11.75c-6.49 0-11.75-5.26-11.75-11.75v-63.25a75 75 0 0175-75zm0 12a63 63 0 00-63 63v63h63c34.446 0 62.435-27.645 62.992-61.93l.008-1.041-.003-.633A63 63 0 0075 11.994zm21 72a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45zm0-24a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45z" fill="currentColor" /></svg> ':"bookmark"==t?`<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M 6.0097656 2 C 4.9143111 2 4.0097656 2.9025988 4.0097656 3.9980469 L 4 22 L 12 19 L 20 22 L 20 20.556641 L 20 4 C 20 2.9069372 19.093063 2 18 2 L 6.0097656 2 z M 6.0097656 4 L 18 4 L 18 19.113281 L 12 16.863281 L 6.0019531 19.113281 L 6.0097656 4 z" fill="currentColor"></path>
|
||||
</svg>`:void 0}function k(t,e){let l=`
|
||||
<div class="color--primary" >
|
||||
<h3 id=`+t+' - header"> '+o(t)+" <span>"+e.length+"</span> "+t+`s </h3>
|
||||
|
||||
<ol class="likes" role = "list" aria - labelledby="`+t+'-header"> ';return e.forEach(function(t){l+=`
|
||||
<li class="h-card">
|
||||
<a class="u-url"
|
||||
href="`+t.author.url+`
|
||||
target="_blank"
|
||||
rel = "noreferrer"
|
||||
title = "`+t.author.name+`" >
|
||||
<img
|
||||
alt=""
|
||||
class="lazy mentions__image u-photo"
|
||||
src="`+t.author.photo+`"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
width="48"
|
||||
height="48"
|
||||
>
|
||||
<span class="p-author visually-hidden" aria-hidden="true">{{ author }}</span>
|
||||
</a>
|
||||
</li>
|
||||
`}),l+=`
|
||||
</ol >
|
||||
</div >
|
||||
`}window.addEventListener("load",async function(){var c=document.getElementById(e);if(c){let e=[y(m)],l=(f&&f.split("|").forEach(function(t){e.push(y(t))}),`https://webmention.io/api/mentions.jf2?per-page=${g}&sort-by=${v}&sort-dir=`+b),t=(e.forEach(function(t){l+=`&target[]=${encodeURIComponent("http:"+t)}&target[]=`+encodeURIComponent("https:"+t)}),{});try{200<=(h=await window.fetch(l)).status&&h.status<300?t=await h.json():(console.error("Could not parse response"),new Error(h.statusText))}catch(t){console.error("Request failed",t)}var h,d=[],u=[],p=[],w=[];let a={"in-reply-to":h=[],"like-of":p,"repost-of":w,"bookmark-of":u,"follow-of":[],"mention-of":d,rsvp:h},o=(t.children.forEach(function(t){var e=a[t["wm-property"]];e&&e.push(t)}),""),n=(0<d.length&&(o=L("mention",x(d))),""),r=(0<h.length&&(n=L("comment",x(h))),""),i=(0<p.length&&(r=k("like",x(p))),""),s=(0<w.length&&(i=k("repost",x(w))),"");0<u.length&&(s=k("bookmark",x(u))),c.innerHTML=`<div id="webmentions">${i}${r}${s}${n}${o}</div>`}})})();
|
|
@ -102,10 +102,11 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
{# End debugging #}
|
||||
|
||||
<main>
|
||||
<article>
|
||||
<h1 class="article-title">
|
||||
<article class="h-entry">
|
||||
<h1 class="p-name article-title">
|
||||
{{ page.title | markdown(inline=true) | safe }}
|
||||
</h1>
|
||||
<a class="u-url u-uid" href="{{ page.permalink | safe }}"></a>
|
||||
|
||||
<ul class="meta">
|
||||
{#- Draft indicator -#}
|
||||
|
@ -122,14 +123,17 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
{%- endif -%}
|
||||
|
||||
{%- if author_list | length == 1 -%}
|
||||
{%- set author_string = author_list.0 -%}
|
||||
{%- set author_string = '<span class="p-author">' ~ author_list.0 ~ '</span>' -%}
|
||||
{%- else -%}
|
||||
{%- set last_author = author_list | last -%}
|
||||
{%- set other_authors = author_list | slice(end=-1) -%}
|
||||
{%- set author_separator = macros_translate::translate(key="author_separator", default=", ", language_strings=language_strings) -%}
|
||||
{%- set author_separator = '</span>' ~ author_separator ~ '<span class="p-author">' -%}
|
||||
{%- set conjunction = macros_translate::translate(key="author_conjunction", default=" and ", language_strings=language_strings) -%}
|
||||
{%- set conjunction = '</span>' ~ conjunction ~ '<span class="p-author">' -%}
|
||||
{%- set author_string = other_authors | join(sep=author_separator) -%}
|
||||
{%- set author_string = author_string ~ conjunction ~ last_author -%}
|
||||
{%- set author_string = '<span class="p-author">' ~ author_string ~ '</span>' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- set by_author = macros_translate::translate(key="by_author", default="By $AUTHOR", language_strings=language_strings) -%}
|
||||
|
@ -145,7 +149,7 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
|
||||
{#- Date -#}
|
||||
{% if page.date and macros_settings::evaluate_setting_priority(setting="show_date", page=page, default_global_value=true) == "true" %}
|
||||
<li>{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{ macros_format_date::format_date(date=page.date, short=true, language_strings=language_strings) }}</li>
|
||||
<li><time class="dt-published" datetime="{{ page.date }}">{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{ macros_format_date::format_date(date=page.date, short=true, language_strings=language_strings) }}</time></li>
|
||||
{#- Variable to keep track of whether we've shown a section, to avoid separators as the first element -#}
|
||||
{%- set previous_visible = true -%}
|
||||
{% endif %}
|
||||
|
@ -160,7 +164,7 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
{%- if page.taxonomies and page.taxonomies.tags -%}
|
||||
<li class="tag">{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{- macros_translate::translate(key="tags", default="tags", language_strings=language_strings) | capitalize -}}: </li>
|
||||
{%- for tag in page.taxonomies.tags -%}
|
||||
<li class="tag"><a href="{{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}">{{ tag }}</a>
|
||||
<li class="tag"><a class="p-category" href="{{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}">{{ tag }}</a>
|
||||
{%- if not loop.last -%}
|
||||
,
|
||||
{%- endif -%}
|
||||
|
@ -175,7 +179,7 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
{%- set formatted_date = macros_format_date::format_date(date=page.updated, short=true, language_strings=language_strings) -%}
|
||||
{%- set updated_str = last_updated_str | replace(from="$DATE", to=formatted_date) -%}
|
||||
{%- set previous_visible = true -%}
|
||||
</ul><ul class="meta last-updated"><li>{{ updated_str }}</li>
|
||||
</ul><ul class="meta last-updated"><li><time class="dt-updated" datetime="{{ page.updated }}">{{ updated_str }}</time></li>
|
||||
{#- Show link to remote changes if enabled -#}
|
||||
{%- if config.extra.remote_repository_url and macros_settings::evaluate_setting_priority(setting="show_remote_changes", page=page, default_global_value=true) == "true" -%}
|
||||
<li>{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}<a class="external" href="{% include "partials/history_url.html" %}" {{ blank_target }} rel="{{ rel_attributes }}">{{ macros_translate::translate(key="see_changes", default="See changes", language_strings=language_strings) }}</a></li>
|
||||
|
@ -227,7 +231,13 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
{{ macros_toc::toc(page=page, header=true, language_strings=language_strings) }}
|
||||
{% endif %}
|
||||
|
||||
<section class="body">
|
||||
{#- Optional Summary paragraph for readers -#}
|
||||
{% if page.description %}
|
||||
<p class="p-summary" hidden>{{ page.description }}</p>
|
||||
{%- endif -%}
|
||||
|
||||
|
||||
<section class="e-content body">
|
||||
{#- Replace series_intro placeholder -#}
|
||||
{%- set content_with_intro = page.content -%}
|
||||
{%- if "<!-- series_intro -->" in page.content -%}
|
||||
|
@ -334,6 +344,14 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||
{% include "partials/comments.html" %}
|
||||
{% endif %}
|
||||
|
||||
{#- Webmentions -#}
|
||||
{%- set global_webmentions_enabled = config.extra.webmentions.enable | default(value=false) -%}
|
||||
{%- set page_webmentions_enabled = page.extra.webmentions | default(value=global_webmentions_enabled) -%}
|
||||
{%- set webmentions_enabled = global_webmentions_enabled and page_webmentions_enabled != false or page_webmentions_enabled == true -%}
|
||||
{%- if webmentions_enabled -%}
|
||||
{%- include "partials/webmentions.html" -%}
|
||||
{%- endif -%}
|
||||
|
||||
</article>
|
||||
</main>
|
||||
|
||||
|
|
|
@ -54,6 +54,11 @@ content="default-src 'self'
|
|||
{%- 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 -%}
|
||||
|
||||
{#- Append WebSocket for Zola serve mode -#}
|
||||
{%- if config.mode == "serve" -%}
|
||||
{%- set connect_src = connect_src ~ " ws:" -%}
|
||||
|
|
|
@ -21,9 +21,16 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Button to go to the comment section #}
|
||||
{% if comment_system %}
|
||||
<a href="#comments" id="comments-button" class="no-hover-padding" title="{{ macros_translate::translate(key="go_to_comments", default="Go to comments section", language_strings=language_strings) }}">
|
||||
{# 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 %}
|
||||
|
|
51
templates/partials/webmentions.html
Normal file
51
templates/partials/webmentions.html
Normal 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>
|
42
theme.toml
42
theme.toml
|
@ -376,6 +376,48 @@ custom_subset = true
|
|||
# page_author_hashes = "" # hash (or list of hashes) of the author.
|
||||
# lazy_loading = true # Loads when the comments are in the viewport (using the Intersection Observer API).
|
||||
|
||||
[extra.webmentions]
|
||||
# To disable for a specific section or page, set webmentions = false in that page/section's front matter's [extra] section.
|
||||
enable = false
|
||||
# Specify the domain registered with webmention.io.
|
||||
# domain = ""
|
||||
|
||||
# The HTML ID for the object to fill in with the webmention data.
|
||||
# Defaults to "webmentions"
|
||||
# id = "webmentions"
|
||||
|
||||
# data configuration for the webmention.min.js script
|
||||
# The base URL to use for this page. Defaults to window.location
|
||||
# page_url =
|
||||
|
||||
# Additional URLs to check, separated by |s
|
||||
# add_urls
|
||||
|
||||
# The maximum number of words to render in reply mentions.
|
||||
# wordcount = 20
|
||||
|
||||
# The maximum number of mentions to retrieve. Defaults to 30.
|
||||
# max_webmentions = 30
|
||||
|
||||
# By default, Webmentions render using the mf2 'url' element, which plays
|
||||
# nicely with webmention bridges (such as brid.gy and telegraph)
|
||||
# but allows certain spoofing attacks. If you would like to prevent
|
||||
# spoofing, set this to a non-empty string (e.g. "true").
|
||||
# prevent_spoofing
|
||||
|
||||
# What to order the responses by; defaults to 'published'. See
|
||||
# https://github.com/aaronpk/webmention.io#api
|
||||
# sort_by
|
||||
|
||||
# The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||
# first). See https://github.com/aaronpk/webmention.io#api
|
||||
# sort_dir
|
||||
|
||||
# If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||
# (replies/mentions/etc.) as being part of the reactions
|
||||
# (favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||
# comments_are_reactions = "true"
|
||||
|
||||
# h-card configuration
|
||||
# Will identify you on the indieweb (see https://microformats.org/wiki/h-card)
|
||||
[extra.hcard]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue