Compare commits
5 Commits
main
...
c7a982e508
Author | SHA1 | Date | |
---|---|---|---|
|
c7a982e508 | ||
|
4e815206c9 | ||
|
b8c1581f5a | ||
|
712c6fe11e | ||
|
8bd21a9a23 |
14
.github/workflows/default.yaml
vendored
14
.github/workflows/default.yaml
vendored
@@ -51,9 +51,21 @@ jobs:
|
|||||||
- name: 🔄 Pull Git LFS files
|
- name: 🔄 Pull Git LFS files
|
||||||
run: git lfs pull
|
run: git lfs pull
|
||||||
|
|
||||||
|
- name: 🔧 Increase file descriptor limits
|
||||||
|
run: |
|
||||||
|
echo "Current file descriptor limits:"
|
||||||
|
ulimit -n
|
||||||
|
echo "Increasing file descriptor limits..."
|
||||||
|
ulimit -n 65536
|
||||||
|
echo "New file descriptor limits:"
|
||||||
|
ulimit -n
|
||||||
|
|
||||||
- name: 🏗️ Build site
|
- name: 🏗️ Build site
|
||||||
run: |
|
run: |
|
||||||
pnpm i && pnpm build
|
# Build with NODE_OPTIONS to increase memory limits and avoid watching files
|
||||||
|
export NODE_OPTIONS="--max-old-space-size=4096 --no-warnings"
|
||||||
|
# Astro-specific optimizations to avoid file watching
|
||||||
|
pnpm i && NODE_ENV=production ASTRO_DISABLE_HMR=true pnpm build
|
||||||
|
|
||||||
- name: 🔑 Configure rclone
|
- name: 🔑 Configure rclone
|
||||||
run: |
|
run: |
|
||||||
|
@@ -17,12 +17,13 @@ const locales = {
|
|||||||
const DEFAULT_LAYOUT = '@layouts/Post.astro';
|
const DEFAULT_LAYOUT = '@layouts/Post.astro';
|
||||||
|
|
||||||
function setDefaultLayout() {
|
function setDefaultLayout() {
|
||||||
return function(_, file) {
|
return function (_, file) {
|
||||||
const { frontmatter } = file.data.astro;
|
const { frontmatter } = file.data.astro;
|
||||||
if (!frontmatter.layout) frontmatter.layout = DEFAULT_LAYOUT;
|
if (!frontmatter.layout) frontmatter.layout = DEFAULT_LAYOUT;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://max-richter.dev",
|
site: "https://max-richter.dev",
|
||||||
trailingSlash: "never",
|
trailingSlash: "never",
|
||||||
@@ -38,7 +39,7 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
watch: {
|
watch: {
|
||||||
// Customize watch behavior to reduce file watchers
|
// Customize watch behavior to reduce file watchers
|
||||||
ignored: ['**/node_modules/**', '**/dist/**', '**/.git/**'],
|
ignored: ['**/node_modules/**', '**/dist/**', '**/.git/**'],
|
||||||
usePolling: process.env.NODE_ENV === 'production',
|
usePolling: process.env.NODE_ENV === 'production',
|
||||||
|
34
package.json
34
package.json
@@ -11,32 +11,32 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/mdx": "^4.3.6",
|
"@astrojs/mdx": "^4.2.6",
|
||||||
"@astrojs/svelte": "^7.2.0",
|
"@astrojs/svelte": "^7.0.13",
|
||||||
"@astrojs/tailwind": "^6.0.2",
|
"@astrojs/tailwind": "^6.0.2",
|
||||||
"astro": "^5.14.1",
|
"astro": "^5.7.13",
|
||||||
"astro-i18n-aut": "^0.7.3",
|
"astro-i18n-aut": "^0.7.3",
|
||||||
"exifreader": "^4.32.0",
|
"exifreader": "^4.30.1",
|
||||||
"svelte": "^5.39.8",
|
"svelte": "^5.28.6",
|
||||||
"svelte-gestures": "^5.2.2",
|
"svelte-gestures": "^5.1.4",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.6",
|
||||||
"thumbhash": "^0.1.1",
|
"thumbhash": "^0.1.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/sitemap": "^3.6.0",
|
"@astrojs/sitemap": "^3.4.0",
|
||||||
"@iconify-json/tabler": "^1.2.23",
|
"@iconify-json/tabler": "^1.2.17",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@unocss/preset-icons": "^66.5.2",
|
"@unocss/preset-icons": "^66.1.1",
|
||||||
"@unocss/reset": "^66.5.2",
|
"@unocss/reset": "^66.1.1",
|
||||||
"astro-font": "^1.1.0",
|
"astro-font": "^1.1.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"ogl": "^1.0.11",
|
"ogl": "^1.0.11",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-astro": "^0.14.1",
|
"prettier-plugin-astro": "^0.14.1",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.1",
|
||||||
"unocss": "^66.5.2",
|
"unocss": "^66.1.1",
|
||||||
"unplugin-icons": "^22.4.2",
|
"unplugin-icons": "^22.1.0",
|
||||||
"vite-plugin-glsl": "^1.5.4"
|
"vite-plugin-glsl": "^1.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2769
pnpm-lock.yaml
generated
2769
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
|||||||
onlyBuiltDependencies:
|
|
||||||
- esbuild
|
|
||||||
- exifreader
|
|
||||||
- sharp
|
|
@@ -32,12 +32,12 @@ const link = translatePath(`/${collection}/${id.split("/")[0]}`);
|
|||||||
<Card.Content classes="px-8 py-7 order-last xs:order-first">
|
<Card.Content classes="px-8 py-7 order-last xs:order-first">
|
||||||
<Card.Title classes="text-4xl flex items-center gap-2">
|
<Card.Title classes="text-4xl flex items-center gap-2">
|
||||||
{
|
{
|
||||||
icon &&
|
icon &&
|
||||||
(icon?.length > 5 ? (
|
(
|
||||||
<img class="h-6 w-6" src={icon} />
|
icon?.length > 5
|
||||||
) : (
|
? <img class="h-6 w-6" src={icon} />
|
||||||
<span class="p-r-4 text-md">{icon}</span>
|
: <span class="p-r-4 text-md">{icon}</span>
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
{title}
|
{title}
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
|
@@ -3,7 +3,7 @@ import type { ImageMetadata } from "astro";
|
|||||||
import { Picture as AstroImage } from "astro:assets";
|
import { Picture as AstroImage } from "astro:assets";
|
||||||
import { generateThumbHash, getExifData } from "@helpers/image";
|
import { generateThumbHash, getExifData } from "@helpers/image";
|
||||||
interface Props {
|
interface Props {
|
||||||
src: ImageMetadata & { fsPath?: string };
|
src: ImageMetadata;
|
||||||
alt: string;
|
alt: string;
|
||||||
pictureClass?: string;
|
pictureClass?: string;
|
||||||
class?: string;
|
class?: string;
|
||||||
@@ -13,18 +13,6 @@ interface Props {
|
|||||||
maxWidth?: number;
|
maxWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkImage(src: string) {
|
|
||||||
try {
|
|
||||||
const res = await fetch(src);
|
|
||||||
if (res.ok) {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
} catch (err) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
src: image,
|
src: image,
|
||||||
loader = true,
|
loader = true,
|
||||||
@@ -34,9 +22,7 @@ const {
|
|||||||
maxWidth,
|
maxWidth,
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
let thumbhash = hash && image.fsPath ? await generateThumbHash(image) : "";
|
let thumbhash = hash ? await generateThumbHash(image) : "";
|
||||||
|
|
||||||
const imageSrc = await checkImage(image.src);
|
|
||||||
|
|
||||||
let exif = await getExifData(image);
|
let exif = await getExifData(image);
|
||||||
|
|
||||||
@@ -59,23 +45,18 @@ const sizes = [
|
|||||||
].filter((size) => !maxWidth || size.width <= maxWidth);
|
].filter((size) => !maxWidth || size.width <= maxWidth);
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
<AstroImage
|
||||||
imageSrc ? (
|
src={image}
|
||||||
<AstroImage
|
alt={alt}
|
||||||
src={imageSrc}
|
data-thumbhash={thumbhash}
|
||||||
alt={alt}
|
data-exif={JSON.stringify(exif)}
|
||||||
data-thumbhash={thumbhash}
|
pictureAttributes={{
|
||||||
data-exif={JSON.stringify(exif)}
|
class: `${hash ? "block h-full relative" : ""} ${loader ? "thumb" : ""} ${pictureClass}`,
|
||||||
inferSize={true}
|
}}
|
||||||
pictureAttributes={{
|
class={Astro.props.class}
|
||||||
class: `${hash ? "block h-full relative" : ""} ${loader ? "thumb" : ""} ${pictureClass}`,
|
widths={sizes.map((size) => size.width)}
|
||||||
}}
|
sizes={sizes
|
||||||
class={Astro.props.class}
|
.map((size) => `${size.media || "100vw"} ${size.width}px`)
|
||||||
widths={sizes.map((size) => size.width)}
|
.join(", ")}>
|
||||||
sizes={sizes
|
<slot />
|
||||||
.map((size) => `${size.media || "100vw"} ${size.width}px`)
|
</AstroImage>
|
||||||
.join(", ")}>
|
|
||||||
<slot />
|
|
||||||
</AstroImage>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
let progress: number[] = [];
|
let progress: number[] = [];
|
||||||
let currentIndex = -1;
|
let currentIndex = -1;
|
||||||
const maxZoom = 5;
|
const maxZoom = 5;
|
||||||
import { useSwipe } from "svelte-gestures";
|
import { swipe } from "svelte-gestures";
|
||||||
|
|
||||||
const mod = (a: number, b: number) => ((a % b) + b) % b;
|
const mod = (a: number, b: number) => ((a % b) + b) % b;
|
||||||
|
|
||||||
@@ -232,9 +232,10 @@
|
|||||||
|
|
||||||
{#if currentIndex > -1}
|
{#if currentIndex > -1}
|
||||||
<div
|
<div
|
||||||
{...useSwipe(handleSwipe)}
|
|
||||||
class="image"
|
class="image"
|
||||||
|
use:swipe
|
||||||
role="dialog"
|
role="dialog"
|
||||||
|
on:swipe={handleSwipe}
|
||||||
on:wheel|passive={handleScroll}
|
on:wheel|passive={handleScroll}
|
||||||
on:mousemove={handleMouseMove}
|
on:mousemove={handleMouseMove}
|
||||||
on:pointermove={handlePointerMove}>
|
on:pointermove={handlePointerMove}>
|
||||||
|
@@ -24,8 +24,8 @@ const paths = [
|
|||||||
text: t("nav.photos"),
|
text: t("nav.photos"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
link: translatePath("/resources"),
|
link: translatePath("/videos"),
|
||||||
text: t("nav.resources"),
|
text: t("nav.videos"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: Resources
|
title: Videos
|
||||||
menu: nav
|
menu: nav
|
||||||
---
|
---
|
||||||
|
1036
src/helpers/exif_
Normal file
1036
src/helpers/exif_
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ async function getSharp(): Promise<typeof import("sharp") | undefined> {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateThumbHash(image: ImageMetadata & { fsPath?: string }) {
|
export async function generateThumbHash(image: { width: number, height: number }) {
|
||||||
|
|
||||||
const sharp = await getSharp();
|
const sharp = await getSharp();
|
||||||
if (!sharp) return;
|
if (!sharp) return;
|
||||||
@@ -19,21 +19,16 @@ export async function generateThumbHash(image: ImageMetadata & { fsPath?: string
|
|||||||
const smallWidth = Math.floor(image.width * scaleFactor);
|
const smallWidth = Math.floor(image.width * scaleFactor);
|
||||||
const smallHeight = Math.floor(image.height * scaleFactor);
|
const smallHeight = Math.floor(image.height * scaleFactor);
|
||||||
|
|
||||||
try {
|
//@ts-ignore
|
||||||
const smallImg = await sharp(image.fsPath)
|
const smallImg = await sharp(image.fsPath)
|
||||||
.resize(smallWidth, smallHeight)
|
.resize(smallWidth, smallHeight)
|
||||||
.withMetadata()
|
.withMetadata()
|
||||||
.raw()
|
.raw()
|
||||||
.ensureAlpha()
|
.ensureAlpha()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
const buffer = rgbaToThumbHash(smallWidth, smallHeight, smallImg);
|
|
||||||
return Buffer.from(buffer).toString("base64");
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Could not generate thumbhash for ${image.fsPath}`, error)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const buffer = rgbaToThumbHash(smallWidth, smallHeight, smallImg);
|
||||||
|
return Buffer.from(buffer).toString("base64");
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowedExif = [
|
const allowedExif = [
|
||||||
@@ -53,15 +48,10 @@ const allowedExif = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export async function getExifData(image: ImageMetadata) {
|
export async function getExifData(image: ImageMetadata) {
|
||||||
if (image.format === "svg") return undefined; // SVGs don't have EXIF data")
|
|
||||||
const sharp = await getSharp();
|
const sharp = await getSharp();
|
||||||
if (!sharp) return;
|
if (!sharp) return;
|
||||||
const imagePath = (image as ImageMetadata & { fsPath: string }).fsPath;
|
|
||||||
try {
|
try {
|
||||||
|
const tags = await ExifReader.load((image as ImageMetadata & { fsPath: string }).fsPath, { async: true });
|
||||||
const buffer = await sharp(imagePath).toBuffer();
|
|
||||||
|
|
||||||
const tags = await ExifReader.load(buffer, { async: true });
|
|
||||||
|
|
||||||
const out: Record<string, any> = {};
|
const out: Record<string, any> = {};
|
||||||
let hasExif = false;
|
let hasExif = false;
|
||||||
@@ -75,7 +65,7 @@ export async function getExifData(image: ImageMetadata) {
|
|||||||
return hasExif ? out : undefined;
|
return hasExif ? out : undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
console.log(`Error reading EXIF data from ${imagePath}`, error);
|
console.log("Error reading EXIF data", error);
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
export type MemoriumFile = {
|
|
||||||
type: "file";
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
modTime: string;
|
|
||||||
mime: string;
|
|
||||||
size: string;
|
|
||||||
content: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MemoriumDir = {
|
|
||||||
type: "dir";
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
modTime: string;
|
|
||||||
mime: string;
|
|
||||||
size: string;
|
|
||||||
content: MemoriumEntry[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MemoriumEntry = MemoriumFile | MemoriumDir;
|
|
||||||
|
|
||||||
export async function listResource(
|
|
||||||
id: string,
|
|
||||||
): Promise<MemoriumEntry | undefined> {
|
|
||||||
const url = `https://marka.max-richter.dev/${id}`;
|
|
||||||
console.log("Fetching: ", url);
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (response.ok) {
|
|
||||||
const json = await response.json();
|
|
||||||
if (json.type == "dir") {
|
|
||||||
return {
|
|
||||||
...json,
|
|
||||||
content: json.content.filter((res) =>
|
|
||||||
res.mime === "application/markdown"
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
} catch (_e) {
|
|
||||||
console.log("Failed to get: ", url);
|
|
||||||
console.log(_e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -20,7 +20,7 @@ export const ui = {
|
|||||||
'home.subtitle': 'Trained Media Designer, Blender Nerd, Developer and Hardware Tinkerer.',
|
'home.subtitle': 'Trained Media Designer, Blender Nerd, Developer and Hardware Tinkerer.',
|
||||||
'nav.blog': 'Blog',
|
'nav.blog': 'Blog',
|
||||||
'nav.projects': 'Projects',
|
'nav.projects': 'Projects',
|
||||||
'nav.resources': 'Resources',
|
'nav.videos': 'Videos',
|
||||||
'nav.photos': 'Photos',
|
'nav.photos': 'Photos',
|
||||||
'toc.title': 'Table of Contents',
|
'toc.title': 'Table of Contents',
|
||||||
"resume": "Resume",
|
"resume": "Resume",
|
||||||
@@ -38,7 +38,7 @@ export const ui = {
|
|||||||
'home.subtitle': 'Ausgebildeter Mediengestalter, Blender Nerd, Entwickler und Hardware Bastler.',
|
'home.subtitle': 'Ausgebildeter Mediengestalter, Blender Nerd, Entwickler und Hardware Bastler.',
|
||||||
'nav.blog': 'Blog',
|
'nav.blog': 'Blog',
|
||||||
'nav.projects': 'Projekte',
|
'nav.projects': 'Projekte',
|
||||||
'nav.resources': 'Resources',
|
'nav.videos': 'Videos',
|
||||||
'nav.photos': 'Fotos',
|
'nav.photos': 'Fotos',
|
||||||
"resume": "Lebenslauf",
|
"resume": "Lebenslauf",
|
||||||
'toc.title': 'Inhaltsverzeichnis',
|
'toc.title': 'Inhaltsverzeichnis',
|
||||||
|
@@ -16,10 +16,11 @@ const { frontmatter, headings } = Astro.props;
|
|||||||
const t = useTranslations(Astro.url);
|
const t = useTranslations(Astro.url);
|
||||||
const { title, url, date: dateString, links, toc, cover } = frontmatter;
|
const { title, url, date: dateString, links, toc, cover } = frontmatter;
|
||||||
const collection = url?.split("/")[2];
|
const collection = url?.split("/")[2];
|
||||||
//@ts-ignore
|
|
||||||
const backlinkContent = t(`nav.${collection}`).toLowerCase();
|
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
const path = useTranslatedPath(Astro.url);
|
const path = useTranslatedPath(Astro.url);
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
const backlinkContent = t(`nav.${collection}`).toLowerCase();
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={title} image={cover}>
|
<Layout title={title} image={cover}>
|
||||||
|
@@ -41,6 +41,10 @@ const list = [...posts, ...photos];
|
|||||||
list.sort((a, b) => {
|
list.sort((a, b) => {
|
||||||
return a.data.date > b.data.date ? -1 : 1;
|
return a.data.date > b.data.date ? -1 : 1;
|
||||||
});
|
});
|
||||||
|
// .sort((a, b) => {
|
||||||
|
// return a.data.date.getDate() > b.data.date.getDate() ? 1 : -1;
|
||||||
|
// });
|
||||||
|
console.log(list.map((post) => [post.data.date, post.data.title]));
|
||||||
|
|
||||||
const featuredPost = list.find((post) => post.data?.featured);
|
const featuredPost = list.find((post) => post.data?.featured);
|
||||||
const otherPosts = list.filter((post) => featuredPost !== post).slice(0, 3);
|
const otherPosts = list.filter((post) => featuredPost !== post).slice(0, 3);
|
||||||
|
@@ -1,65 +0,0 @@
|
|||||||
---
|
|
||||||
import Layout from "@layouts/Layout.astro";
|
|
||||||
import { useTranslatedPath } from "@i18n/utils";
|
|
||||||
import * as memorium from "@helpers/memorium";
|
|
||||||
import { resources as resourceTypes } from "../resources.ts";
|
|
||||||
import markdownToText from "@helpers/markdownToText";
|
|
||||||
|
|
||||||
const { resourceType, resourceName } = Astro.params;
|
|
||||||
|
|
||||||
const path = useTranslatedPath(Astro.url);
|
|
||||||
|
|
||||||
export async function getStaticPaths(props) {
|
|
||||||
const paths = await Promise.all(resourceTypes.map(async (resourceType: string) => {
|
|
||||||
const resources = await memorium.listResource(resourceType.id);
|
|
||||||
return resources.content.map((res: any) => {
|
|
||||||
return {
|
|
||||||
params: {
|
|
||||||
resourceType: resourceType.id,
|
|
||||||
resourceName: res.name.replace(/\.md$/,"")
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
const flat = paths.flat();
|
|
||||||
|
|
||||||
console.log(flat.map(p => `/resources/${p.params.resourceType}/${p.params.resourceName}`))
|
|
||||||
|
|
||||||
return flat;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const resource = await memorium.listResource(
|
|
||||||
`/${resourceType}/${resourceName}.md`
|
|
||||||
);
|
|
||||||
---
|
|
||||||
|
|
||||||
<Layout title="Max Richter">
|
|
||||||
<div class="top-info flex items-center place-content-between m-y-2">
|
|
||||||
<a class="flex items-center gap-1 opacity-50" href={path("/resources/"+resourceType)}>
|
|
||||||
<span class="i-tabler-arrow-left"></span> back
|
|
||||||
</a>
|
|
||||||
<div class="date opacity-50">
|
|
||||||
{
|
|
||||||
resource?.content.date?.toLocaleString("en-US", {
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
year: "numeric",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1>{resource?.content?.name}</h1>
|
|
||||||
<p>{resource?.content?.description}</p>
|
|
||||||
<h2>Ingredients</h2>
|
|
||||||
<ul>
|
|
||||||
{resource.content.recipeIngredient.map(ingredient => <li>{markdownToText(ingredient)}</li>)}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Steps</h2>
|
|
||||||
<ol>
|
|
||||||
{resource.content.recipeInstructions.map(ingredient => <li>{markdownToText(ingredient)}</li>)}
|
|
||||||
</ol>
|
|
||||||
</Layout>
|
|
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
import Layout from "@layouts/Layout.astro";
|
|
||||||
import HeroCard from "@components/HeroCard.astro";
|
|
||||||
import * as memorium from "@helpers/memorium";
|
|
||||||
import { resources as resourceTypes } from "../resources.ts";
|
|
||||||
|
|
||||||
const { resourceType } = Astro.params;
|
|
||||||
|
|
||||||
const resources = await memorium.listResource(resourceType);
|
|
||||||
|
|
||||||
export async function getStaticPaths(): string[] {
|
|
||||||
return resourceTypes.map((type: any) => {
|
|
||||||
return {
|
|
||||||
params: {
|
|
||||||
resourceType: type.id,
|
|
||||||
resourceName: "Recipe"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<Layout title="Max Richter">
|
|
||||||
{ resources.content.filter(res => res && res?.content && res?.content?.name).map((resource: any) => (
|
|
||||||
<HeroCard
|
|
||||||
post={{
|
|
||||||
collection: "resources/"+resourceType,
|
|
||||||
id: resource.name.replace(/\.md$/,""),
|
|
||||||
data: {
|
|
||||||
title: resource.content.name,
|
|
||||||
cover: {src:`https://marka.max-richter.dev/${resource.content.image}`}
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Layout>
|
|
@@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
import Layout from "@layouts/Layout.astro";
|
|
||||||
import HeroCard from "@components/HeroCard.astro";
|
|
||||||
import { resources } from "./resources.ts";
|
|
||||||
---
|
|
||||||
|
|
||||||
<Layout title="Max Richter">
|
|
||||||
{resources.map((resource) => <HeroCard post={resource} />)}
|
|
||||||
</Layout>
|
|
@@ -1,63 +0,0 @@
|
|||||||
const collection = "resources";
|
|
||||||
|
|
||||||
type Resource = {
|
|
||||||
id: string;
|
|
||||||
collection: string;
|
|
||||||
body: string;
|
|
||||||
data: {
|
|
||||||
title: string;
|
|
||||||
icon: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// const wiki = {
|
|
||||||
// id: "wiki",
|
|
||||||
// collection,
|
|
||||||
// body: "My knowledge base",
|
|
||||||
// data: {
|
|
||||||
// title: "Wiki",
|
|
||||||
// icon: "🧠",
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const articles = {
|
|
||||||
// id: "Articles",
|
|
||||||
// collection,
|
|
||||||
// body: "Articles saved",
|
|
||||||
// data: {
|
|
||||||
// title: "Articles",
|
|
||||||
// icon: "📰",
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
const recipes = {
|
|
||||||
id: "Recipes",
|
|
||||||
collection,
|
|
||||||
body: "Recipes",
|
|
||||||
data: {
|
|
||||||
title: "Recipes",
|
|
||||||
icon: "🍲",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// const movies = {
|
|
||||||
// id: "Movies",
|
|
||||||
// collection,
|
|
||||||
// body: "Movies",
|
|
||||||
// data: {
|
|
||||||
// title: "Movies",
|
|
||||||
// icon: "🎥",
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const series = {
|
|
||||||
// id: "Series",
|
|
||||||
// collection,
|
|
||||||
// body: "Series",
|
|
||||||
// data: {
|
|
||||||
// title: "Series",
|
|
||||||
// icon: "📺",
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
export const resources: Resource[] = [recipes];
|
|
@@ -1,10 +1,5 @@
|
|||||||
import { vitePreprocess } from "@astrojs/svelte";
|
import { vitePreprocess } from '@astrojs/svelte';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
preprocess: vitePreprocess(),
|
preprocess: vitePreprocess(),
|
||||||
compilerOptions: {
|
}
|
||||||
experimental: {
|
|
||||||
async: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
@@ -11,8 +11,7 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": [
|
"types": [
|
||||||
"vite-plugin-glsl/ext",
|
"vite-plugin-glsl/ext",
|
||||||
"unplugin-icons/types",
|
"unplugin-icons/types"
|
||||||
"svelte-gestures/globals"
|
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@components/*": [
|
"@components/*": [
|
||||||
|
Reference in New Issue
Block a user