queste informazioni sono leggermente datate. questo sito è stato offline tra luglio 2025 e gennaio 2026 e da quando l’ho ri pubblicato ho fatto varie modifiche a questo workflow
🗳️questo sito è il mio vault obsidian, che viene compilato in html tramite lume.
il mio vault risiede su una repository privata su github, e uno script sulla mia vps droplet digitalocean gira periodicamente per pullare le ultime modifiche e ricompilarle. il sito viene servito con caddy.
preprocessori
lume permette l’utilizzo di preprocessori per modificare il markdown prima che venga compilato in html. ne ho scritti alcuni per adattare il mio vault obsidian al mio caso d’uso:
titolo
in obsidian il titolo di una nota (pagina) è il nome del file, a meno che non sia espressamente specificato. per poter inserire il titolo in cima alla pagina, ce uso la proprietà title nel template
site.preprocess([".md"], pages => {
for (const page of pages) {
if (!page.data.title) {
page.data.title = page.data.basename
}
}
})
<header>
<a href="/">home</a>
<h1>{{ title }}</h1>
</header>
a capo
in markdown per andare a capo si devono mettere due spazi alla fine di una riga, mentre in obsidian non serve.
site.preprocess([".md"], pages => {
for (const page of pages) {
if (typeof page.data.content === "string") {
page.data.content = addHardLineBreaks(page.data.content);
}
}
});
function addHardLineBreaks(content: string): string {
const lines = content.split("\n");
let inCodeBlock = false;
return lines
.map((line) => {
const trimmed = line.trimStart();
if (trimmed.startsWith("```")) {
inCodeBlock = !inCodeBlock;
return line;
}
if (inCodeBlock || line.endsWith(" ")) return line;
return line + " ";
})
.join("\n");
}
wikilinks
in markdown i link vengono creati con la sintassi:

per i link interni (tra le pagine di un vault) obsidian utilizza gli wiki-style links, fatti così
[[nome nota]] OPPURE [[nome nota|alias da mostrare nel documento]]
per parsarli uso la libreria wikilinks insieme a un preprocessor personalizzato
import wikilinks from "https://deno.land/x/lume_markdown_plugins@v0.9.0/wikilinks.ts";
site.use(wikilinks({ slugify: s => s }));
site.process([".html"], (pages) => {
for (const page of pages) {
for (const link of page.document!.querySelectorAll("a[data-wikilink]")) {
const id = link.getAttribute("data-wikilink");
link.removeAttribute("data-wikilink");
let linkName = ''
let linkLocation = ''
if (link.textContent?.includes('|')) {
[linkLocation, linkName] = link.textContent.split('|')
link.textContent = linkName
} else {
[linkName, linkLocation] = [id ?? '', id ?? '']
}
// Search a page with this id
const found = pages.find((p) => p.data.basename === linkLocation);
if (found) {
link.setAttribute("href", found.data.url);
} else {
link.setAttribute("title", "This page does not exist");
}
}
}
});
immagini e audio
in markdown le immagini si inseriscono con la sintassi

obsidian supporta questa sintassi ma preferisce la sua versione:
![[url immagine]]
stessa cosa vale per i file audio: supporta la sintassi html
<audio src="url file audio" controls> </audio>
ma preferisce la sua sintassi.
per accontentire sia lume che obsidian ho scritto una regex per trasformare dall’uno all’altro
const imageExtensions = [".jpg", ".jpeg", ".gif", ".png", ".bmp", ".svg"]
const audioExtensions = [".flac", ".mp3", ".ogg", ".opus", ".wav"]
function wikicontentToRegular(line: string): string {
const r = /^\!\[\[(.*)\]\]$/
const match = line.match(r)
if (!match) {
// throw new Error('not wiki content')
return line
}
const media = match[1]
for (const ext of audioExtensions) {
if (media.endsWith(ext)) {
return `<audio src="/${media}" controls> </audio>`
}
}
for (const ext of imageExtensions) {
if (media.endsWith(ext)) {
return `})`
}
}
}
checklist
per trasformare le checklist da Github Flavored Markdown:
site.process([".html"], pages => {
for (const page of pages) {
for (const item of page.document!.querySelectorAll("li")) {
if (item.firstChild?.nodeType === 3/* Node.TEXT_NODE */) {
const text = item.firstChild.textContent || '';
const updatedEmpty = text.replace(/^\[ \]/, emptyMark);
if (updatedEmpty !== text) {
item.firstChild.textContent = updatedEmpty;
}
const updatedFull = text.replace(/^\[x\]/, fullMark);
if (updatedFull !== text) {
item.firstChild.textContent = updatedFull;
}
}
}
}
})