@@ -15,13 +15,15 @@ interface Props {
|
||||
|
||||
async function checkImage(src: string) {
|
||||
try {
|
||||
if (src.startsWith("/@fs") || src.startsWith("/_astro")) return true;
|
||||
const res = await fetch(src);
|
||||
if (res.ok) {
|
||||
return src;
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
return false;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
console.log("Failed to fetch: ",src);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +37,7 @@ const {
|
||||
} = Astro.props;
|
||||
|
||||
let thumbhash = hash && image.fsPath ? await generateThumbHash(image) : "";
|
||||
|
||||
const imageSrc = await checkImage(image.src);
|
||||
const imageOk = await checkImage(image.src);
|
||||
|
||||
let exif = await getExifData(image);
|
||||
|
||||
@@ -60,9 +61,9 @@ const sizes = [
|
||||
---
|
||||
|
||||
{
|
||||
imageSrc ? (
|
||||
imageOk ? (
|
||||
<AstroImage
|
||||
src={imageSrc}
|
||||
src={image}
|
||||
alt={alt}
|
||||
data-thumbhash={thumbhash}
|
||||
data-exif={JSON.stringify(exif)}
|
||||
|
||||
BIN
src/content/projects/silvester/images/cover.png
Normal file
|
After Width: | Height: | Size: 1007 KiB |
BIN
src/content/projects/silvester/images/eif-anim-1.png
Normal file
|
After Width: | Height: | Size: 339 KiB |
BIN
src/content/projects/silvester/images/eif-anim-2.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
src/content/projects/silvester/images/eif-anim-3.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
src/content/projects/silvester/images/eif-navigation.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
src/content/projects/silvester/images/eif-teaser.mp4
Normal file
BIN
src/content/projects/silvester/images/hp-1-start.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
src/content/projects/silvester/images/hp-2-start.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/content/projects/silvester/images/hp-3-need-name.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/content/projects/silvester/images/hp-4-which-house.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/content/projects/silvester/images/hp-5-select-house.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/content/projects/silvester/images/hp-7-invitation-card.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
src/content/projects/silvester/images/occult-1.png
Normal file
|
After Width: | Height: | Size: 664 KiB |
BIN
src/content/projects/silvester/images/occult-2.png
Normal file
|
After Width: | Height: | Size: 701 KiB |
BIN
src/content/projects/silvester/images/occult-3.png
Normal file
|
After Width: | Height: | Size: 743 KiB |
BIN
src/content/projects/silvester/images/occult-4.png
Normal file
|
After Width: | Height: | Size: 646 KiB |
BIN
src/content/projects/silvester/images/party-placeholder.jpg
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
src/content/projects/silvester/images/venice-1-start.png
Normal file
|
After Width: | Height: | Size: 584 KiB |
BIN
src/content/projects/silvester/images/venice-2-mask.png
Normal file
|
After Width: | Height: | Size: 376 KiB |
|
After Width: | Height: | Size: 322 KiB |
|
After Width: | Height: | Size: 287 KiB |
BIN
src/content/projects/silvester/images/venice-5-portrait.png
Normal file
|
After Width: | Height: | Size: 844 KiB |
BIN
src/content/projects/silvester/images/venice-6-gallery.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
91
src/content/projects/silvester/index.mdx
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
date: 2025-10-21
|
||||
title: "Silvester-Partys: Eine Retrospektive"
|
||||
draft: true
|
||||
cover: ./images/cover.png
|
||||
description: "Eine Übersicht über unsere jährlichen Silvester-Partys, von Gatsby bis Okkult."
|
||||
tags: ["event", "webdev", "party", "design"]
|
||||
icon: 🎉
|
||||
---
|
||||
|
||||
import Image from "@components/Image.astro"
|
||||
import ImageSlider from "@components/ImageSlider.svelte"
|
||||
import GatsbyPartyScreenshot from "./images/party-placeholder.jpg"
|
||||
import GatsbyPartyPhoto from "./images/party-placeholder.jpg"
|
||||
import HarryPotterPartyScreenshot from "./images/party-placeholder.jpg"
|
||||
import HarryPotterPartyPhoto from "./images/party-placeholder.jpg"
|
||||
import VenicePartyScreenshot from "./images/party-placeholder.jpg"
|
||||
import VenicePartyPhoto from "./images/party-placeholder.jpg"
|
||||
import FashionPartyScreenshot from "./images/party-placeholder.jpg"
|
||||
import FashionPartyPhoto from "./images/party-placeholder.jpg"
|
||||
import ImageGallery from "@components/ImageGallery.svelte"
|
||||
import videoUrl from "./images/eif-teaser.mp4?url"
|
||||
|
||||
<ImageGallery client:load/>
|
||||
|
||||
Seit 2019 veranstaltet meine WG eine Silvester Party. Dafür haben wir jedes Jahr ein besonderes Motto ausgewählt welches dann die Kostüme und die Dekoration bestimmt.
|
||||
Ich hab die Parties immer zum Anlass genommen um mich einmal kreativ auszuleben und habe für jede Party mal einfachere mal komplexere Digital Einladungskarten gestaltet.
|
||||
|
||||
### 2019-2020: The Great Gatsby
|
||||
|
||||
Unsere erste Party stand unter dem Motto "Great Gatsby".
|
||||
|
||||
<ImageSlider title="The Great Gatsby" client:load>
|
||||
<Image src={GatsbyPartyScreenshot} alt="Screenshot der Gatsby Party Visualisierung" />
|
||||
<Image src={GatsbyPartyPhoto} alt="Foto der Gatsby Party" />
|
||||
</ImageSlider>
|
||||
|
||||
### 2022-2023: Harry Potter und der sprechende Hut
|
||||
|
||||
Für den Jahreswechsel 2022-2023 verwandelte sich unsere Wohnung in die Große Halle von Hogwarts. Das Thema war Harry Potter, und wir hatten einen WebGL-basierten sprechenden Hut, der die Gäste interaktiv den Häusern zuordnete. Zusätzlich gab es ein interaktives Harry-Potter-Quiz auf unserer Webseite. Ein visuelles Display zeigte live den Punktestand der einzelnen Häuser an, wobei ein Admin Punkte in Echtzeit hinzufügen oder abziehen konnte.
|
||||
|
||||
Seit 2022 nutzen wir ein Google Sheet als Backend für die Partyorganisation. Jeder Gast gibt bei der Anmeldung eine Wahrscheinlichkeit (in Prozent von 0-100) an, wie wahrscheinlich er oder sie teilnehmen wird. Dies ermöglicht uns eine genauere Vorhersage der Gästezahl.
|
||||
|
||||
<ImageSlider title="Harry Potter und der sprechende Hut" client:load>
|
||||
<Image src={HarryPotterPartyScreenshot} alt="Screenshot der Harry Potter Party Webseite" />
|
||||
<Image src={HarryPotterPartyPhoto} alt="Foto der Harry Potter Party" />
|
||||
<Image src={import("./images/hp-1-start.png")} alt="Harry Potter Party Start" />
|
||||
<Image src={import("images/hp-2-start.png")} alt="Harry Potter Party Start 2" />
|
||||
<Image src={import("images/hp-3-need-name.png")} alt="Harry Potter Party Need Name" />
|
||||
<Image src={import("images/hp-4-which-house.png")} alt="Harry Potter Party Which House" />
|
||||
<Image src={import("images/hp-5-select-house.png")} alt="Harry Potter Party Select House" />
|
||||
<Image src={import("images/hp-6-attendance-probability.png")} alt="Harry Potter Party Attendance Probability" />
|
||||
<Image src={import("images/hp-7-invitation-card.png")} alt="Harry Potter Party Invitation Card" />
|
||||
</ImageSlider>
|
||||
|
||||
### 2023-2024: Venezianischer Maskenball
|
||||
|
||||
Unsere Silvesterparty 2023-2024 entführte unsere Gäste nach Venedig zu einem opulenten Maskenball. Bei der Registrierung auf unserer Webseite erhielt jeder Gast einen einzigartigen Adelstitel und ein passendes Porträt, die dann in einer digitalen Galerie auf der Webseite ausgestellt wurden.
|
||||
|
||||
**Organisation:** Auch hier kam unser Google Sheet Backend zum Einsatz, um die Gästeanmeldungen und Wahrscheinlichkeiten zu verwalten.
|
||||
|
||||
<ImageSlider title="Venezianischer Maskenball" client:load>
|
||||
<Image src={VenicePartyScreenshot} alt="Screenshot der Venezianischer Maskenball Galerie" />
|
||||
<Image src={VenicePartyPhoto} alt="Foto der Venezianischer Maskenball Party" />
|
||||
<Image src={import("images/venice-1-start.png")} alt="Venice Party Start" />
|
||||
<Image src={import("images/venice-2-mask.png")} alt="Venice Party Mask" />
|
||||
<Image src={import("images/venice-3-invitation-test.png")} alt="Venice Party Invitation Test" />
|
||||
<Image src={import("images/venice-4-generate-portrait.png")} alt="Venice Party Generate Portrait" />
|
||||
<Image src={import("images/venice-5-portrait.png")} alt="Venice Party Portrait" />
|
||||
<Image src={import("images/venice-6-gallery.png")} alt="Venice Party Gallery" />
|
||||
</ImageSlider>
|
||||
|
||||
### 2024-2025: Everything is Fashion
|
||||
|
||||
Das Motto für 2024-2025 war "Everything is Fashion". Die Party begann mit einem animierten Intro, das die Gäste auf das modische Thema einstimmte.
|
||||
|
||||
<video src={videoUrl} controls alt="Fashion Party Teaser Video" />
|
||||
|
||||
<ImageSlider title="Everything is Fashion" client:load>
|
||||
<Image src={FashionPartyScreenshot} alt="Screenshot des Fashion Party Intros" />
|
||||
<Image src={FashionPartyPhoto} alt="Foto der Fashion Party" />
|
||||
<Image src={import("images/eif-anim-1.png")} alt="Fashion Party Animation 1" />
|
||||
<Image src={import("images/eif-anim-2.png")} alt="Fashion Party Animation 2" />
|
||||
<Image src={import("images/eif-anim-3.png")} alt="Fashion Party Animation 3" />
|
||||
<Image src={import("images/eif-navigation.png")} alt="Fashion Party Navigation" />
|
||||
</ImageSlider>
|
||||
|
||||
### 2025-2026: Okkult
|
||||
|
||||
Für die kommende Silvesterparty 2025-2026 planen wir ein "Okkult"-Thema, das eine mystische und geheimnisvolle Atmosphäre schaffen wird.
|
||||
|
||||
@@ -5,7 +5,7 @@ export type MemoriumFile = {
|
||||
modTime: string;
|
||||
mime: string;
|
||||
size: string;
|
||||
content: unknown;
|
||||
content: any;
|
||||
};
|
||||
|
||||
export type MemoriumDir = {
|
||||
|
||||
@@ -5,39 +5,42 @@ import * as memorium from "@helpers/memorium";
|
||||
import { resources as resourceTypes } from "../resources.ts";
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
|
||||
const { resourceType, resourceName } = Astro.params;
|
||||
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$/,"")
|
||||
},
|
||||
};
|
||||
});
|
||||
}));
|
||||
export async function getStaticPaths() {
|
||||
try {
|
||||
const paths = await Promise.all(
|
||||
resourceTypes.map(async (resourceType) => {
|
||||
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;
|
||||
return paths.flat().filter(Boolean);
|
||||
}catch(err){
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const resource = await memorium.listResource(
|
||||
`/${resourceType}/${resourceName}.md`
|
||||
`/${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)}>
|
||||
<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">
|
||||
@@ -55,11 +58,19 @@ const resource = await memorium.listResource(
|
||||
<p>{resource?.content?.description}</p>
|
||||
<h2>Ingredients</h2>
|
||||
<ul>
|
||||
{resource.content.recipeIngredient.map(ingredient => <li>{markdownToText(ingredient)}</li>)}
|
||||
{
|
||||
resource.content.recipeIngredient.map((ingredient) => (
|
||||
<li>{markdownToText(ingredient)}</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<h2>Steps</h2>
|
||||
<ol>
|
||||
{resource.content.recipeInstructions.map(ingredient => <li>{markdownToText(ingredient)}</li>)}
|
||||
{
|
||||
resource.content.recipeInstructions.map((ingredient) => (
|
||||
<li>{markdownToText(ingredient)}</li>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
</Layout>
|
||||
|
||||
@@ -8,12 +8,12 @@ const { resourceType } = Astro.params;
|
||||
|
||||
const resources = await memorium.listResource(resourceType);
|
||||
|
||||
export async function getStaticPaths(): string[] {
|
||||
export async function getStaticPaths() {
|
||||
return resourceTypes.map((type: any) => {
|
||||
return {
|
||||
params: {
|
||||
resourceType: type.id,
|
||||
resourceName: "Recipe"
|
||||
resourceName: "Recipe",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -21,17 +21,22 @@ export async function getStaticPaths(): string[] {
|
||||
---
|
||||
|
||||
<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}`}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))
|
||||
{
|
||||
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>
|
||||
|
||||