From 155d9fb4b07024f35dfd0c191a3dce8f8a5552dc Mon Sep 17 00:00:00 2001 From: Peter Meier Date: Sun, 7 Jun 2026 13:14:14 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Social-Bild-Generator=20auch=20f=C3=BCr?= =?UTF-8?q?=20Posts=20(Endpoint,=20Modal,=20PostActions-Button)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neuer Endpoint /api/social/post/[slug] mit gleichem Pattern wie für Termine: Logo, Tag-Pill aus Title/Tags-Keywords (Presse, Demo, Rückblick, Interview, Studie, Rechtlich, Meinung etc.), Datum, Headline mit dynamischer Fontsize, Subheadline/Excerpt-Snippet, QR zum Beitrag, Footer. 3 Formate × 3 Themes wie Kalender. SocialImageModal in eigene wiederverwendbare Komponente extrahiert und sowohl im PostActions als auch im CalendarBlock eingebunden. PostActions akzeptiert socialSlug-Prop und blendet bei vorhandenem Slug einen Modal-Trigger-Button neben den anderen Aktions-Icons ein. Co-Authored-By: Claude Sonnet 4.6 --- src/lib/components/PostActions.svelte | 29 ++ src/lib/components/SocialImageModal.svelte | 140 +++++++ .../components/blocks/CalendarBlock.svelte | 150 +------ src/routes/api/social/post/[slug]/+server.ts | 375 ++++++++++++++++++ src/routes/post/[slug]/+page.server.ts | 1 + src/routes/post/[slug]/+page.svelte | 1 + 6 files changed, 555 insertions(+), 141 deletions(-) create mode 100644 src/lib/components/SocialImageModal.svelte create mode 100644 src/routes/api/social/post/[slug]/+server.ts diff --git a/src/lib/components/PostActions.svelte b/src/lib/components/PostActions.svelte index 592698a..05a00bb 100644 --- a/src/lib/components/PostActions.svelte +++ b/src/lib/components/PostActions.svelte @@ -5,6 +5,8 @@ import { useTranslate, T } from "$lib/translations"; import QrButton from "$lib/components/QrButton.svelte"; + import SocialImageModal from "$lib/components/SocialImageModal.svelte"; + let { url, title, @@ -12,6 +14,7 @@ commentPageId = null, commentEnvironment, commentTargetId = "comments-section", + socialSlug = null, class: cls = "", }: { /** Absolute URL of the page being shared. */ @@ -26,9 +29,13 @@ commentEnvironment?: string; /** DOM id of the comment accordion (used by the count-badge click). */ commentTargetId?: string; + /** Wenn gesetzt: zeigt Social-Bild-Button, der Modal öffnet. */ + socialSlug?: string | null; class?: string; } = $props(); + let socialModalOpen = $state(false); + const t = useTranslate(); // ── Comment count fetch (only when commentPageId is set) ──────────────── @@ -195,6 +202,17 @@ > + {#if socialSlug} + + {/if} + {#if socialSlug} + (socialModalOpen = false)} + /> + {/if} + {#if readingTimeMinutes != null && readingTimeMinutes > 0} + import Icon from "@iconify/svelte"; + import "$lib/iconify-offline"; + + let { + open, + title, + slug, + endpointBase, + downloadPrefix = "social", + onclose, + }: { + open: boolean; + title: string; + slug: string; + /** "/api/social/calendar" oder "/api/social/post" */ + endpointBase: string; + /** Prefix für Download-Dateinamen, z.B. "termin" oder "beitrag" */ + downloadPrefix?: string; + onclose: () => void; + } = $props(); + + let theme = $state<"wald" | "transparent-onlight" | "transparent-ondark">("wald"); + + const themeQ = $derived(theme === "wald" ? "" : `&theme=${theme}`); + const ogUrl = $derived(`${endpointBase}/${slug}?format=og${themeQ}`); + const sqUrl = $derived(`${endpointBase}/${slug}?format=square${themeQ}`); + const storyUrl = $derived(`${endpointBase}/${slug}?format=story${themeQ}`); + const previewBg = $derived( + theme === "wald" + ? "bg-stein-50" + : theme === "transparent-onlight" + ? "bg-[repeating-linear-gradient(45deg,#f5f5f4,#f5f5f4_10px,#e7e5e4_10px,#e7e5e4_20px)]" + : "bg-[repeating-linear-gradient(45deg,#292524,#292524_10px,#1c1917_10px,#1c1917_20px)]", + ); + + +{#if open} + +{/if} diff --git a/src/lib/components/blocks/CalendarBlock.svelte b/src/lib/components/blocks/CalendarBlock.svelte index ba81535..3a21952 100644 --- a/src/lib/components/blocks/CalendarBlock.svelte +++ b/src/lib/components/blocks/CalendarBlock.svelte @@ -16,6 +16,7 @@ import "$lib/iconify-offline"; import Icon from "@iconify/svelte"; import ImageModal from "$lib/components/ImageModal.svelte"; + import SocialImageModal from "$lib/components/SocialImageModal.svelte"; type EventCardItem = CalendarItemData & { start: Date; @@ -447,7 +448,6 @@ let modalImage = $state<{ src: string; alt: string } | null>(null); let linkedSlug = $state(null); let socialModal = $state<{ slug: string; title: string } | null>(null); - let socialTheme = $state<"wald" | "transparent-onlight" | "transparent-ondark">("wald"); function openSocial(item: EventCardItem) { if (!item._slug) return; @@ -1294,146 +1294,14 @@ {/if} {#if socialModal} - {@const themeQ = socialTheme === "wald" ? "" : `&theme=${socialTheme}`} - {@const ogUrl = `/api/social/calendar/${socialModal.slug}?format=og${themeQ}`} - {@const sqUrl = `/api/social/calendar/${socialModal.slug}?format=square${themeQ}`} - {@const storyUrl = `/api/social/calendar/${socialModal.slug}?format=story${themeQ}`} - {@const previewBg = - socialTheme === "wald" - ? "bg-stein-50" - : socialTheme === "transparent-onlight" - ? "bg-[repeating-linear-gradient(45deg,#f5f5f4,#f5f5f4_10px,#e7e5e4_10px,#e7e5e4_20px)]" - : "bg-[repeating-linear-gradient(45deg,#292524,#292524_10px,#1c1917_10px,#1c1917_20px)]"} - + {/if}