feat(projects): load projects from content/

This commit is contained in:
2025-11-19 12:48:45 +01:00
parent 23a395a9ed
commit a664b2660e
26 changed files with 2692 additions and 83 deletions

View File

@@ -1,6 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import Background from "./Background.vue"; import Background from "./Background.vue";
import Projects from "./Projects.vue"; import Projects from "./Projects.vue";
const store = useProjectsStore();
store.$reset();
await store.loadProjects();
</script> </script>
<template> <template>

View File

@@ -1,10 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
// TODO: handle mouse wheel
import gsap from "gsap"; import gsap from "gsap";
import SELECTOR_IMAGE from "/assets/images/projects/bottom-screen/selector.webp"; import SELECTOR_IMAGE from "/assets/images/projects/bottom-screen/selector.webp";
import PROJECT_SQUARE_IMAGE from "/assets/images/projects/bottom-screen/project-square.webp"; import PROJECT_SQUARE_IMAGE from "/assets/images/projects/bottom-screen/project-square.webp";
import { useKeyDown } from "~/composables/useKeyDown";
const store = useProjectsStore(); const store = useProjectsStore();
@@ -68,7 +65,7 @@ useRender((ctx) => {
ctx.font = "10px NDS10"; ctx.font = "10px NDS10";
const lines = store.projects[store.currentProject]!.description.split("\n"); const lines = store.projects[store.currentProject]!.description.split("\\n");
const textY = lines.length === 1 ? 35 : 28; const textY = lines.length === 1 ? 35 : 28;
for (let i = 0; i < lines.length; i += 1) { for (let i = 0; i < lines.length; i += 1) {
const { actualBoundingBoxRight: width } = ctx.measureText(lines[i]!); const { actualBoundingBoxRight: width } = ctx.measureText(lines[i]!);

View File

@@ -1,67 +1,15 @@
import PIHKAAL_ME_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/pihkaal.me.webp"; // 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 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 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 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 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 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 SP3WEB_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/s3pweb.webp";
import BIOBLEUD_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/biobleud.webp"; // import BIOBLEUD_THUMBNAIL from "/assets/images/projects/bottom-screen/thumbnails/biobleud.webp";
export const useProjectsStore = defineStore("projects", { export const useProjectsStore = defineStore("projects", {
state: () => ({ state: () => ({
// prettier-ignore projects: [] as {
projects: [
{
description: "pihkaal.me - my personal website",
thumbnail: PIHKAAL_ME_THUMBNAIL,
url: "https://pihkaal.me",
},
{
description: "tlock - fully customizable and cross-\nplatform terminal based clock",
thumbnail: TLOCK_THUMBNAIL,
url: "https://github.com/pihkaal/tlock",
},
{
description: "Simple QR - Simple QR code generator\nwith straightforward API",
thumbnail: SIMPLE_QR_THUMBNAIL,
url: "https://simple-qr.com",
},
{
description: "lilou.cat - My cat's website",
thumbnail: LILOU_CAT_THUMBNAIL,
url: "https://lilou.cat",
},
{
description: "LBF Bot - Custom Discord bot for\na gaming group",
thumbnail: LBF_BOT_THUMBNAIL,
url: "https://github.com/pihkaal/lbf-bot",
},
{
description: "Raylib Speedruns - Collection of simple\nRaylib setups in multiple languages",
thumbnail: RAYLIB_SPEENDRUNS_THUMBNAIL,
url: "https://github.com/pihkaal/raylib-speedruns",
},
{
description: "S3P Map Editor - Web based map editor\nspecialized for trucks",
thumbnail: SP3WEB_THUMBNAIL,
url: null,
},
{
description: "S3P Eramba Visualizer - Eramba\nasset visualization",
thumbnail: SP3WEB_THUMBNAIL,
url: null,
},
{
description: "S3P Incident Engine - Automated\nalerts to Jira",
thumbnail: SP3WEB_THUMBNAIL,
url: null,
},
{
description: "Biobleud - Automated Excel imports\nfor an ERP system",
thumbnail: BIOBLEUD_THUMBNAIL,
url: null,
},
] satisfies {
description: string; description: string;
thumbnail: string; thumbnail: string;
url: string | null; url: string | null;
@@ -70,6 +18,14 @@ export const useProjectsStore = defineStore("projects", {
}), }),
actions: { actions: {
async loadProjects() {
const { data: projects } = await useAsyncData("projects", () =>
queryCollection("projects").order("order", "ASC").all(),
);
if (!projects.value) throw "Cannot load projects";
this.projects = projects.value;
},
visitProject() { visitProject() {
const url = this.projects[this.currentProject]!.url; const url = this.projects[this.currentProject]!.url;
if (url) navigateTo(url, { external: true, open: { target: "_blank" } }); if (url) navigateTo(url, { external: true, open: { target: "_blank" } });

17
content.config.ts Normal file
View File

@@ -0,0 +1,17 @@
import { defineContentConfig, defineCollection } from "@nuxt/content";
import { z } from "zod";
export default defineContentConfig({
collections: {
projects: defineCollection({
type: "page",
source: "projects/*.md",
schema: z.object({
description: z.string(),
url: z.url().nullable(),
thumbnail: z.string(),
order: z.number(),
}),
}),
},
});

View File

@@ -0,0 +1,6 @@
---
description: Biobleud - Automated Excel imports\nfor an ERP system
url: null
thumbnail: /images/projects/biobleud.webp
order: 100
---

View File

@@ -0,0 +1,6 @@
---
description: LBF Bot - Custom Discord bot for\na gaming group
url: https://github.com/pihkaal/lbf-bot
thumbnail: /images/projects/lbf-bot.webp
order: 50
---

View File

@@ -0,0 +1,6 @@
---
description: lilou.cat - My cat's website
url: https://lilou.cat
thumbnail: /images/projects/lilou-cat.webp
order: 40
---

View File

@@ -0,0 +1,6 @@
---
description: pihkaal.me - my personal website
url: https://pihkaal.me
thumbnail: /images/projects/pihkaal-me.webp
order: 10
---

View File

@@ -0,0 +1,6 @@
---
description: Raylib Speedruns - Collection of simple\nRaylib setups in multiple languages
url: https://github.com/pihkaal/raylib-speedruns
thumbnail: /images/projects/raylib-speedruns.webp
order: 60
---

View File

@@ -0,0 +1,6 @@
---
description: S3P Eramba Visualizer - Eramba\nasset visualization
url: null
thumbnail: /images/projects/s3pweb.webp
order: 80
---

View File

@@ -0,0 +1,6 @@
---
description: S3P Incident Engine - Automated\nalerts to Jira
url: null
thumbnail: /images/projects/s3pweb.webp
order: 90
---

View File

@@ -0,0 +1,6 @@
---
description: S3P Map Editor - Web based map editor\nspecialized for trucks
url: null
thumbnail: /images/projects/s3pweb.webp
order: 70
---

View File

@@ -0,0 +1,6 @@
---
description: Simple QR - Simple QR code generator\nwith straightforward API
url: https://simple-qr.com
thumbnail: /images/projects/simple-qr.webp
order: 30
---

View File

@@ -0,0 +1,6 @@
---
description: tlock - fully customizable and cross-\nplatform terminal based clock
url: https://github.com/pihkaal/tlock
thumbnail: /images/projects/tlock.webp
order: 20
---

View File

@@ -2,7 +2,7 @@
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: "2025-07-15", compatibilityDate: "2025-07-15",
devtools: { enabled: true }, devtools: { enabled: true },
modules: ["@nuxt/eslint", "@pinia/nuxt"], modules: ["@nuxt/eslint", "@nuxt/content", "@pinia/nuxt"],
css: ["~/assets/app.css"], css: ["~/assets/app.css"],
ssr: false, ssr: false,
}); });

View File

@@ -12,7 +12,9 @@
"lint": "eslint --fix --cache ." "lint": "eslint --fix --cache ."
}, },
"dependencies": { "dependencies": {
"@nuxt/content": "^3.8.2",
"@pinia/nuxt": "0.11.3", "@pinia/nuxt": "0.11.3",
"better-sqlite3": "^12.4.1",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"nuxt": "^4.2.1", "nuxt": "^4.2.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
@@ -23,6 +25,7 @@
"@nuxt/eslint": "^1.10.0", "@nuxt/eslint": "^1.10.0",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"typescript": "^5.9.3" "typescript": "^5.9.3",
"zod": "^4.1.12"
} }
} }

2602
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
onlyBuiltDependencies: onlyBuiltDependencies:
- "@parcel/watcher" - "@parcel/watcher"
- better-sqlite3
- esbuild - esbuild
- unrs-resolver - unrs-resolver

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B

View File

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 94 B

View File

Before

Width:  |  Height:  |  Size: 42 B

After

Width:  |  Height:  |  Size: 42 B