chadtmiller.com/knotless

Git hosted in your atproto account, on any stock PDS, fast enough for agents.

git clone knotless://chadtmiller.com/knotless

needs the knotless git helper. set it up

Draw Mermaid diagrams in Markdown with Pele

A ```mermaid block in a README, a Markdown file or a pull or issue is
drawn as a diagram by Pele (https://pele.run), in the browser. The server
still sends the block as code, so a page without scripts shows its
source. Pele, and each kind of diagram, load only on a page that has
one; the diagram takes the site's colors and fonts, light or dark.
chadtmiller.com committed on Oct 22, 2026 parent 373e638 commit 99a16a8
Showing 5 changed files with 66 additions and 3 deletions
web/package.json+1−0
@@ -15,6 +15,7 @@
"@sveltejs/kit": "^3.0.0",
"@sveltejs/vite-plugin-svelte": "^7.2.0",
"@types/node": "^24.19.2",
"pele": "^1.8.0",
"svelte": "^5.57.1",
"svelte-check": "^4.6.0",
"typescript": "^6.0.3",
web/src/lib/mermaid.tsnew+59−0
@@ -0,0 +1,59 @@
// Mermaid diagrams in Markdown, drawn in the browser by Pele
// (https://pele.run). The server sends a ```mermaid block as code, so a
// page without scripts still shows its source; this swaps each block for
// its diagram, loading Pele, and each kind of diagram, only once a page
// has one.
import type { Mounted, MountOptions } from 'pele/lazy';
const BLOCKS = 'pre > code.language-mermaid';
/** Anyone with a repository writes diagrams, so their links are not vouched for. */
const OPTIONS: MountOptions = { linkRel: 'noopener nofollow ugc' };
/** An action drawing the Mermaid blocks in its element, again as the HTML it shows changes. */
export function diagrams(el: HTMLElement, _html: string) {
let mounted: Mounted[] = [];
let run = 0;
const clear = () => {
run++;
mounted.forEach((m) => m.destroy());
mounted = [];
};
async function draw() {
const id = ++run;
const blocks = [...el.querySelectorAll<HTMLElement>(BLOCKS)];
if (!blocks.length) return;
let pele: typeof import('pele/lazy');
try {
pele = await import('pele/lazy');
} catch {
// Not loaded: the source stays shown.
return;
}
for (const code of blocks) {
const pre = code.parentElement;
if (id !== run || !pre?.isConnected) return;
const box = document.createElement('div');
box.className = 'diagram';
pre.replaceWith(box);
try {
const m = await pele.mountAsync(box, code.textContent ?? '', OPTIONS);
if (id === run) mounted.push(m);
else m.destroy();
} catch {
// Its kind of diagram failed to load: show the source again.
box.replaceWith(pre);
}
}
}
// After the HTML it is given is in place.
queueMicrotask(draw);
return {
update() {
clear();
queueMicrotask(draw);
},
destroy: clear
};
}
web/src/lib/organisms/Comment.svelte+2−1
@@ -1,6 +1,7 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { date } from '#lib/format.ts';
import { diagrams } from '#lib/mermaid.ts';
import Avatar from '#lib/atoms/Avatar.svelte';
import Person from '#lib/molecules/Person.svelte';
/** Something said on a pull or an issue: who said it, when, and what. */
@@ -15,5 +16,5 @@
<span class="mute">{said} on {date(createdAt)}</span>
{#if end}<span class="end">{@render end()}</span>{/if}
</div>
<div class="prose">{@html html}</div>
<div class="prose" use:diagrams={html}>{@html html}</div>
</div>
web/src/lib/organisms/Readme.svelte+2−1
@@ -1,13 +1,14 @@
<script lang="ts">
import type { Readme } from '#lib/api/Readme.ts';
import Icon from '#lib/atoms/Icon.svelte';
import { diagrams } from '#lib/mermaid.ts';
let { readme }: { readme: Readme } = $props();
</script>
<article class="box">
<div class="boxhead"><Icon name="file" /><b>{readme.name}</b></div>
{#if readme.html !== null}
<div class="prose">{@html readme.html}</div>
<div class="prose" use:diagrams={readme.html}>{@html readme.html}</div>
{:else}
<div class="prose"><pre>{readme.text}</pre></div>
{/if}
web/src/routes/[authority]/[repo]/blob/[...rest]/+page.svelte+2−1
@@ -1,6 +1,7 @@
<script lang="ts">
import { page } from '$app/state';
import { repoHref, size, urlPath } from '#lib/format.ts';
import { diagrams } from '#lib/mermaid.ts';
import Icon from '#lib/atoms/Icon.svelte';
import CodeView from '#lib/organisms/CodeView.svelte';
import Toolbar from '#lib/organisms/Toolbar.svelte';
@@ -36,7 +37,7 @@
{:else if b.shown === 'large'}
<div class="note">Too large to show here: view it raw.</div>
{:else if b.html !== null && !plain}
<div class="prose">{@html b.html}</div>
<div class="prose" use:diagrams={b.html}>{@html b.html}</div>
{:else}
<CodeView text={b.text ?? ''} lines={b.lines} ranges={b.ranges} />
{/if}