Skip to content

Des commentaires a Mkdocs avec Mastodon

Sources :

J'ai utiliser ce lien pour ajouter le plugin Mastodon pour les commentaires

Ma configuration

La conf mkdocs.yml :

site_name: Be Cloud, code it !
theme: 
  name: material 
  custom_dir: theme

extra_css:
  - assets/stylesheets/comments.css

extra:
  mastodon:
    user: myUserName     # Replace with your account name
    host: myMastodon.com  # Replace with the instance you use

Le contenu du fichier theme/partials/comments.html

{% if page.meta.comments %}
  {% if page.meta.comment_id %}
    <noscript>
      <div class="admonition danger">
        <p class="admonition-title">
          Please enable Javascript to see comments from Mastodon.
        </p>
      </div>
    </noscript>
    <p>
      <a href="https://{{ config.extra.mastodon.host }}/@{{ config.extra.mastodon.user }}/{{ page.meta.comment_id }}">Commentez cet article de blog </a> en utilisant un compte compatible Fediverse (Mastodon ou similaire).
    </p>

    <p id="mastodon-comments-list"></p>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.4.1/purify.min.js" integrity="sha512-uHOKtSfJWScGmyyFr2O2+efpDx2nhwHU2v7MVeptzZoiC7bdF6Ny/CmZhN2AwIK1oCFiVQQ5DA/L9FSzyPNu6Q=="    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script type="text/javascript">
      var host = '{{ config.extra.mastodon.host }}';
      var user = '{{ config.extra.mastodon.user }}';
      var id = '{{ page.meta.comment_id }}'

      function escapeHtml(unsafe) {
        return unsafe
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;")
          .replace(/"/g, "&quot;")
          .replace(/'/g, "&#039;");
      }

      var commentsLoaded = false;

      function toot_active(toot, what) {
        var count = toot[what+'_count'];
        return count > 0 ? 'active' : '';
      }

      function toot_count(toot, what) {
        var count = toot[what+'_count'];
        return count > 0 ? count : '';
      }

      function user_account(account) {
        var result =`@${account.acct}`;
        if (account.acct.indexOf('@') === -1) {
          var domain = new URL(account.url)
          result += `@${domain.hostname}`
        }
        return result;
      }

      function render_toots(toots, in_reply_to, depth) {
        var tootsToRender = toots
          .filter(toot => toot.in_reply_to_id === in_reply_to)
          .sort((a, b) => a.created_at.localeCompare(b.created_at));
        tootsToRender.forEach(toot => render_toot(toots, toot, depth));
      }

      function render_toot(toots, toot, depth) {
        toot.account.display_name = escapeHtml(toot.account.display_name);
        toot.account.emojis.forEach(emoji => {
          toot.account.display_name = toot.account.display_name.replace(`:${emoji.shortcode}:`, `<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" height="20" width="20" />`);
        });
        mastodonComment =
          `<div class="mastodon-comment" style="margin-left: calc(var(--mastodon-comment-indent) * ${depth})">
            <div class="author">
              <div class="avatar">
                <img src="${escapeHtml(toot.account.avatar_static)}" height=60 width=60 alt="">
              </div>
              <div class="details">
                <a class="name" href="${toot.account.url}" rel="nofollow">${toot.account.display_name}</a>
                <a class="user" href="${toot.account.url}" rel="nofollow">${user_account(toot.account)}</a>
              </div>
              <a class="date" href="${toot.url}" rel="nofollow">${toot.created_at.substr(0, 10)} ${toot.created_at.substr(11, 8)}</a>
            </div>
            <div class="content">${toot.content}</div>
            <div class="attachments">
              ${toot.media_attachments.map(attachment => {
                if (attachment.type === 'image') {
                  return `<a href="${attachment.url}" rel="nofollow"><img src="${attachment.preview_url}" alt="${attachment.description}" /></a>`;
                } else if (attachment.type === 'video') {
                  return `<video controls><source src="${attachment.url}" type="${attachment.mime_type}"></video>`;
                } else if (attachment.type === 'gifv') {
                  return `<video autoplay loop muted playsinline><source src="${attachment.url}" type="${attachment.mime_type}"></video>`;
                } else if (attachment.type === 'audio') {
                  return `<audio controls><source src="${attachment.url}" type="${attachment.mime_type}"></audio>`;
                } else {
                  return `<a href="${attachment.url}" rel="nofollow">${attachment.type}</a>`;
                }
              }).join('')}
            </div>
            <div class="status">
              <div class="twemoji replies ${toot_active(toot, 'replies')}">
                <a href="${toot.url}" rel="nofollow">{% include ".icons/fontawesome/solid/reply.svg" %}${toot_count(toot, 'replies')}</a>
              </div>
              <div class="twemoji reblogs ${toot_active(toot, 'reblogs')}">
                <a href="${toot.url}" rel="nofollow">{% include ".icons/fontawesome/solid/retweet.svg" %}${toot_count(toot, 'reblogs')}</a>
              </div>
              <div class="twemoji favourites ${toot_active(toot, 'favourites')}">
                <a href="${toot.url}" rel="nofollow">{% include ".icons/fontawesome/solid/star.svg" %}${toot_count(toot, 'favourites')}</a>
              </div>
            </div>
          </div>`;
        document.getElementById('mastodon-comments-list').appendChild(DOMPurify.sanitize(mastodonComment, {'RETURN_DOM_FRAGMENT': true}));

        render_toots(toots, toot.id, depth + 1)
      }

      function loadComments() {
        if (commentsLoaded) return;

        document.getElementById("mastodon-comments-list").innerHTML = "Loading comments from the Fediverse...";

        fetch('https://' + host + '/api/v1/statuses/' + id + '/context')
          .then(function(response) {
            return response.json();
          })
          .then(function(data) {
            if(data['descendants'] && Array.isArray(data['descendants']) && data['descendants'].length > 0) {
                document.getElementById('mastodon-comments-list').innerHTML = "";
                render_toots(data['descendants'], id, 0)
            } else {
              document.getElementById('mastodon-comments-list').innerHTML = 
              `<div class="admonition info">
                <p class="admonition-title">
                  Aucun commentaire trouvé. <a href="https://{{ config.extra.mastodon.host }}/@{{ config.extra.mastodon.user }}/{{ page.meta.comment_id }}">Be the first!</a>
                </p>
              </div>
              `;
            }

            commentsLoaded = true;
          });
      }

      function respondToVisibility(element, callback) {
        var options = {
          root: null,
        };

        var observer = new IntersectionObserver((entries, observer) => {
          entries.forEach(entry => {
            if (entry.intersectionRatio > 0) {
              callback();
            }
          });
        }, options);

        observer.observe(element);
      }

      var comments = document.getElementById("mastodon-comments-list");
      respondToVisibility(comments, loadComments);
    </script>
  {% else %}
    <div class="admonition warning">
      <p class="admonition-title">
        No Mastodon post configured for this page. Contact {{ config.site_author | default('the post author', true) }} if you want to comment here.
      </p>
    </div>
  {% endif %}
{% endif %}

Le fichier assets/stylesheets/comments.css :

:root{
    --mastodon-comment-indent: 40px;
    --mastodon-comment-border-radius: 3px;

    --mastodon-comment-bg-color: rgba(0, 0, 0, 0.2);
    --mastodon-comment-border-color: rgba(0, 0, 0, 0.4);
    --mastodon-comment-user-color: #939393;

    --mastodon-comment-status--inactive: #5d686f;
    --mastodon-comment-status-replies--active: #448aff;
    --mastodon-comment-status-favourite--active: #ff9100;
    --mastodon-comment-status-reblog--active: #00c853;
  }

  @media only screen and (max-width: 1024px){
    :root{
      --mastodon-comment-indent: 20px;
    }
  }

  @media only screen and (max-width: 640px){
    :root{
      --mastodon-comment-indent: 0px;
    }
  }

  .mastodon-comment{
    background-color: var(--mastodon-comment-bg-color);
    border-radius: var(--mastodon-comment-border-radius);
    border: 1px var(--mastodon-comment-border-color) solid;
    padding: 20px;
    margin-bottom: 1.5rem;
    display: flex;
    flex-direction: column;
  }

  .mastodon-comment p{
    margin-bottom: 0px;
  }

  .mastodon-comment .content{
    margin: 15px 20px;
  }

  .mastodon-comment .content p:first-child{
    margin-top: 0;
    margin-bottom: 0;
  }

  .mastodon-comment .attachments{
    max-width: 0px 10px;
  }

  .mastodon-comment .attachments > *{
    max-width: 0px 10px;
  }

  .mastodon-comment .author{
    padding-top: 0;
    display: flex;
  }

  .mastodon-comment .author a{
    text-decoration: none;
  }

  .mastodon-comment .author .avatar img{
    margin-right: 1rem;
    min-width: 60px;
    border-radius: 5px;
  }

  .mastodon-comment .author .details{
    display: flex;
    flex-direction: column;
  }

  .mastodon-comment .author .details .name{
    font-weight: bold;
  }

  .mastodon-comment .author .details .user{
    color: var(--mastodon-comment-user-color);
  }

  .mastodon-comment .author .date{
    margin-left: auto;
    font-size: small;
  }

  .mastodon-comment .status > div{
    display: inline-block;
    margin-right: 15px;
  }

  .mastodon-comment .status a{
    color: var(--mastodon-comment-status--inactive);
    text-decoration: none;
  }

  .mastodon-comment .status .twemoji.replies.active a{
    color: var(--mastodon-comment-status-replies--active);
  }

  .mastodon-comment .status .twemoji.reblogs.active a{
    color: var(--mastodon-comment-status-reblog--active);
  }

  .mastodon-comment .status .twemoji.favourites.active a{
    color: var(--mastodon-comment-status-favourite--active);
  }

  .mastodon-comment .status svg{
    margin-right: 0.2rem;
    vertical-align: middle;
  }

En debut de fichier.md sur lequel vous voulez les commentaires ajouter ces 2 lignes :

comment_id: 12345....6789
comments: true
Le comment_id est trouver en copiant le lien d'un poste mastodon https://fosstodon.org/@Fl0r1an/12345....6789

Commentez cet article de blog en utilisant un compte compatible Fediverse (Mastodon ou similaire).