diff --git a/src/lib/components/PostActions.svelte b/src/lib/components/PostActions.svelte
new file mode 100644
index 0000000..7045e3a
--- /dev/null
+++ b/src/lib/components/PostActions.svelte
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+ {#if readingTimeMinutes != null && readingTimeMinutes > 0}
+
+
+ {t(T.post_action_reading_time, { minutes: readingTimeMinutes })}
+
+ {/if}
+
diff --git a/src/lib/translations.ts b/src/lib/translations.ts
index 66dcaa9..8dfa41c 100644
--- a/src/lib/translations.ts
+++ b/src/lib/translations.ts
@@ -162,6 +162,12 @@ const TRANSLATION_KEYS = [
"comments_pending_summary_other",
"comments_count_aria",
"comments_jump_to",
+ // Post actions toolbar (share / copy link / print / reading time).
+ "post_action_share",
+ "post_action_copy",
+ "post_action_copied",
+ "post_action_print",
+ "post_action_reading_time",
] as const;
export type TranslationKey = (typeof TRANSLATION_KEYS)[number];
diff --git a/src/routes/post/[slug]/+page.server.ts b/src/routes/post/[slug]/+page.server.ts
index fff18ca..21e523c 100644
--- a/src/routes/post/[slug]/+page.server.ts
+++ b/src/routes/post/[slug]/+page.server.ts
@@ -236,6 +236,19 @@ export const load: PageServerLoad = async ({ params, locals, url, setHeaders })
jsonLd.push(eventLd);
}
+ // Reading-time estimate. ~200 words per minute for German non-fiction.
+ // Strips HTML tags, then counts whitespace-separated tokens. Includes only
+ // the prose body (headline + content); rows aren't part of the prose flow.
+ const readingTimeMinutes = (() => {
+ const plain = (contentHeadlineHtml + " " + contentBodyHtml)
+ .replace(/<[^>]+>/g, " ")
+ .replace(/\s+/g, " ")
+ .trim();
+ if (!plain) return 0;
+ const wordCount = plain.split(" ").length;
+ return Math.max(1, Math.ceil(wordCount / 200));
+ })();
+
return {
post,
preview: !!preview,
@@ -254,6 +267,8 @@ export const load: PageServerLoad = async ({ params, locals, url, setHeaders })
mapEmbedUrl,
mapLinkUrl,
commentPageId: showCommentSection ? slug : null,
+ canonicalUrl: canonical,
+ readingTimeMinutes,
translations,
seoTitle: post.seoTitle ?? postHeadline,
seoDescription: postDescription,
diff --git a/src/routes/post/[slug]/+page.svelte b/src/routes/post/[slug]/+page.svelte
index 93d0272..fcb83e0 100644
--- a/src/routes/post/[slug]/+page.svelte
+++ b/src/routes/post/[slug]/+page.svelte
@@ -7,6 +7,7 @@
import Accordion from "$lib/components/Accordion.svelte";
import CommentCountBadge from "$lib/components/CommentCountBadge.svelte";
import Comments from "$lib/components/Comments.svelte";
+ import PostActions from "$lib/components/PostActions.svelte";
import RustyImage from "$lib/components/RustyImage.svelte";
import { t, T } from "$lib/translations";
import type { PageData } from "./$types";
@@ -76,13 +77,18 @@
-
+
{#if data.commentPageId}
{/if}
+