summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.well-known/host-meta.xml4
-rw-r--r--_comments.sh3
-rw-r--r--_config.yml5
-rw-r--r--_data/news.yml2
-rw-r--r--_includes/commentNotice.html35
-rw-r--r--_includes/header.html1
-rw-r--r--_layouts/postlist.html3
-rw-r--r--_posts/2021-03-23-winvm.md1
-rw-r--r--_posts/2021-04-25-networkstuff.md1
-rw-r--r--_posts/2021-06-03-fakir-teardown.md1
-rw-r--r--_posts/2021-06-27-wpa-enterprise-unifi.md1
-rw-r--r--_posts/2021-09-22-problems-updating-proxmox.md1
-rw-r--r--_posts/2021-10-28-rant-devuan-upgrade.md1
-rw-r--r--_posts/2022-01-20-server-stuff.md2
-rw-r--r--_posts/2022-04-03-backporting-pipewire-in-devuan.md2
-rw-r--r--_posts/2024-09-04-convention-report-east-12-2024.md1
-rw-r--r--assets/main.scss6
-rw-r--r--assets/mscomm/comments.js311
-rw-r--r--assets/mscomm/styles.css92
-rw-r--r--dn42.md2
-rw-r--r--projects.md31
-rw-r--r--readme.txt3
22 files changed, 497 insertions, 12 deletions
diff --git a/.well-known/host-meta.xml b/.well-known/host-meta.xml
new file mode 100644
index 0000000..3f156a0
--- /dev/null
+++ b/.well-known/host-meta.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
+ <Link rel="lrdd" type="application/json" template="https://blog.uvokchee.de/.well-known/webfinger?resource={uri}"></Link>
+ </XRD> \ No newline at end of file
diff --git a/_comments.sh b/_comments.sh
new file mode 100644
index 0000000..2dc6c85
--- /dev/null
+++ b/_comments.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+#scp hetzner:/var/www/hatsu/hatsu.sqlite3 .
+sqlite3 -box hatsu.sqlite3 'SELECT id, in_reply_to_root FROM post where in_reply_to IS NOT NULL;'
diff --git a/_config.yml b/_config.yml
index 80f282c..cc4ac74 100644
--- a/_config.yml
+++ b/_config.yml
@@ -71,4 +71,7 @@ autopages:
collections:
enabled: false
-cssversion: "2023120301"
+cssversion: "2024010301"
+
+feed:
+ posts_limit: 20
diff --git a/_data/news.yml b/_data/news.yml
index 4684514..0e64621 100644
--- a/_data/news.yml
+++ b/_data/news.yml
@@ -2,7 +2,7 @@
content: >-
<div>
<div style="font-weight: bold">You don't seem to be using an ad blocker.</div>
- <div>Please consider installing one.</div>
+ <div>Please consider installing one. I would recommend uBlock Origin.</div>
</div>
- id: news1
content: >-
diff --git a/_includes/commentNotice.html b/_includes/commentNotice.html
index b1be501..a46dadf 100644
--- a/_includes/commentNotice.html
+++ b/_includes/commentNotice.html
@@ -1,19 +1,44 @@
<section>
+ {% assign base_url = 'https://blog.uvokchee.de/notice/' %}
+ {% assign url = page.url %}
+ {% assign domain = 'https://blog.uvokchee.de' %}
+ {% assign full_url = domain | append: page.url %}
+ {% capture encoded_url %}{{ full_url | base64_encode }}{% endcapture %}
+ {% assign src_url = base_url | append: encoded_url %}
+
<h2>Kommentare / Comments</h2>
- <p>Kommentare werden von mir selbst auf einem anderen Server über <a href="https://isso-comments.de/">Isso</a> gehostet.</p>
+ <h3>Isso</h3>
+
+ <p>Kommentare werden von mir selbst auf einem anderen Server über <a href="https://isso-comments.de/">Isso</a>
+ gehostet.</p>
<p>Comments are hosted by myself on another server, powered by <a href="https://isso-comments.de/">Isso</a>.</p>
- <script data-isso="//c.uvokchee.de/"
- data-isso-require-author=true
- data-isso-vote=false
+ <script data-isso="//c.uvokchee.de/" data-isso-require-author=true data-isso-vote=false
src="//c.uvokchee.de/js/embed.min.js"></script>
<section id="isso-thread">
{% if jekyll.environment == "development" %}
{% include testc.html %}
{% endif %}
</section>
+
+ <h3>Fediverse</h3>
+ <script type="module">
+ import Comments from '{{ "/assets/mscomm/comments.js" | relative_url }}'
+ export function loadComments() {
+ document.getElementById('load-comments-btn').remove();
+ customElements.define('oom-comments', Comments);
+ }
+ window.loadComments = loadComments;
+ </script>
+ <link rel="stylesheet" href="{{ "/assets/mscomm/styles.css" | relative_url }}?v={{ site.cssversion }}">
+ <oom-comments src="{{ src_url }}">
+ <button id="load-comments-btn" class="load-comments-btn" onclick="loadComments()">Load Comments from
+ ActivityPub</button>
+ </oom-comments>
+ <p style="margin-top: 1em"><a href="{{ src_url }}">ActivityPub-Link</a></p>
+
<noscript>
<p>Um Kommentare zu hinterlassen, ist leider JavaScript nötig.</p>
- <p>Unfortunately, JavaScript is required to leave comments</p>
+ <p>Unfortunately, JavaScript is required to leave comments.</p>
</noscript>
</section>
diff --git a/_includes/header.html b/_includes/header.html
index 492dcd5..86e812e 100644
--- a/_includes/header.html
+++ b/_includes/header.html
@@ -13,7 +13,6 @@
</label>
<div class="trigger" lang="en">
{% include_cached navlinks.html %}
- <a class="page-link" href="https://uvokchee.de/wiki/">Wiki</a>
<a class="rss-subscribe page-link" href="{{ "/feed.xml" | relative_url }}">RSS feed</a>
</div>
</nav>
diff --git a/_layouts/postlist.html b/_layouts/postlist.html
index 67a1d48..52d07df 100644
--- a/_layouts/postlist.html
+++ b/_layouts/postlist.html
@@ -39,6 +39,9 @@ layout: default
{%- elsif site.show_excerpts -%}
{{ post.excerpt }}
{%- assign showsep = true -%}
+ {%- elsif post.description -%}
+ {{ post.description }}
+ {%- assign showsep = true -%}
{%- endif -%}
</div>
</li>
diff --git a/_posts/2021-03-23-winvm.md b/_posts/2021-03-23-winvm.md
index 528d636..3ee73f4 100644
--- a/_posts/2021-03-23-winvm.md
+++ b/_posts/2021-03-23-winvm.md
@@ -4,6 +4,7 @@ title: Windows VM under Devuan with QEMU / libvirt
date: 2021-03-23 20:32 +0100
categories: tech
description: How to run a Windows VM under Devuan with QEMU / libvirt
+lang: en
---
So, I'm running Devuan Beowulf (Debian Buster based). Recently, I was in the need of running a
diff --git a/_posts/2021-04-25-networkstuff.md b/_posts/2021-04-25-networkstuff.md
index 3152f3f..7b2cff6 100644
--- a/_posts/2021-04-25-networkstuff.md
+++ b/_posts/2021-04-25-networkstuff.md
@@ -4,6 +4,7 @@ title: Network stuff
date: 2021-04-25 21:50 +0200
categories: tech
description: "I do some experiments with networking stuff. Proxmox and running OpenWRT in a VM."
+lang: en
---
Been a long time.
diff --git a/_posts/2021-06-03-fakir-teardown.md b/_posts/2021-06-03-fakir-teardown.md
index 1de8bf9..75f609d 100644
--- a/_posts/2021-06-03-fakir-teardown.md
+++ b/_posts/2021-06-03-fakir-teardown.md
@@ -3,6 +3,7 @@ layout: post
title: Fakir Robert Teardown
date: 2021-06-03 20:39 +0200
description: Ein Teardown eines alten Staubsauger-Roboters
+lang: de
---
Da ich mir demnächst mal einen neuen gebrauchten Saugroboter holen werde,
diff --git a/_posts/2021-06-27-wpa-enterprise-unifi.md b/_posts/2021-06-27-wpa-enterprise-unifi.md
index 61c49a7..36b9e6e 100644
--- a/_posts/2021-06-27-wpa-enterprise-unifi.md
+++ b/_posts/2021-06-27-wpa-enterprise-unifi.md
@@ -4,6 +4,7 @@ title: WPA Enterprise mit Unifi Access Points
date: 2021-06-27 20:28 +0200
categories: tech
description: "Wie man WPA Enterprise einrichtet, mit Unifi WiFi Access Points und Software, die unter Linux läuft"
+lang: de
---
Ich hab mal einen kurzen Artikel geschrieben, wie man WPA Enterprise
diff --git a/_posts/2021-09-22-problems-updating-proxmox.md b/_posts/2021-09-22-problems-updating-proxmox.md
index 3885b79..7353d67 100644
--- a/_posts/2021-09-22-problems-updating-proxmox.md
+++ b/_posts/2021-09-22-problems-updating-proxmox.md
@@ -2,6 +2,7 @@
layout: post
title: Network gone after updating Proxmox
date: 2021-09-22 20:59 +0200
+lang: en
---
So, after upgrading Proxmox from version 6.x to 7.x, the network
diff --git a/_posts/2021-10-28-rant-devuan-upgrade.md b/_posts/2021-10-28-rant-devuan-upgrade.md
index 100dd3b..c3da9b0 100644
--- a/_posts/2021-10-28-rant-devuan-upgrade.md
+++ b/_posts/2021-10-28-rant-devuan-upgrade.md
@@ -3,6 +3,7 @@ layout: post
title: 'Rant: Devuan Upgrade'
date: 2021-10-28 19:58 +0200
categories: tech
+lang: en
---
Gna. So I upgraded my Devuan installation from Beowulf to Chimaera. Process was a bit tricky,
diff --git a/_posts/2022-01-20-server-stuff.md b/_posts/2022-01-20-server-stuff.md
index 40d39a1..f5f053f 100644
--- a/_posts/2022-01-20-server-stuff.md
+++ b/_posts/2022-01-20-server-stuff.md
@@ -3,7 +3,7 @@ layout: post
title: Server stuff
date: 2022-01-20 18:55 +0100
category: tech
-language: en
+lang: en
description: "I experiment with SPF, DMARC and DKIM."
---
diff --git a/_posts/2022-04-03-backporting-pipewire-in-devuan.md b/_posts/2022-04-03-backporting-pipewire-in-devuan.md
index 1d20df2..c0a9fe3 100644
--- a/_posts/2022-04-03-backporting-pipewire-in-devuan.md
+++ b/_posts/2022-04-03-backporting-pipewire-in-devuan.md
@@ -3,7 +3,7 @@ layout: post
title: Backporting Pipewire in Devuan
date: 2022-04-03 17:45 +0200
category: tech
-language: en
+lang: en
---
**Update 2023-09-07**: With Devuan Daedelus Pipewire is in the
diff --git a/_posts/2024-09-04-convention-report-east-12-2024.md b/_posts/2024-09-04-convention-report-east-12-2024.md
index 7c71898..31785a8 100644
--- a/_posts/2024-09-04-convention-report-east-12-2024.md
+++ b/_posts/2024-09-04-convention-report-east-12-2024.md
@@ -3,6 +3,7 @@ layout: post
title: 'Convention Report: EAST 12 (2024)'
date: 2024-09-04 20:00 +0200
description: My convention report of the furry convention "EAST 12" in 2024
+lang: en
---
It's time for another convention report.
I've been going to the EAST convention since EAST 7 (except EAST 10, which was
diff --git a/assets/main.scss b/assets/main.scss
index ff45870..4ff4a71 100644
--- a/assets/main.scss
+++ b/assets/main.scss
@@ -53,7 +53,6 @@ a:visited {
background: #eeeeee;
margin-bottom: 1em;
position: relative;
- z-index: -1;
}
.banner-close-button {
@@ -67,3 +66,8 @@ hr.postsep {
margin: auto;
margin-bottom: 1em;
}
+
+/* site nav above news items */
+.site-nav {
+ z-index: 10;
+}
diff --git a/assets/mscomm/comments.js b/assets/mscomm/comments.js
new file mode 100644
index 0000000..57b9793
--- /dev/null
+++ b/assets/mscomm/comments.js
@@ -0,0 +1,311 @@
+// © https://phosphoricons.com/
+export const icons = {
+ reblog:
+ `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="none" stroke="currentColor" stroke-width="24"><polyline points="200 88 224 64 200 40"/><path d="M32,128A64,64,0,0,1,96,64H224"/><polyline points="56 168 32 192 56 216"/><path d="M224,128a64,64,0,0,1-64,64H32"/></svg>`,
+ favourite:
+ `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor"><path d="M240,94c0,70-103.79,126.66-108.21,129a8,8,0,0,1-7.58,0C119.79,220.66,16,164,16,94A62.07,62.07,0,0,1,78,32c20.65,0,38.73,8.88,50,23.89C139.27,40.88,157.35,32,178,32A62.07,62.07,0,0,1,240,94Z"/></svg>`,
+ author:
+ `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor" class="comment-author"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"></path></svg>`,
+
+ // @ https://simpleicons.org/
+ mastodon:
+ `<svg role="img" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Mastodon</title><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>`,
+
+ pleroma:
+ `<svg role="img" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Pleroma</title><path d="M6.36 0A1.868 1.868 0 004.49 1.868V24h5.964V0zm7.113 0v12h4.168a1.868 1.868 0 001.868-1.868V0zm0 18.036V24h4.168a1.868 1.868 0 001.868-1.868v-4.096Z"/></svg>`,
+
+ bluesky:
+ `<svg role="img" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z"/></svg>`,
+};
+
+export default class SocialComments extends HTMLElement {
+ comments = {};
+
+ async connectedCallback() {
+ const lang = this.closest("[lang]")?.lang || navigator.language || "en";
+
+ this.dateTimeFormatter = new Intl.DateTimeFormat(lang, {
+ dateStyle: "medium",
+ timeStyle: "short",
+ });
+
+ const mastodon = this.getAttribute("mastodon") || this.getAttribute("src");
+ const bluesky = this.getAttribute("bluesky");
+
+ await Promise.all([
+ mastodon && this.#fetchMastodon(new URL(mastodon)),
+ bluesky && this.#fetchBluesky(new URL(bluesky)),
+ ]);
+
+ this.refresh();
+ }
+
+ refresh() {
+ const comments = [
+ ...this.comments.mastodon || [],
+ ...this.comments.bluesky || [],
+ ].sort(
+ (a, b) => new Date(a.createdAt) - new Date(b.createdAt),
+ );
+
+ if (comments.length) {
+ this.innerHTML = "";
+ this.render(this, comments);
+ }
+ }
+
+ async #fetchBluesky(url) {
+ const { pathname } = url;
+
+ const [, handle, rkey] = pathname.match(
+ /\/profile\/([\w\.]+)\/post\/(\w+)/,
+ );
+
+ if (!handle || !rkey) {
+ return;
+ }
+
+ const options = {
+ ttl: Number(this.getAttribute("cache") || 0),
+ };
+
+ const didData = await fetchJSON(
+ `https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`,
+ options,
+ );
+ const uri = `at://${didData.did}/app.bsky.feed.post/${rkey}`;
+
+ this.comments.bluesky = dataFromBluesky(
+ await fetchJSON(
+ `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${uri}`,
+ options,
+ ),
+ );
+ }
+
+ async #fetchMastodon(url) {
+ const { origin, pathname } = url;
+ let id;
+
+ const source = pathname.includes("/notice/") ? "pleroma" : "mastodon";
+
+ if (source === "pleroma") {
+ [, id] = pathname.match(/^\/notice\/([^\/?#]+)/);
+ } else {
+ [, id] = pathname.match(/\/(\d+)$/);
+ }
+
+ if (!id) {
+ return;
+ }
+
+ const token = this.getAttribute("token");
+ const options = {
+ ttl: Number(this.getAttribute("cache") || 0),
+ };
+ if (token) {
+ options.headers = {
+ Authorization: `Bearer ${token}`,
+ };
+ }
+
+ const user = url.pathname.split("/")[1];
+ const author = `${user}@${url.hostname}`;
+
+ const comments = dataFromMastodon(
+ await fetchJSON(
+ new URL(`${origin}/api/v1/statuses/${id}/context`),
+ options,
+ ),
+ author,
+ source,
+ );
+
+ this.comments.mastodon = comments.filter((comment) =>
+ comment.parent === id
+ );
+ }
+
+ render(container, replies) {
+ const ul = document.createElement("ul");
+
+ for (const reply of replies) {
+ const comment = document.createElement("li");
+ comment.innerHTML = this.renderComment(reply);
+
+ if (reply.replies.length) {
+ this.render(comment, reply.replies);
+ }
+ ul.appendChild(comment);
+ }
+
+ container.appendChild(ul);
+ }
+
+ renderComment(comment) {
+ return `
+ <article class="comment" id="comment-${comment.id}">
+ <footer class="comment-footer">
+ <a href="${comment.author.url}" class="comment-user">
+ <img class="comment-avatar" src="${comment.author.avatar}" alt="${comment.author.alt}'s avatar" width="200" height="200">
+ ${comment.isMine ? icons.author : ""}
+ <strong class="comment-username">
+ ${comment.author.name}
+ </strong>
+ <em class="comment-useraddress">${comment.author.handler}</em>
+ </a>
+ <a href="${comment.url}" class="comment-address">
+ <time class="comment-time" title="${comment.createdAt.toISOString()}">
+ ${this.dateTimeFormatter.format(comment.createdAt)}
+ ${icons[comment.source]}
+ </time>
+ </a>
+ </footer>
+ <div class="comment-body">
+ ${comment.content}
+
+ <p class="comment-counts">
+ ${
+ comment.boosts ? `<span>${icons.reblog} ${comment.boosts}</span>` : ""
+ }
+ ${
+ comment.likes ? `<span>${icons.favourite} ${comment.likes}</span>` : ""
+ }
+ </p>
+ </div>
+ </article>
+ `;
+ }
+}
+
+function formatEmojis(html, emojis) {
+ emojis.forEach(({ shortcode, static_url, url }) => {
+ html = html.replace(
+ `:${shortcode}:`,
+ `<picture>
+ <source srcset="${url}" media="(prefers-reduced-motion: no-preference)">
+ <img src="${static_url}" alt=":${shortcode}:" title=":${shortcode}:" width="16" height="16">
+ </picture>`,
+ );
+ });
+ return html;
+}
+
+async function fetchJSON(url, options = {}) {
+ const headers = new Headers();
+
+ if (options.headers) {
+ for (const [key, value] of Object.entries(options.headers)) {
+ headers.set(key, value);
+ }
+ }
+
+ if (typeof caches === "undefined") {
+ return await (await fetch(url), { headers }).json();
+ }
+
+ const cache = await caches.open("mastodon-comments");
+ let cached = await cache.match(url);
+
+ if (cached && options.ttl) {
+ const cacheTime = new Date(cached.headers.get("x-cached-at"));
+ const diff = Date.now() - cacheTime.getTime();
+
+ if (diff <= options.ttl * 1000) {
+ return await cached.json();
+ }
+ }
+
+ try {
+ const response = await fetch(url, { headers });
+ const body = await response.json();
+
+ cached = new Response(JSON.stringify(body));
+ cached.headers.set("x-cached-at", new Date());
+ cached.headers.set("content-type", "application/json; charset=utf-8");
+ await cache.put(url, cached);
+ return body;
+ } catch {
+ if (cached) {
+ return await cached.json();
+ }
+ }
+}
+
+function dataFromMastodon(data, author, source) {
+ const comments = new Map();
+
+ // Transform data to a more usable format
+ for (const comment of data.descendants) {
+ if (comment.visibility !== "public") {
+ continue;
+ }
+
+ const { account } = comment;
+ const handler = `@${account.username}@${new URL(account.url).hostname}`;
+ comments.set(comment.id, {
+ id: comment.id,
+ isMine: author === handler,
+ source,
+ url: comment.url,
+ parent: comment.in_reply_to_id,
+ createdAt: new Date(comment.created_at),
+ content: formatEmojis(comment.content, comment.emojis),
+ author: {
+ name: formatEmojis(account.display_name, account.emojis),
+ handler,
+ url: account.url,
+ avatar: account.avatar_static,
+ alt: account.display_name,
+ },
+ boosts: comment.reblogs_count,
+ likes: comment.favourites_count,
+ replies: [],
+ });
+ }
+
+ // Group comments by parent
+ for (const comment of comments.values()) {
+ if (comment.parent && comments.has(comment.parent)) {
+ comments.get(comment.parent).replies.push(comment);
+ }
+ }
+
+ return Array.from(comments.values());
+}
+
+function dataFromBluesky(data) {
+ const { thread } = data;
+
+ return blueskyComments(
+ thread.post.author.did,
+ thread.post.cid,
+ thread.replies,
+ );
+}
+
+function blueskyComments(author, parent, comments) {
+ return comments.map((reply) => {
+ const { post, replies } = reply;
+ const rkey = post.uri.split("/").pop();
+ return {
+ id: post.cid,
+ isMine: post.author.did === author,
+ source: "bluesky",
+ url: `https://bsky.app/profile/${post.author.handle}/post/${rkey}`,
+ parent,
+ createdAt: new Date(post.record.createdAt),
+ content: post.record.text,
+ author: {
+ name: post.author.displayName,
+ handler: post.author.handle,
+ url: `https://bsky.app/profile/${post.author.handle}`,
+ avatar: post.author.avatar,
+ alt: post.author.displayName,
+ },
+ boosts: post.repostCount,
+ likes: post.likeCount,
+ replies: blueskyComments(author, post.cid, replies || []),
+ };
+ });
+}
diff --git a/assets/mscomm/styles.css b/assets/mscomm/styles.css
new file mode 100644
index 0000000..61d4d0d
--- /dev/null
+++ b/assets/mscomm/styles.css
@@ -0,0 +1,92 @@
+oom-comments {
+ display: block;
+ /*padding: 2em;*/
+}
+oom-comments ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+oom-comments li {
+ margin: 32px 0;
+}
+oom-comments article {
+ max-width: 600px;
+}
+oom-comments ul ul {
+ margin-left: 64px;
+}
+oom-comments .comment-avatar {
+ width: 50px;
+ height: 50px;
+ border-radius: 6px;
+ float: left;
+ margin-right: 14px;
+ box-shadow: 0 0 1px #0009;
+}
+oom-comments .comment-user {
+ color: currentColor;
+ text-decoration: none;
+ display: block;
+ position: relative;
+}
+oom-comments .comment-author {
+ position: absolute;
+ left: 35px;
+ top: 35px;
+ background: white;
+ border-radius: 50%;
+ width: 20px;
+ height: 20px;
+ color: gray;
+}
+oom-comments .comment-user:hover .comment-username {
+ text-decoration: underline;
+}
+oom-comments .comment-username {
+ margin-right: 0.5em;
+}
+oom-comments .comment-useraddress {
+ color: gray;
+ font-size: small;
+ font-style: normal;
+}
+oom-comments .comment-time {
+ font-size: small;
+ display: flex;
+ align-items: center;
+ column-gap: 0.4em;
+}
+oom-comments .comment-time svg {
+ width: 1em;
+ height: 1em;
+ fill: gray;
+}
+oom-comments .comment-address {
+ color: currentColor;
+ text-decoration: none;
+ display: block;
+ margin-top: 0.25em;
+}
+oom-comments .comment-address:hover {
+ text-decoration: underline;
+}
+oom-comments .comment-body {
+ margin-top: 0.5em;
+ margin-left: 64px;
+ line-height: 1.5;
+}
+oom-comments .comment-body p {
+ margin: 0.5em 0;
+}
+oom-comments .comment-counts {
+ display: flex;
+ column-gap: 1em;
+ font-size: small;
+}
+oom-comments .comment-counts > span {
+ display: flex;
+ align-items: center;
+ column-gap: 0.3em;
+ color: gray;
+}
diff --git a/dn42.md b/dn42.md
index 0015f23..9d6fca7 100644
--- a/dn42.md
+++ b/dn42.md
@@ -1,7 +1,7 @@
---
layout: page
title: DN42
-in_navbar: true
+in_navbar: false
order: 50
lang: "en"
---
diff --git a/projects.md b/projects.md
new file mode 100644
index 0000000..b3d47f7
--- /dev/null
+++ b/projects.md
@@ -0,0 +1,31 @@
+---
+layout: page
+title: Projects
+in_navbar: true
+order: 50
+lang: "en"
+---
+
+Some of my projects and sites:
+
+- This blog you're reading right now
+- [DN42]({% link dn42.md %})
+- [Wiki](https://uvokchee.de/wiki/)
+- [Funkwhale](https://fw.uvok.de/) (currently defunc)
+- [Personal Matrix and XMPP Server]({% link contact.html %})
+- Running an authoritative DNS server with [PowerDNS](https://www.powerdns.com/powerdns-community)
+- Running various VPS (all with Debian, of course)
+- Running [OpenWRT](https://openwrt.org/) in my home network (e.g. for tagged VLAN) [2]
+- Running [Proxmox](https://www.proxmox.com/en/) (hosting various LXC containers) in my home
+- Running an [RIPE ATLAS](https://atlas.ripe.net/docs/) probe - [software](https://github.com/RIPE-NCC/ripe-atlas-software-probe)
+- [OpenPGP WKD](https://wiki.gnupg.org/WKD) via DNS
+ (for shits and giggles - I don't really write mail, and PGP has it's usability problems)
+- [Git server](https://git.uvok.de/)
+ using [gitolite](https://gitolite.com/gitolite/index.html) [1]
+ and [cgit](https://git.zx2c4.com/cgit/)
+
+[1] Can really recommend this if you *don't* want a full-blown Git hosting with
+ "UI" / CI etc. - just the bare-bones git repository hosting.
+ (Which saves you setting up the bare git repos manually, though). \
+[2] I tried OPNsense in the past, too. But at some point I ran into problems I couldn't fix.
+ Also, it was virtualized inside Proxmox. Not the optimal solution.
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..a8e359f
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,3 @@
+URL scheme for piwigo:
+
+i.php?/upload/2021/06/04/20210604124815-78046f20-sm.jpg