feat(projects): render preview in top screen, also prepare for markdown rendering

This commit is contained in:
2025-11-20 23:57:10 +01:00
parent a664b2660e
commit 02ee08b4b0
15 changed files with 1109 additions and 577 deletions

View File

@@ -1,18 +1,70 @@
// import PIHKAAL_ME_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/pihkaal.me.webp";
// import TLOCK_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/tlock.webp";
// import SIMPLE_QR_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/simple-qr.webp";
// import LILOU_CAT_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/lilou.cat.webp";
// import LBF_BOT_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/lbf-bot.webp";
// import RAYLIB_SPEENDRUNS_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/raylib-speedruns.webp";
// import SP3WEB_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/s3pweb.webp";
// import BIOBLEUD_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/biobleud.webp";
import type { MarkdownRoot } from "@nuxt/content";
type MarkdownBody = (
| {
type: "h1" | "p";
text: string;
}
| {
type: "img";
image: HTMLImageElement;
}
)[];
const createImage = (src: string): HTMLImageElement => {
// TODO: how to cleanup ?
const img = document.createElement("img");
img.src = src;
return img;
};
// TODO: move to utils or smth maybe
const simplifyMarkdownAST = (root: MarkdownRoot) => {
const body: MarkdownBody = [];
for (const node of root.value) {
if (Array.isArray(node)) {
const [type, props, value] = node;
console.log("--------------");
console.log(`type = ${type}`);
console.log(`props = ${JSON.stringify(props)}`);
console.log(`value = ${value}`);
switch (type) {
case "h1":
case "p": {
if (typeof value !== "string")
throw `Unsupported node value for '${type}': '${value}'`;
body.push({ type, text: value });
break;
}
case "img": {
if (typeof props["src"] !== "string")
throw `Unsupported type or missing node prop "src" for '${type}': '${JSON.stringify(props)}'`;
body.push({ type, image: createImage(props.src) });
break;
}
default: {
throw `Unsupported '${type}'`;
}
}
} else {
throw `Unsupported node kind 'string'`;
}
}
return body;
};
export const useProjectsStore = defineStore("projects", {
state: () => ({
projects: [] as {
description: string;
thumbnail: string;
preview: HTMLImageElement;
url: string | null;
body: MarkdownBody;
}[],
currentProject: 0,
}),
@@ -23,7 +75,18 @@ export const useProjectsStore = defineStore("projects", {
queryCollection("projects").order("order", "ASC").all(),
);
if (!projects.value) throw "Cannot load projects";
this.projects = projects.value;
this.projects = [];
for (const project of projects.value) {
this.projects.push({
description: project.description,
thumbnail: project.thumbnail,
preview: createImage(project.preview),
url: project.url,
body: simplifyMarkdownAST(project.body),
});
}
},
visitProject() {