Initial SvelteKit frontend port of windwiderstand.de
Deploy / verify (push) Failing after 32s
Deploy / deploy (push) Has been skipped

Full parity with Astro site: content rows, post/tag routes, pagination,
event badges + OSM map, comments, Live-Search via /api/search-index,
CMS image proxy, RSS, sitemap.

Deploy: Dockerfile + docker-compose.prod.yml + Gitea Actions workflow
to build + push to git.pm86.de registry and ssh-deploy to Contabo.
This commit is contained in:
Peter Meier
2026-04-17 22:01:30 +02:00
parent 852cef0980
commit 2fef91a548
137 changed files with 28585 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
/**
* Layout für Content-Blöcke (z. B. Markdown): Grid-Spalten (112) und Abstand.
* Entspricht component_layout im CMS (desktop/tablet/mobile = Breite in 12tel, spaceBottom = rem).
*/
export type BlockLayout = {
/** Breite auf Desktop (112), z. B. "8" = 8/12. */
desktop?:
| "1"
| "2"
| "3"
| "4"
| "5"
| "6"
| "7"
| "8"
| "9"
| "10"
| "11"
| "12";
/** Breite auf Tablet (112). */
tablet?:
| "1"
| "2"
| "3"
| "4"
| "5"
| "6"
| "7"
| "8"
| "9"
| "10"
| "11"
| "12";
/** Breite auf Mobile (112), Default 12. */
mobile?:
| "1"
| "2"
| "3"
| "4"
| "5"
| "6"
| "7"
| "8"
| "9"
| "10"
| "11"
| "12";
/** Abstand nach unten (rem): 0, 0.5, 1, 1.5, 2. */
spaceBottom?: 0 | 0.5 | 1 | 1.5 | 2;
/** true = Block fullwidth aus dem Grid ausbrechen (viewport-breit). */
breakout?: boolean;
};
const COL_SPAN: Record<string, string> = {
"1": "col-span-1",
"2": "col-span-2",
"3": "col-span-3",
"4": "col-span-4",
"5": "col-span-5",
"6": "col-span-6",
"7": "col-span-7",
"8": "col-span-8",
"9": "col-span-9",
"10": "col-span-10",
"11": "col-span-11",
"12": "col-span-12",
};
const MD_COL_SPAN: Record<string, string> = {
"1": "md:col-span-1",
"2": "md:col-span-2",
"3": "md:col-span-3",
"4": "md:col-span-4",
"5": "md:col-span-5",
"6": "md:col-span-6",
"7": "md:col-span-7",
"8": "md:col-span-8",
"9": "md:col-span-9",
"10": "md:col-span-10",
"11": "md:col-span-11",
"12": "md:col-span-12",
};
const LG_COL_SPAN: Record<string, string> = {
"1": "lg:col-span-1",
"2": "lg:col-span-2",
"3": "lg:col-span-3",
"4": "lg:col-span-4",
"5": "lg:col-span-5",
"6": "lg:col-span-6",
"7": "lg:col-span-7",
"8": "lg:col-span-8",
"9": "lg:col-span-9",
"10": "lg:col-span-10",
"11": "lg:col-span-11",
"12": "lg:col-span-12",
};
const SPACE_BOTTOM: Record<number, string> = {
0: "mb-0",
0.5: "mb-2", // 0.5rem
1: "mb-4", // 1rem
1.5: "mb-6", // 1.5rem
2: "mb-8", // 2rem
};
/**
* Gibt Tailwind-Klassen für das Block-Layout zurück (12-Spalten-Grid + Abstand).
* Parent muss grid grid-cols-12 haben.
*/
/**
* Gibt Tailwind-Klassen für den Block-Inhalt zurück.
* Bei breakout: true nur w-full + Abstand (Spalten/Wrapper kommen von außen).
*/
export function getBlockLayoutClasses(
layout: BlockLayout | undefined | null,
): string {
if (!layout || typeof layout !== "object") {
return "col-span-12 mb-4";
}
const breakout = layout.breakout === true;
const sb =
typeof layout.spaceBottom === "number"
? layout.spaceBottom
: Number(layout.spaceBottom);
const spaceClass = SPACE_BOTTOM[sb as keyof typeof SPACE_BOTTOM] ?? "mb-4";
if (breakout) {
return "w-full";
}
const mobile = String(layout.mobile ?? "12");
const tablet = layout.tablet != null ? String(layout.tablet) : undefined;
const desktop = layout.desktop != null ? String(layout.desktop) : undefined;
const parts: string[] = [
COL_SPAN[mobile] ?? "col-span-12",
tablet ? (MD_COL_SPAN[tablet] ?? "") : "",
desktop ? (LG_COL_SPAN[desktop] ?? "") : "",
spaceClass,
];
return parts.filter(Boolean).join(" ");
}
/** Tailwind-Klasse für spaceBottom (z. B. am Breakout-Wrapper). */
export function getSpaceBottomClass(
layout: BlockLayout | undefined | null,
): string {
if (!layout || typeof layout !== "object") return "mb-4";
const sb =
typeof layout.spaceBottom === "number"
? layout.spaceBottom
: Number(layout.spaceBottom);
return SPACE_BOTTOM[sb as keyof typeof SPACE_BOTTOM] ?? "mb-4";
}
/** Prüft, ob das Layout breakout (fullwidth) haben soll. */
export function isBlockLayoutBreakout(
layout: BlockLayout | undefined | null,
): boolean {
return !!(layout && typeof layout === "object" && layout.breakout === true);
}