feat: add madeira blog post upgrade astro

This commit is contained in:
2025-02-16 15:33:45 +01:00
parent c1fefe3f3f
commit b607cbeb2a
48 changed files with 2653 additions and 3235 deletions

View File

@@ -10,7 +10,7 @@ import UnoCSS from 'unocss/astro'
const defaultLocale = "de"; const defaultLocale = "de";
const locales = { const locales = {
en: "en", // the `defaultLocale` value must present in `locales` keys en: "en",
de: "de", de: "de",
}; };
@@ -43,16 +43,16 @@ export default defineConfig({
remarkPlugins: [setDefaultLayout] remarkPlugins: [setDefaultLayout]
}, },
integrations: [ integrations: [
mdx(),
svelte(),
UnoCSS({
injectReset: true
}),
i18n({ i18n({
exclude: ["pages/**/*.json.ts", "pages/api/**/*",], exclude: ["pages/**/*.json.ts", "pages/api/**/*",],
locales, locales,
defaultLocale, defaultLocale,
}), }),
mdx(),
svelte(),
UnoCSS({
injectReset: true
}),
sitemap({ sitemap({
i18n: { i18n: {
locales, locales,

View File

@@ -10,33 +10,33 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.7.0", "@astrojs/check": "^0.9.4",
"@astrojs/mdx": "^3.1.1", "@astrojs/mdx": "^4.0.8",
"@astrojs/svelte": "^5.6.0", "@astrojs/svelte": "^7.0.4",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^6.0.0",
"astro": "^4.11.0", "astro": "^5.3.0",
"astro-i18n-aut": "^0.7.0", "astro-i18n-aut": "^0.7.3",
"exifreader": "^4.23.3", "exifreader": "^4.26.1",
"svelte": "^4.2.18", "svelte": "^5.20.1",
"svelte-gestures": "^5.0.1", "svelte-gestures": "^5.1.3",
"tailwindcss": "^3.4.4", "tailwindcss": "^4.0.6",
"thumbhash": "^0.1.1", "thumbhash": "^0.1.1",
"typescript": "^5.5.2" "typescript": "^5.7.3"
}, },
"devDependencies": { "devDependencies": {
"@astrojs/sitemap": "^3.1.6", "@astrojs/sitemap": "^3.2.1",
"@iconify-json/tabler": "^1.1.114", "@iconify-json/tabler": "^1.2.16",
"@types/markdown-it": "^14.1.1", "@types/markdown-it": "^14.1.2",
"@unocss/preset-icons": "^0.61.0", "@unocss/preset-icons": "^65.5.0",
"@unocss/reset": "^0.61.0", "@unocss/reset": "^65.5.0",
"astro-font": "^0.0.81", "astro-font": "^1.0.0",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"ogl": "^1.0.7", "ogl": "^1.0.11",
"prettier": "^3.3.2", "prettier": "^3.5.1",
"prettier-plugin-astro": "^0.14.0", "prettier-plugin-astro": "^0.14.1",
"sharp": "^0.33.4", "sharp": "^0.33.5",
"unocss": "^0.61.0", "unocss": "^65.5.0",
"unplugin-icons": "^0.19.0", "unplugin-icons": "^22.0.0",
"vite-plugin-glsl": "^1.3.0" "vite-plugin-glsl": "^1.3.1"
} }
} }

5559
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,18 +3,14 @@ import markdownToText from "@helpers/markdownToText";
import { Card } from "./card"; import { Card } from "./card";
import { useTranslatedPath, useTranslations } from "@i18n/utils"; import { useTranslatedPath, useTranslations } from "@i18n/utils";
import Image from "@components/Image.astro"; import Image from "@components/Image.astro";
import type { ImageMetadata } from "astro"; import type { InferEntrySchema } from "astro:content";
interface Props { interface Props {
post: { post: {
data: { data: InferEntrySchema<"projects">;
title: string;
icon?: string;
cover?: ImageMetadata;
};
collection: string; collection: string;
slug: string; id: string;
body: string; body?: string;
}; };
} }
@@ -22,13 +18,13 @@ const {
data: { title, cover, icon }, data: { title, cover, icon },
collection, collection,
body, body,
slug, id,
} = Astro.props.post; } = Astro.props.post;
const translatePath = useTranslatedPath(Astro.url); const translatePath = useTranslatedPath(Astro.url);
const t = useTranslations(Astro.url); const t = useTranslations(Astro.url);
const link = translatePath(`/${collection}/${slug.split("/")[0]}`); const link = translatePath(`/${collection}/${id.split("/")[0]}`);
--- ---
<Card <Card
@@ -39,7 +35,7 @@ const link = translatePath(`/${collection}/${slug.split("/")[0]}`);
{title} {title}
</Card.Title> </Card.Title>
<Card.Description> <Card.Description>
{markdownToText(body).slice(0, 200)} {markdownToText(body ?? "").slice(0, 200)}
</Card.Description> </Card.Description>
<Card.ReadMoreButton link={link} text={t("read-more")} /> <Card.ReadMoreButton link={link} text={t("read-more")} />
</Card.Content> </Card.Content>

View File

@@ -141,6 +141,15 @@
// console.log(ev); // console.log(ev);
}; };
function formatExposureTime(num: string) {
if (num.includes("/")) {
const [a, b] = num.split("/");
return `${a}/${b}s`;
} else {
return num + "s";
}
}
onMount(() => { onMount(() => {
const wrappers = Array.prototype.slice.call( const wrappers = Array.prototype.slice.call(
document.querySelectorAll("picture > img"), document.querySelectorAll("picture > img"),
@@ -168,7 +177,9 @@
? exifData.FocalLength.replace(" mm", "mm") ? exifData.FocalLength.replace(" mm", "mm")
: "", : "",
"FNumber" in exifData ? exifData.FNumber : "", "FNumber" in exifData ? exifData.FNumber : "",
"ExposureTime" in exifData ? exifData.ExposureTime : "", "ExposureTime" in exifData
? formatExposureTime(exifData.ExposureTime)
: "",
]; ];
} }
} catch (error) { } catch (error) {
@@ -206,10 +217,11 @@
<div class="controls"> <div class="controls">
{#each images as _, i} {#each images as _, i}
<button <button
aria-label={`Image ${i + 1}`}
class:active={currentIndex === i} class:active={currentIndex === i}
on:click={() => { on:click={() => {
currentIndex = i; currentIndex = i;
}} /> }}></button>
{/each} {/each}
</div> </div>
{/if} {/if}

View File

@@ -1,20 +1,16 @@
--- ---
import markdownToText from "@helpers/markdownToText"; import markdownToText from "@helpers/markdownToText";
import { useTranslatedPath } from "@i18n/utils"; import { useTranslatedPath } from "@i18n/utils";
import type { InferEntrySchema } from "astro:content";
const tp = useTranslatedPath(Astro.url); const tp = useTranslatedPath(Astro.url);
interface Props { interface Props {
post: { post: {
data: { data: InferEntrySchema<"blog">;
title: string;
description?: string;
icon?: string;
tags?: string[];
};
collection: string; collection: string;
body: string; body?: string;
slug: string; id: string;
}; };
} }
@@ -22,15 +18,14 @@ const { post } = Astro.props;
--- ---
<div class="rounded-diag-md border border-neutral p-4 overflow-hidden"> <div class="rounded-diag-md border border-neutral p-4 overflow-hidden">
<a href={tp(`/${post.collection}/${post.slug.split("/")[0]}`)}> <a href={tp(`/${post.collection}/${post.id.split("/")[0]}`)}>
<h2 <h2
class="text-2xl flex gap-2 items-center line-clamp text-ellipsis overflow-hidden" class="text-2xl flex gap-2 items-center line-clamp text-ellipsis overflow-hidden">
>
{post.data.icon && <img src={post.data.icon} class="h-6" />} {post.data.icon && <img src={post.data.icon} class="h-6" />}
{post.data.title} {post.data.title}
</h2> </h2>
<p class="text-ellipsis overflow-hidden line-clamp-2"> <p class="text-ellipsis overflow-hidden line-clamp-2">
{post.data.description || markdownToText(post.body).slice(0, 200)} {post.data.description || markdownToText(post?.body || "").slice(0, 200)}
</p> </p>
</a> </a>
{ {
@@ -39,8 +34,7 @@ const { post } = Astro.props;
{post.data.tags.map((tag) => ( {post.data.tags.map((tag) => (
<a <a
href={tp(`/tag/${tag}`)} href={tp(`/tag/${tag}`)}
class="text-xs border border-neutral p-2 rounded-md" class="text-xs border border-neutral p-2 rounded-md">
>
{tag} {tag}
</a> </a>
))} ))}

View File

@@ -7,5 +7,5 @@
href={link} href={link}
data-astro-prefetch data-astro-prefetch
class="bg-light p-2 text-s rounded-md px-4 flex flex-0 items-center gap-2 w-fit" class="bg-light p-2 text-s rounded-md px-4 flex flex-0 items-center gap-2 w-fit"
>{text}<span class="i-tabler-arrow-right inline-block w-4 h-4" /> >{text}<span class="i-tabler-arrow-right inline-block w-4 h-4"></span>
</a> </a>

View File

@@ -5,14 +5,7 @@ import Title from './Title.svelte';
import Description from './Description.svelte'; import Description from './Description.svelte';
import ReadMoreButton from './ReadMoreButton.svelte'; import ReadMoreButton from './ReadMoreButton.svelte';
const Card = { const Card = Wrapper as typeof Wrapper & {
...Wrapper,
Image,
Content,
Title,
Description,
ReadMoreButton
} as typeof Wrapper & {
Image: typeof Image; Image: typeof Image;
Content: typeof Content; Content: typeof Content;
Title: typeof Title; Title: typeof Title;
@@ -20,4 +13,10 @@ const Card = {
ReadMoreButton: typeof ReadMoreButton; ReadMoreButton: typeof ReadMoreButton;
} }
Card.Image = Image;
Card.Content = Content;
Card.Title = Title;
Card.Description = Description;
Card.ReadMoreButton = ReadMoreButton;
export { Card }; export { Card };

33
src/content.config.ts Normal file
View File

@@ -0,0 +1,33 @@
import { glob } from 'astro/loaders';
import { defineCollection, z, type ImageFunction } from 'astro:content';
const defaultSchema = ({ image }: { image: ImageFunction }) => z.object({
title: z.string(),
date: z.date(),
cover: image().optional(),
links: z.array(z.array(z.string())).optional(),
coverAlt: z.string().optional(),
toc: z.boolean().optional(),
description: z.string().optional(),
icon: z.string().optional(),
draft: z.boolean().optional(),
featured: z.boolean().optional(),
tags: z.array(z.string()).optional(),
_layout: z.enum(['normal', 'transparent']).optional(),
})
export const collections = {
'blog': defineCollection({
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/blog" }),
schema: defaultSchema,
}),
"projects": defineCollection({
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/projects" }),
schema: defaultSchema,
}),
"photos": defineCollection({
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/photos" }),
schema: defaultSchema,
})
};

View File

@@ -1,26 +0,0 @@
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
schema: ({ image }) => z.object({
title: z.string(),
date: z.date(),
cover: image().refine((img) => img.width >= 720, {
message: "Cover image must be at least 720 pixels wide!",
}).optional(),
links: z.array(z.array(z.string())).optional(),
coverAlt: z.string().optional(),
toc: z.boolean().optional(),
description: z.string().optional(),
icon: z.string().optional(),
draft: z.boolean().optional(),
featured: z.boolean().optional(),
tags: z.array(z.string()).optional(),
_layout: z.enum(['normal', 'transparent']).optional(),
})
});
export const collections = {
'blog': blogCollection,
"projects": blogCollection,
"photos": blogCollection,
};

BIN
src/content/photos/madeira-2025/images/MAX_0354.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0369.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0442.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0447-Panorama.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0463.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0505.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0603.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/images/MAX_0646.jpg (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/content/photos/madeira-2025/index.en.mdx (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/photos/madeira-2025/index.mdx (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -1,4 +1,4 @@
import { defaultLocale, getLocale } from 'astro-i18n-aut'; import { defaultLocale, getLocale, locales } from 'astro-i18n-aut';
import { ui, defaultLang, showDefaultLang } from './ui'; import { ui, defaultLang, showDefaultLang } from './ui';
export function useTranslatedPath(url: URL) { export function useTranslatedPath(url: URL) {
@@ -17,9 +17,13 @@ export function useTranslations(url: URL) {
export function parseSlug(id: string) { export function parseSlug(id: string) {
const splitPath = id.split('/'); const splitPath = id.split('/');
const split = splitPath.pop()?.split('.'); const split = splitPath.pop()?.split('.').map(s => s.replace(/^index/, ""));
const lang = split?.length === 2 ? defaultLocale : split?.[1]; for (const s of split || []) {
return [splitPath.join("/"), lang] if (locales[s]) {
return [splitPath[0] ?? id, s]
}
}
return [splitPath[0] ?? id, defaultLocale]
} }
export function filterCollection<T extends { id: string, data: { draft?: boolean, date?: Date } }>(collection: T[], locale: string): T[] { export function filterCollection<T extends { id: string, data: { draft?: boolean, date?: Date } }>(collection: T[], locale: string): T[] {

View File

@@ -1,16 +1,16 @@
--- ---
import { getCollection } from "astro:content"; import { getCollection, render } from "astro:content";
import { getLocale } from "astro-i18n-aut"; import { getLocale } from "astro-i18n-aut";
import { filterCollection, parseSlug } from "@i18n/utils"; import { filterCollection, parseSlug } from "@i18n/utils";
const locale = getLocale(Astro.url); const locale = getLocale(Astro.url);
export async function getStaticPaths() { export async function getStaticPaths() {
const pages = await getCollection("blog"); const posts = await getCollection("blog");
const paths = pages.map((page) => { const paths = posts.map((post) => {
const [slug] = parseSlug(page.id); const [slug] = parseSlug(post.id);
return { params: { slug }, props: { ...page } }; return { params: { slug }, props: { ...post } };
}); });
return paths; return paths;
@@ -28,7 +28,7 @@ if (!page) {
}); });
} }
const { Content } = await page.render(); const { Content } = await render(page);
--- ---
<Content /> <Content />

View File

@@ -1,5 +1,5 @@
--- ---
import { getCollection } from "astro:content"; import { getCollection, render } from "astro:content";
import { getLocale } from "astro-i18n-aut"; import { getLocale } from "astro-i18n-aut";
import { filterCollection, parseSlug } from "@i18n/utils"; import { filterCollection, parseSlug } from "@i18n/utils";
@@ -19,6 +19,7 @@ export async function getStaticPaths() {
const pages = await getCollection("photos"); const pages = await getCollection("photos");
const page = filterCollection(pages, locale).find((page) => { const page = filterCollection(pages, locale).find((page) => {
const [slug] = parseSlug(page.id); const [slug] = parseSlug(page.id);
console.log({ slug, id: page.id, params: Astro.params.slug, locale });
return slug === Astro.params.slug; return slug === Astro.params.slug;
}); });
@@ -28,7 +29,7 @@ if (!page) {
}); });
} }
const { Content } = await page.render(); const { Content } = await render(page);
--- ---
<Content /> <Content />

View File

@@ -1,5 +1,5 @@
--- ---
import { getCollection } from "astro:content"; import { getCollection, render } from "astro:content";
import { getLocale } from "astro-i18n-aut"; import { getLocale } from "astro-i18n-aut";
import { filterCollection, parseSlug } from "@i18n/utils"; import { filterCollection, parseSlug } from "@i18n/utils";
@@ -28,7 +28,7 @@ if (!page) {
}); });
} }
const { Content } = await page.render(); const { Content } = await render(page);
--- ---
<Content /> <Content />

View File

@@ -1,5 +1,5 @@
import { vitePreprocess } from '@astrojs/svelte'; import { vitePreprocess } from '@astrojs/svelte';
export default { export default {
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
} }

View File

@@ -1,5 +1,9 @@
{ {
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/base",
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [ "exclude": [
"dist" "dist"
], ],