feat(print): add print-optimized route with server-side portion scaling

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 15:41:20 +02:00
parent 0635c2702a
commit a22fb86479

View File

@@ -0,0 +1,140 @@
<script lang="ts">
import { onMount } from 'svelte';
let { data } = $props();
function formatQty(q: number | null): string {
if (q === null) return '';
if (Number.isInteger(q)) return String(q);
return q.toLocaleString('de-DE', { maximumFractionDigits: 2 });
}
onMount(() => {
setTimeout(() => window.print(), 250);
});
</script>
<article class="print">
<header>
<h1>{data.recipe.title}</h1>
<p class="meta">
{data.servings} {data.recipe.servings_unit ?? 'Portionen'}
{#if data.recipe.prep_time_min}· Vorb. {data.recipe.prep_time_min} min{/if}
{#if data.recipe.cook_time_min}· Kochen {data.recipe.cook_time_min} min{/if}
{#if data.recipe.source_url}
· Quelle: {data.recipe.source_domain}
{/if}
</p>
</header>
{#if data.recipe.image_path}
<img src={`/images/${data.recipe.image_path}`} alt="" />
{/if}
<section>
<h2>Zutaten</h2>
<ul class="ingredients">
{#each data.ingredients as ing, i (i)}
<li>
<span class="qty">
{formatQty(ing.quantity)}{#if ing.unit}{' '}{ing.unit}{/if}
</span>
<span class="name">
{ing.name}{#if ing.note}{' '}<em>({ing.note})</em>{/if}
</span>
</li>
{/each}
</ul>
</section>
<section>
<h2>Zubereitung</h2>
<ol class="steps">
{#each data.recipe.steps as step (step.position)}
<li>{step.text}</li>
{/each}
</ol>
</section>
{#if data.recipe.source_url}
<footer>
<p>Quelle: <a href={data.recipe.source_url}>{data.recipe.source_url}</a></p>
</footer>
{/if}
</article>
<style>
:global(body) {
background: white !important;
}
:global(header.bar),
:global(nav.tabs) {
display: none !important;
}
.print {
font-family: Georgia, 'Liberation Serif', serif;
color: #111;
max-width: 190mm;
margin: 0 auto;
padding: 1rem;
}
h1 {
font-size: 2rem;
margin: 0 0 0.4rem;
}
.meta {
color: #555;
margin: 0 0 1rem;
font-size: 0.95rem;
}
img {
max-width: 50%;
float: right;
margin: 0 0 1rem 1rem;
border-radius: 6px;
}
h2 {
font-size: 1.15rem;
border-bottom: 1px solid #bbb;
padding-bottom: 0.25rem;
margin-top: 1.5rem;
}
.ingredients {
list-style: none;
padding: 0;
margin: 0;
columns: 2;
column-gap: 1.5rem;
}
.ingredients li {
break-inside: avoid;
padding: 0.25rem 0;
font-size: 0.95rem;
}
.qty {
display: inline-block;
min-width: 5rem;
font-weight: 600;
}
.steps {
padding-left: 1.25rem;
}
.steps li {
margin-bottom: 0.6rem;
line-height: 1.4;
}
footer {
margin-top: 2rem;
color: #666;
font-size: 0.85rem;
text-align: center;
}
@media print {
@page {
margin: 15mm;
}
.print {
padding: 0;
}
}
</style>