diff --git a/code/comments.js b/code/comments.js
new file mode 100644
index 00000000..bea910d9
--- /dev/null
+++ b/code/comments.js
@@ -0,0 +1,163 @@
+import * as params from '@params';
+
+function escapeHtml(unsafe) {
+ return unsafe
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
+ function emojify(input, emojis) {
+ let output = input;
+
+ emojis.forEach(emoji => {
+ let picture = document.createElement("picture");
+
+ let source = document.createElement("source");
+ source.setAttribute("srcset", escapeHtml(emoji.url));
+ source.setAttribute("media", "(prefers-reduced-motion: no-preference)");
+
+ let img = document.createElement("img");
+ img.className = "emoji";
+ img.setAttribute("src", escapeHtml(emoji.static_url));
+ img.setAttribute("alt", `:${emoji.shortcode}:`);
+ img.setAttribute("title", `:${emoji.shortcode}:`);
+ img.setAttribute("width", "20");
+ img.setAttribute("height", "20");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ output = output.replace(`:${emoji.shortcode}:`, picture.outerHTML);
+ });
+
+ return output;
+ }
+
+ function loadComments() {
+ let commentsWrapper = document.getElementById("comments-wrapper");
+ document.getElementById("load-comment").innerHTML = "Chargement";
+ fetch(`https://${params.host}/api/v1/statuses/${params.id}/context`)
+ .then(function (response) {
+ return response.json();
+ })
+ .then(function (data) {
+ document.getElementById("load-comment").innerHTML = "Charger les commentaires";
+
+ if (data.error) {
+ commentsWrapper.innerHTML = "Une erreur est survenue pendant le chargement des commentaires.";
+ return;
+ }
+
+ let descendants = data['descendants'];
+ if (!descendants || !Array.isArray(descendants) || descendants.length <= 0) {
+ commentsWrapper.innerHTML = "Pas encore de commentaires.";
+ return;
+ }
+
+ commentsWrapper.innerHTML = "";
+
+ descendants.forEach(function (status) {
+ if (status.account.display_name.length > 0) {
+ status.account.display_name = escapeHtml(status.account.display_name);
+ status.account.display_name = emojify(status.account.display_name, status.account.emojis);
+ } else {
+ status.account.display_name = status.account.username;
+ };
+
+ let instance = "";
+ if (status.account.acct.includes("@")) {
+ instance = status.account.acct.split("@")[1];
+ } else {
+ instance = params.host;
+ }
+
+ const isReply = status.in_reply_to_id !== params.id;
+
+ status.content = emojify(status.content, status.emojis);
+
+ let avatarSource = document.createElement("source");
+ avatarSource.setAttribute("srcset", escapeHtml(status.account.avatar));
+ avatarSource.setAttribute("media", "(prefers-reduced-motion: no-preference)");
+
+ let avatarImg = document.createElement("img");
+ avatarImg.className = "avatar";
+ avatarImg.setAttribute("src", escapeHtml(status.account.avatar_static));
+ avatarImg.setAttribute("alt", `@${status.account.username}@${instance} avatar`);
+
+ let avatarPicture = document.createElement("picture");
+ avatarPicture.appendChild(avatarSource);
+ avatarPicture.appendChild(avatarImg);
+
+ let avatar = document.createElement("a");
+ avatar.className = "avatar-link";
+ avatar.setAttribute("href", status.account.url);
+ avatar.setAttribute("rel", "external nofollow");
+ avatar.setAttribute("title", `Voir le profil sur @${status.account.username}@${instance}`);
+ avatar.appendChild(avatarPicture);
+
+ let instanceBadge = document.createElement("a");
+ instanceBadge.className = "instance";
+ instanceBadge.setAttribute("href", status.account.url);
+ instanceBadge.setAttribute("title", `@${status.account.username}@${instance}`);
+ instanceBadge.setAttribute("rel", "external nofollow");
+ instanceBadge.textContent = instance;
+
+ let display = document.createElement("span");
+ display.className = "display";
+ display.setAttribute("itemprop", "author");
+ display.setAttribute("itemtype", "http://schema.org/Person");
+ display.innerHTML = status.account.display_name;
+
+ let header = document.createElement("header");
+ header.className = "author";
+ header.appendChild(display);
+ header.appendChild(instanceBadge);
+
+ let permalink = document.createElement("a");
+ permalink.setAttribute("href", status.url);
+ permalink.setAttribute("itemprop", "url");
+ permalink.setAttribute("title", `Voir le commentaire sur ${instance}`);
+ permalink.setAttribute("rel", "external nofollow");
+ permalink.textContent = new Date(status.created_at).toLocaleString('fr-FR', {
+ dateStyle: "long",
+ timeStyle: "short",
+ });
+
+ let timestamp = document.createElement("time");
+ timestamp.setAttribute("datetime", status.created_at);
+ timestamp.appendChild(permalink);
+
+ let main = document.createElement("main");
+ main.setAttribute("itemprop", "text");
+ main.innerHTML = status.content;
+
+ let interactions = document.createElement("footer");
+ if (status.favourites_count > 0) {
+ let faves = document.createElement("a");
+ faves.className = "faves";
+ faves.setAttribute("href", `${status.url}/favourites`);
+ faves.setAttribute("title", `Favoris depuis ${instance}`);
+ faves.textContent = status.favourites_count;
+
+ interactions.appendChild(faves);
+ }
+
+ let comment = document.createElement("article");
+ comment.id = `comment-${status.id}`;
+ comment.className = isReply ? "comment comment-reply" : "comment";
+ comment.setAttribute("itemprop", "comment");
+ comment.setAttribute("itemtype", "http://schema.org/Comment");
+ comment.appendChild(avatar);
+ comment.appendChild(header);
+ comment.appendChild(timestamp);
+ comment.appendChild(main);
+ comment.appendChild(interactions);
+
+ commentsWrapper.innerHTML += DOMPurify.sanitize(comment.outerHTML);
+ });
+ });
+ }
+
+ document.getElementById("load-comment").addEventListener("click", loadComments);
\ No newline at end of file
diff --git a/code/purify.min.js b/code/purify.min.js
new file mode 100644
index 00000000..23f67903
--- /dev/null
+++ b/code/purify.min.js
@@ -0,0 +1,3 @@
+/*! @license DOMPurify 3.0.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.11/LICENSE */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t,n){return e.apply(t,n)}),s||(s=function(e,t){return new e(...t)});const u=b(Array.prototype.forEach),m=b(Array.prototype.pop),f=b(Array.prototype.push),p=b(String.prototype.toLowerCase),d=b(String.prototype.toString),h=b(String.prototype.match),g=b(String.prototype.replace),T=b(String.prototype.indexOf),y=b(String.prototype.trim),E=b(Object.prototype.hasOwnProperty),A=b(RegExp.prototype.test),_=(N=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:p;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function R(e){for(let t=0;t/gm),B=a(/\${[\w\W]*}/gm),W=a(/^data-[\-\w.\u00B7-\uFFFF]/),G=a(/^aria-[\-\w]+$/),Y=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),q=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),X=a(/^html$/i),$=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var K=Object.freeze({__proto__:null,MUSTACHE_EXPR:F,ERB_EXPR:z,TMPLIT_EXPR:B,DATA_ATTR:W,ARIA_ATTR:G,IS_ALLOWED_URI:Y,IS_SCRIPT_OR_DATA:j,ATTR_WHITESPACE:q,DOCTYPE_NAME:X,CUSTOM_ELEMENT:$});const V=function(){return"undefined"==typeof window?null:window},Z=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}};var J=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:V();const o=e=>t(e);if(o.version="3.0.11",o.removed=[],!n||!n.document||9!==n.document.nodeType)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:b,Element:R,NodeFilter:F,NamedNodeMap:z=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:B,DOMParser:W,trustedTypes:G}=n,j=R.prototype,q=D(j,"cloneNode"),$=D(j,"nextSibling"),J=D(j,"childNodes"),Q=D(j,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let ee,te="";const{implementation:ne,createNodeIterator:oe,createDocumentFragment:re,getElementsByTagName:ie}=r,{importNode:ae}=a;let le={};o.isSupported="function"==typeof e&&"function"==typeof Q&&ne&&void 0!==ne.createHTMLDocument;const{MUSTACHE_EXPR:ce,ERB_EXPR:se,TMPLIT_EXPR:ue,DATA_ATTR:me,ARIA_ATTR:fe,IS_SCRIPT_OR_DATA:pe,ATTR_WHITESPACE:de,CUSTOM_ELEMENT:he}=K;let{IS_ALLOWED_URI:ge}=K,Te=null;const ye=S({},[...L,...C,...O,...v,...I]);let Ee=null;const Ae=S({},[...M,...U,...P,...H]);let _e=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ne=null,be=null,Se=!0,Re=!0,we=!1,De=!0,Le=!1,Ce=!1,Oe=!1,xe=!1,ve=!1,ke=!1,Ie=!1,Me=!0,Ue=!1;const Pe="user-content-";let He=!0,Fe=!1,ze={},Be=null;const We=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ge=null;const Ye=S({},["audio","video","img","source","image","track"]);let je=null;const qe=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Xe="http://www.w3.org/1998/Math/MathML",$e="http://www.w3.org/2000/svg",Ke="http://www.w3.org/1999/xhtml";let Ve=Ke,Ze=!1,Je=null;const Qe=S({},[Xe,$e,Ke],d);let et=null;const tt=["application/xhtml+xml","text/html"],nt="text/html";let ot=null,rt=null;const it=r.createElement("form"),at=function(e){return e instanceof RegExp||e instanceof Function},lt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!rt||rt!==e){if(e&&"object"==typeof e||(e={}),e=w(e),et=-1===tt.indexOf(e.PARSER_MEDIA_TYPE)?nt:e.PARSER_MEDIA_TYPE,ot="application/xhtml+xml"===et?d:p,Te=E(e,"ALLOWED_TAGS")?S({},e.ALLOWED_TAGS,ot):ye,Ee=E(e,"ALLOWED_ATTR")?S({},e.ALLOWED_ATTR,ot):Ae,Je=E(e,"ALLOWED_NAMESPACES")?S({},e.ALLOWED_NAMESPACES,d):Qe,je=E(e,"ADD_URI_SAFE_ATTR")?S(w(qe),e.ADD_URI_SAFE_ATTR,ot):qe,Ge=E(e,"ADD_DATA_URI_TAGS")?S(w(Ye),e.ADD_DATA_URI_TAGS,ot):Ye,Be=E(e,"FORBID_CONTENTS")?S({},e.FORBID_CONTENTS,ot):We,Ne=E(e,"FORBID_TAGS")?S({},e.FORBID_TAGS,ot):{},be=E(e,"FORBID_ATTR")?S({},e.FORBID_ATTR,ot):{},ze=!!E(e,"USE_PROFILES")&&e.USE_PROFILES,Se=!1!==e.ALLOW_ARIA_ATTR,Re=!1!==e.ALLOW_DATA_ATTR,we=e.ALLOW_UNKNOWN_PROTOCOLS||!1,De=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,Le=e.SAFE_FOR_TEMPLATES||!1,Ce=e.WHOLE_DOCUMENT||!1,ve=e.RETURN_DOM||!1,ke=e.RETURN_DOM_FRAGMENT||!1,Ie=e.RETURN_TRUSTED_TYPE||!1,xe=e.FORCE_BODY||!1,Me=!1!==e.SANITIZE_DOM,Ue=e.SANITIZE_NAMED_PROPS||!1,He=!1!==e.KEEP_CONTENT,Fe=e.IN_PLACE||!1,ge=e.ALLOWED_URI_REGEXP||Y,Ve=e.NAMESPACE||Ke,_e=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&at(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(_e.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&at(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(_e.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(_e.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Le&&(Re=!1),ke&&(ve=!0),ze&&(Te=S({},I),Ee=[],!0===ze.html&&(S(Te,L),S(Ee,M)),!0===ze.svg&&(S(Te,C),S(Ee,U),S(Ee,H)),!0===ze.svgFilters&&(S(Te,O),S(Ee,U),S(Ee,H)),!0===ze.mathMl&&(S(Te,v),S(Ee,P),S(Ee,H))),e.ADD_TAGS&&(Te===ye&&(Te=w(Te)),S(Te,e.ADD_TAGS,ot)),e.ADD_ATTR&&(Ee===Ae&&(Ee=w(Ee)),S(Ee,e.ADD_ATTR,ot)),e.ADD_URI_SAFE_ATTR&&S(je,e.ADD_URI_SAFE_ATTR,ot),e.FORBID_CONTENTS&&(Be===We&&(Be=w(Be)),S(Be,e.FORBID_CONTENTS,ot)),He&&(Te["#text"]=!0),Ce&&S(Te,["html","head","body"]),Te.table&&(S(Te,["tbody"]),delete Ne.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw _('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw _('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');ee=e.TRUSTED_TYPES_POLICY,te=ee.createHTML("")}else void 0===ee&&(ee=Z(G,c)),null!==ee&&"string"==typeof te&&(te=ee.createHTML(""));i&&i(e),rt=e}},ct=S({},["mi","mo","mn","ms","mtext"]),st=S({},["foreignobject","desc","title","annotation-xml"]),ut=S({},["title","style","font","a","script"]),mt=S({},[...C,...O,...x]),ft=S({},[...v,...k]),pt=function(e){let t=Q(e);t&&t.tagName||(t={namespaceURI:Ve,tagName:"template"});const n=p(e.tagName),o=p(t.tagName);return!!Je[e.namespaceURI]&&(e.namespaceURI===$e?t.namespaceURI===Ke?"svg"===n:t.namespaceURI===Xe?"svg"===n&&("annotation-xml"===o||ct[o]):Boolean(mt[n]):e.namespaceURI===Xe?t.namespaceURI===Ke?"math"===n:t.namespaceURI===$e?"math"===n&&st[o]:Boolean(ft[n]):e.namespaceURI===Ke?!(t.namespaceURI===$e&&!st[o])&&(!(t.namespaceURI===Xe&&!ct[o])&&(!ft[n]&&(ut[n]||!mt[n]))):!("application/xhtml+xml"!==et||!Je[e.namespaceURI]))},dt=function(e){f(o.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.remove()}},ht=function(e,t){try{f(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){f(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Ee[e])if(ve||ke)try{dt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},gt=function(e){let t=null,n=null;if(xe)e=" "+e;else{const t=h(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===et&&Ve===Ke&&(e=''+e+"");const o=ee?ee.createHTML(e):e;if(Ve===Ke)try{t=(new W).parseFromString(o,et)}catch(e){}if(!t||!t.documentElement){t=ne.createDocument(Ve,"template",null);try{t.documentElement.innerHTML=Ze?te:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),Ve===Ke?ie.call(t,Ce?"html":"body")[0]:Ce?t.documentElement:i},Tt=function(e){return oe.call(e.ownerDocument||e,e,F.SHOW_ELEMENT|F.SHOW_COMMENT|F.SHOW_TEXT|F.SHOW_PROCESSING_INSTRUCTION|F.SHOW_CDATA_SECTION,null)},yt=function(e){return e instanceof B&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof z)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},Et=function(e){return"function"==typeof b&&e instanceof b},At=function(e,t,n){le[e]&&u(le[e],(e=>{e.call(o,t,n,rt)}))},_t=function(e){let t=null;if(At("beforeSanitizeElements",e,null),yt(e))return dt(e),!0;const n=ot(e.nodeName);if(At("uponSanitizeElement",e,{tagName:n,allowedTags:Te}),e.hasChildNodes()&&!Et(e.firstElementChild)&&A(/<[/\w]/g,e.innerHTML)&&A(/<[/\w]/g,e.textContent))return dt(e),!0;if(7===e.nodeType)return dt(e),!0;if(!Te[n]||Ne[n]){if(!Ne[n]&&bt(n)){if(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,n))return!1;if(_e.tagNameCheck instanceof Function&&_e.tagNameCheck(n))return!1}if(He&&!Be[n]){const t=Q(e)||e.parentNode,n=J(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o)t.insertBefore(q(n[o],!0),$(e))}}return dt(e),!0}return e instanceof R&&!pt(e)?(dt(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!A(/<\/no(script|embed|frames)/i,e.innerHTML)?(Le&&3===e.nodeType&&(t=e.textContent,u([ce,se,ue],(e=>{t=g(t,e," ")})),e.textContent!==t&&(f(o.removed,{element:e.cloneNode()}),e.textContent=t)),At("afterSanitizeElements",e,null),!1):(dt(e),!0)},Nt=function(e,t,n){if(Me&&("id"===t||"name"===t)&&(n in r||n in it))return!1;if(Re&&!be[t]&&A(me,t));else if(Se&&A(fe,t));else if(!Ee[t]||be[t]){if(!(bt(e)&&(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,e)||_e.tagNameCheck instanceof Function&&_e.tagNameCheck(e))&&(_e.attributeNameCheck instanceof RegExp&&A(_e.attributeNameCheck,t)||_e.attributeNameCheck instanceof Function&&_e.attributeNameCheck(t))||"is"===t&&_e.allowCustomizedBuiltInElements&&(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,n)||_e.tagNameCheck instanceof Function&&_e.tagNameCheck(n))))return!1}else if(je[t]);else if(A(ge,g(n,de,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==T(n,"data:")||!Ge[e]){if(we&&!A(pe,g(n,de,"")));else if(n)return!1}else;return!0},bt=function(e){return"annotation-xml"!==e&&h(e,he)},St=function(e){At("beforeSanitizeAttributes",e,null);const{attributes:t}=e;if(!t)return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Ee};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=ot(a);let f="value"===a?c:y(c);if(n.attrName=s,n.attrValue=f,n.keepAttr=!0,n.forceKeepAttr=void 0,At("uponSanitizeAttribute",e,n),f=n.attrValue,n.forceKeepAttr)continue;if(ht(a,e),!n.keepAttr)continue;if(!De&&A(/\/>/i,f)){ht(a,e);continue}Le&&u([ce,se,ue],(e=>{f=g(f,e," ")}));const p=ot(e.nodeName);if(Nt(p,s,f)){if(!Ue||"id"!==s&&"name"!==s||(ht(a,e),f=Pe+f),ee&&"object"==typeof G&&"function"==typeof G.getAttributeType)if(l);else switch(G.getAttributeType(p,s)){case"TrustedHTML":f=ee.createHTML(f);break;case"TrustedScriptURL":f=ee.createScriptURL(f)}try{l?e.setAttributeNS(l,a,f):e.setAttribute(a,f),m(o.removed)}catch(e){}}}At("afterSanitizeAttributes",e,null)},Rt=function e(t){let n=null;const o=Tt(t);for(At("beforeSanitizeShadowDOM",t,null);n=o.nextNode();)At("uponSanitizeShadowNode",n,null),_t(n)||(n.content instanceof s&&e(n.content),St(n));At("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(Ze=!e,Ze&&(e="\x3c!--\x3e"),"string"!=typeof e&&!Et(e)){if("function"!=typeof e.toString)throw _("toString is not a function");if("string"!=typeof(e=e.toString()))throw _("dirty is not a string, aborting")}if(!o.isSupported)return e;if(Oe||lt(t),o.removed=[],"string"==typeof e&&(Fe=!1),Fe){if(e.nodeName){const t=ot(e.nodeName);if(!Te[t]||Ne[t])throw _("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof b)n=gt("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),1===r.nodeType&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!ve&&!Le&&!Ce&&-1===e.indexOf("<"))return ee&&Ie?ee.createHTML(e):e;if(n=gt(e),!n)return ve?null:Ie?te:""}n&&xe&&dt(n.firstChild);const c=Tt(Fe?e:n);for(;i=c.nextNode();)_t(i)||(i.content instanceof s&&Rt(i.content),St(i));if(Fe)return e;if(ve){if(ke)for(l=re.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(Ee.shadowroot||Ee.shadowrootmode)&&(l=ae.call(a,l,!0)),l}let m=Ce?n.outerHTML:n.innerHTML;return Ce&&Te["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&A(X,n.ownerDocument.doctype.name)&&(m="\n"+m),Le&&u([ce,se,ue],(e=>{m=g(m,e," ")})),ee&&Ie?ee.createHTML(m):m},o.setConfig=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};lt(e),Oe=!0},o.clearConfig=function(){rt=null,Oe=!1},o.isValidAttribute=function(e,t,n){rt||lt({});const o=ot(e),r=ot(t);return Nt(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&(le[e]=le[e]||[],f(le[e],t))},o.removeHook=function(e){if(le[e])return m(le[e])},o.removeHooks=function(e){le[e]&&(le[e]=[])},o.removeAllHooks=function(){le={}},o}();return J}));
+//# sourceMappingURL=purify.min.js.map
diff --git a/www/assets/css/comments.css b/www/assets/css/comments.css
new file mode 100644
index 00000000..a4df359f
--- /dev/null
+++ b/www/assets/css/comments.css
@@ -0,0 +1,112 @@
+/**
+* ╭───────────────────────────────────────────────────────────────╮
+* │ Comments │
+* ╰───────────────────────────────────────────────────────────────╯
+**/
+
+#comments-wrapper {
+ margin: 1.5em 0;
+ padding: 0 var(25px);
+}
+
+.comment {
+ display: grid;
+ column-gap: 1rem;
+ grid-template-areas: "avatar name" "avatar time" "avatar post" "...... interactions";
+ grid-template-columns: min-content;
+ justify-items: start;
+ margin: 0 auto 0 -1em;
+ padding: .5em;
+}
+
+.comment.comment-reply {
+ margin: 0 auto 0 1em;
+}
+
+.comment .avatar-link {
+ grid-area: avatar;
+ height: 4rem;
+ position: relative;
+ width: 4rem;
+}
+
+.comment .avatar-link .avatar {
+ height: 100%;
+ width: 100%;
+}
+
+.comment .author {
+ align-items: center;
+ display: flex;
+ font-weight: 700;
+ gap: .5em;
+ grid-area: name;
+}
+
+.comment .author .instance {
+ background-color: var(--bg-accent);
+ color: var(--fg-accent);
+ border-radius: 9999px;
+ font-size: smaller;
+ font-weight: 400;
+ padding: .25em .75em;
+}
+
+.comment .author .instance:hover {
+ opacity: .8;
+ text-decoration: none;
+}
+
+.comment time {
+ grid-area: time;
+ line-height: 3.5rem;
+}
+
+.comment main {
+ grid-area: post;
+}
+
+.comment main p:first-child {
+ margin-top: .25em;
+}
+
+.comment main p:last-child {
+ margin-bottom: 0;
+}
+
+.comment footer {
+ grid-area: interactions;
+ border: none;
+ margin-top: 1em;
+}
+
+.comment footer .faves {
+ color: inheritE
+}
+
+.comment footer .faves:hover {
+ opacity: .8;
+ text-decoration: none;
+}
+
+.comment footer .faves::before {
+ color: red;
+ content: "♥";
+ font-size: 1rem;
+ margin-inline-end: .25em;
+}
+
+.comment .emoji {
+ display: inline;
+ height: 1.25em;
+ vertical-align: middle;
+ width: 1.25em;
+}
+
+.comment .invisible {
+ display: none;
+}
+
+.comment .ellipsis::after {
+ content: "…";
+}
\ No newline at end of file
diff --git a/www/assets/css/theme.css b/www/assets/css/theme.css
index 159eed54..f2318c00 100644
--- a/www/assets/css/theme.css
+++ b/www/assets/css/theme.css
@@ -377,4 +377,4 @@ code { /* character, digrams, trigrams */
header + nav li:has(.active) a.active {
color: var(--fg-banner);
}
-}
+}
\ No newline at end of file
diff --git a/www/content/articles/bienvenue.md b/www/content/articles/bienvenue.md
index a5cc79ac..51f8b42f 100644
--- a/www/content/articles/bienvenue.md
+++ b/www/content/articles/bienvenue.md
@@ -3,6 +3,10 @@ title = "Bienvenue chez les Ergonautes !"
date = 2024-03-18T22:01:23+01:00
author = "nuclear_squid"
tags = ["communauté"]
+[comments]
+ host = "mastodon.social"
+ username = "fabi1cazenave"
+ id = "112124416010685631"
+++
Après plus de quatre ans de travail et avec une version 1.0 en approche, il
diff --git a/www/layouts/_default/single.html b/www/layouts/_default/single.html
index 9331f08a..76781562 100644
--- a/www/layouts/_default/single.html
+++ b/www/layouts/_default/single.html
@@ -14,4 +14,8 @@
{{- end -}}
+ {{ with .Params.comments }}
+ {{ partial "comments.html" . }}
+ {{ end }}
+
{{ end }}
diff --git a/www/layouts/partials/comments.html b/www/layouts/partials/comments.html
new file mode 100644
index 00000000..52bc24f5
--- /dev/null
+++ b/www/layouts/partials/comments.html
@@ -0,0 +1,46 @@
+{{- with resources.Get "css/comments.css" }}
+ {{- if eq hugo.Environment "development" }}
+
+ {{- else }}
+ {{- with . | minify | fingerprint }}
+
+ {{- end }}
+ {{- end }}
+{{- end }}
+
+{{- with resources.Get "js/purify.min.js" }}
+ {{- if eq hugo.Environment "development" }}
+
+ {{- else }}
+ {{- with . | minify | fingerprint }}
+
+ {{- end }}
+ {{- end }}
+{{- end }}
+
+
+ Commentaires
+ Avec un compte sur le Fediverse ou Mastodon, vous pouvez répondre à ce post . Mastodon étant décentralisé, vous pouvez utiliser
+ votre compte existant hébergé sur un autre serveur Mastodon ou une plateforme compatible si vous n'avez pas de
+ compte sur celui-ci.
+
+
+
+
+ {{- with resources.Get "js/comments.js" | js.Build (dict "params" (dict "host" .host "id" .id)) }}
+ {{- if eq hugo.Environment "development" }}
+
+ {{- else }}
+ {{- with . | minify | fingerprint }}
+
+ {{- end }}
+ {{- end }}
+ {{- end }}
+
+
\ No newline at end of file
Commentaires
+Avec un compte sur le Fediverse ou Mastodon, vous pouvez répondre à ce post. Mastodon étant décentralisé, vous pouvez utiliser + votre compte existant hébergé sur un autre serveur Mastodon ou une plateforme compatible si vous n'avez pas de + compte sur celui-ci.
+ + +