initial implementation of placeholder images, thumbnails and blurring between using sharp
@@ -10,17 +10,19 @@ if(icon_override){
 | 
				
			|||||||
	current_month = recipe.season[0]
 | 
						current_month = recipe.season[0]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let isloaded = false
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
.card{
 | 
					.card{
 | 
				
			||||||
 | 
						flex-shrink: 0;
 | 
				
			||||||
	--card-width: 300px;
 | 
						--card-width: 300px;
 | 
				
			||||||
	text-decoration: none;
 | 
						text-decoration: none;
 | 
				
			||||||
	position: relative;
 | 
						position: relative;
 | 
				
			||||||
	box-sizing: border-box;
 | 
						box-sizing: border-box;
 | 
				
			||||||
	font-family: sans-serif;
 | 
						font-family: sans-serif;
 | 
				
			||||||
	cursor: pointer;
 | 
						cursor: pointer;
 | 
				
			||||||
 | 
						height: calc(7/4 * var(--card-width)); /* otherwise card is not initialized at correct size and readjusts when populated*/
 | 
				
			||||||
	width: var(--card-width);
 | 
						width: var(--card-width);
 | 
				
			||||||
	aspect-ratio: 4/7;
 | 
					 | 
				
			||||||
	border-radius: 20px;
 | 
						border-radius: 20px;
 | 
				
			||||||
	background-size: contain;
 | 
						background-size: contain;
 | 
				
			||||||
	display: flex;
 | 
						display: flex;
 | 
				
			||||||
@@ -28,7 +30,32 @@ if(icon_override){
 | 
				
			|||||||
	justify-content: end;
 | 
						justify-content: end;
 | 
				
			||||||
	background-color:  var(--blue);
 | 
						background-color:  var(--blue);
 | 
				
			||||||
	box-shadow: 0em 0em 2em 0.1em rgba(0, 0, 0, 0.3);
 | 
						box-shadow: 0em 0em 2em 0.1em rgba(0, 0, 0, 0.3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.card #image{
 | 
				
			||||||
 | 
						width: var(--card-width);
 | 
				
			||||||
 | 
						height: calc(var(--card-width)*0.85);
 | 
				
			||||||
 | 
						object-fit: cover;
 | 
				
			||||||
	transition: 200ms;
 | 
						transition: 200ms;
 | 
				
			||||||
 | 
						backdrop-filter: blur(10px);
 | 
				
			||||||
 | 
						filter: blur(10px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.unblur{
 | 
				
			||||||
 | 
						filter: blur(0px) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					div:has(#image){
 | 
				
			||||||
 | 
						width: var(--card-width);
 | 
				
			||||||
 | 
						background-repeat: no-repeat;
 | 
				
			||||||
 | 
						background-size: cover;
 | 
				
			||||||
 | 
						background-position: center;
 | 
				
			||||||
 | 
						overflow: hidden;
 | 
				
			||||||
 | 
						border-top-left-radius: inherit;
 | 
				
			||||||
 | 
						border-top-right-radius: inherit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					div:has(div #image){
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
						width: var(--card-width);
 | 
				
			||||||
 | 
						top: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.card:hover,
 | 
					.card:hover,
 | 
				
			||||||
.card:focus-within{
 | 
					.card:focus-within{
 | 
				
			||||||
@@ -39,12 +66,6 @@ if(icon_override){
 | 
				
			|||||||
.card:active{
 | 
					.card:active{
 | 
				
			||||||
	scale: 0.95 0.95;
 | 
						scale: 0.95 0.95;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.card img{
 | 
					 | 
				
			||||||
	height: 50%;
 | 
					 | 
				
			||||||
	object-fit: cover;
 | 
					 | 
				
			||||||
	border-top-left-radius: inherit;
 | 
					 | 
				
			||||||
	border-top-right-radius: inherit;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.card .title {
 | 
					.card .title {
 | 
				
			||||||
	position: relative;
 | 
						position: relative;
 | 
				
			||||||
	box-sizing: border-box;
 | 
						box-sizing: border-box;
 | 
				
			||||||
@@ -129,14 +150,20 @@ if(icon_override){
 | 
				
			|||||||
.card:hover .icon,
 | 
					.card:hover .icon,
 | 
				
			||||||
.card:focus-visible .icon
 | 
					.card:focus-visible .icon
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	animation:  shake 0.6s
 | 
						animation: shake 0.6s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<a class="card {search}" href="/rezepte/{recipe.short_name}" data-tags=[{recipe.tags}]>
 | 
					<a class="card {search}" href="/rezepte/{recipe.short_name}" data-tags=[{recipe.tags}]>
 | 
				
			||||||
{#if icon_override || recipe.season.includes(current_month)}
 | 
						<div>
 | 
				
			||||||
 | 
						<div style="background-image:url({'https://new.bocken.org/static/rezepte/placeholder/' + recipe.short_name + '.webp'})">
 | 
				
			||||||
 | 
						<img class:unblur={isloaded} id=image src={'https://new.bocken.org/static/rezepte/thumb/' + recipe.short_name + '.webp'} loading=lazy  alt="{recipe.alt}" on:load={() => isloaded=true}/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						{#if icon_override || recipe.season.includes(current_month)}
 | 
				
			||||||
	<a class=icon href="/rezepte/icon/{recipe.icon}">{recipe.icon}</a>
 | 
						<a class=icon href="/rezepte/icon/{recipe.icon}">{recipe.icon}</a>
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
	<img width=300px height=300px src="/images/{recipe.images[0].mediapath}" alt="{recipe.alt}" />
 | 
					
 | 
				
			||||||
	<div class=title>
 | 
						<div class=title>
 | 
				
			||||||
		<a class=category href="/rezepte/category/{recipe.category}" >{recipe.category}</a>
 | 
							<a class=category href="/rezepte/category/{recipe.category}" >{recipe.category}</a>
 | 
				
			||||||
		<div>
 | 
							<div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								src/lib/components/Icon.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import '$lib/css/nordtheme.css';
 | 
				
			||||||
 | 
						import "$lib/css/shake.css"
 | 
				
			||||||
 | 
						export let icon : string;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
						a{
 | 
				
			||||||
 | 
							font-size: 2rem;
 | 
				
			||||||
 | 
							text-decoration: none;
 | 
				
			||||||
 | 
							padding: 0.5em;
 | 
				
			||||||
 | 
							background-color: var(--nord4);
 | 
				
			||||||
 | 
							border-radius: 1000px;
 | 
				
			||||||
 | 
					    		box-shadow: 0em 0em 0.5em 0.2em rgba(0, 0, 0, 0.2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						a:hover{
 | 
				
			||||||
 | 
							--angle: 15deg;
 | 
				
			||||||
 | 
							animation: shake 0.5s ease forwards;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					<a href="/rezepte/icon/{icon}" {...$$restProps} >{icon}</a>
 | 
				
			||||||
@@ -27,7 +27,7 @@ h4{
 | 
				
			|||||||
{#if data.ingredients}
 | 
					{#if data.ingredients}
 | 
				
			||||||
<div class=ingredients>
 | 
					<div class=ingredients>
 | 
				
			||||||
{#if data.portions}
 | 
					{#if data.portions}
 | 
				
			||||||
	<h4>Portionen:</h4>
 | 
						<h3>Portionen:</h3>
 | 
				
			||||||
	{data.portions}
 | 
						{data.portions}
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
<h2>Zutaten</h2>
 | 
					<h2>Zutaten</h2>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
<script>
 | 
					<script>
 | 
				
			||||||
	export let src
 | 
						export let src
 | 
				
			||||||
 | 
						export let placeholder_src
 | 
				
			||||||
 | 
						let isloaded=false
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
:root {
 | 
					:root {
 | 
				
			||||||
@@ -46,7 +48,7 @@
 | 
				
			|||||||
  z-index: -10;
 | 
					  z-index: -10;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.image-container img {
 | 
					#image{
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  top: 0;
 | 
					  top: 0;
 | 
				
			||||||
@@ -54,8 +56,10 @@
 | 
				
			|||||||
  z-index: -1;
 | 
					  z-index: -1;
 | 
				
			||||||
  height: max(60dvh,600px);
 | 
					  height: max(60dvh,600px);
 | 
				
			||||||
  object-fit: cover;
 | 
					  object-fit: cover;
 | 
				
			||||||
  /*object-position: top;*/
 | 
					 | 
				
			||||||
  object-position: 50% 20%;
 | 
					  object-position: 50% 20%;
 | 
				
			||||||
 | 
					  backdrop-filter: blur(20px);
 | 
				
			||||||
 | 
					  transition: 50ms;
 | 
				
			||||||
 | 
					  filter: blur(20px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.image-container::after {
 | 
					.image-container::after {
 | 
				
			||||||
@@ -68,9 +72,33 @@
 | 
				
			|||||||
:global(h1){
 | 
					:global(h1){
 | 
				
			||||||
	width: 100%;
 | 
						width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.placeholder{
 | 
				
			||||||
 | 
						background-repeat: no-repeat;
 | 
				
			||||||
 | 
						background-size: cover;
 | 
				
			||||||
 | 
						background-position: 50% 20%;
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
					        width: min(1000px, 100dvw);
 | 
				
			||||||
 | 
					  	height: max(60dvh,600px);
 | 
				
			||||||
 | 
						z-index: -2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					div:has(.placeholder){
 | 
				
			||||||
 | 
						position: absolute;
 | 
				
			||||||
 | 
						top: 0;
 | 
				
			||||||
 | 
					        width: min(1000px, 100dvw);
 | 
				
			||||||
 | 
					  	height: max(60dvh,600px);
 | 
				
			||||||
 | 
						overflow: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.unblur#image{
 | 
				
			||||||
 | 
						filter: blur(0px) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
<section class="section">
 | 
					<section class="section">
 | 
				
			||||||
    <figure class="image-container"><img {src} alt=""/></figure>
 | 
					    <figure class="image-container">
 | 
				
			||||||
 | 
					    	<div>
 | 
				
			||||||
 | 
							<div class=placeholder style="background-image:url({placeholder_src})" >
 | 
				
			||||||
 | 
								<img class:unblur={isloaded} id=image {src} on:load={() => isloaded=true} alt=""/>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						</figure>
 | 
				
			||||||
    <div class=content><slot></slot></div>
 | 
					    <div class=content><slot></slot></div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ const RecipeSchema = new mongoose.Schema(
 | 
				
			|||||||
     		      steps: [String]}],
 | 
					     		      steps: [String]}],
 | 
				
			||||||
     preamble : String,
 | 
					     preamble : String,
 | 
				
			||||||
     addendum : String,
 | 
					     addendum : String,
 | 
				
			||||||
     },
 | 
					     }, {timestamps: true}
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Recipe = mongoose.model("Recipe", RecipeSchema);
 | 
					export const Recipe = mongoose.model("Recipe", RecipeSchema);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,18 +3,40 @@ import path from 'path'
 | 
				
			|||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
import { BEARER_TOKEN } from '$env/static/private'
 | 
					import { BEARER_TOKEN } from '$env/static/private'
 | 
				
			||||||
import { error } from '@sveltejs/kit';
 | 
					import { error } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import { Image } from '../../../../models/Image';
 | 
				
			||||||
 | 
					import { IMAGE_DIR } from '$env/static/private'
 | 
				
			||||||
 | 
					import sharp from 'sharp';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const POST =  (async ({ request })  => {
 | 
					export const POST =  (async ({ request })  => {
 | 
				
			||||||
    const data = await request.json();
 | 
					    const data = await request.json();
 | 
				
			||||||
    const filePath = path.join(
 | 
					 | 
				
			||||||
            process.cwd(),
 | 
					 | 
				
			||||||
            "static",
 | 
					 | 
				
			||||||
            "images",
 | 
					 | 
				
			||||||
	    data.filename as string
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    const file = data.image;
 | 
					 | 
				
			||||||
    if(data.bearer === BEARER_TOKEN){
 | 
					    if(data.bearer === BEARER_TOKEN){
 | 
				
			||||||
    	writeFileSync(filePath, file, 'base64');
 | 
						let full_res = new Buffer.from(data.image, 'base64')
 | 
				
			||||||
 | 
						// reduce image size if over 500KB
 | 
				
			||||||
 | 
						const MAX_SIZE_KB = 500
 | 
				
			||||||
 | 
						//const metadata = await sharp(full_res).metadata()
 | 
				
			||||||
 | 
						////reduce image size if larger than 500KB
 | 
				
			||||||
 | 
						//if(metadata.size > MAX_SIZE_KB*1000){
 | 
				
			||||||
 | 
						//	full_res = sharp(full_res).
 | 
				
			||||||
 | 
						//		webp( { quality: 70})
 | 
				
			||||||
 | 
						//		.toBuffer()
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
						await sharp(full_res)
 | 
				
			||||||
 | 
							.toFormat('webp')
 | 
				
			||||||
 | 
							.toFile(path.join(IMAGE_DIR,
 | 
				
			||||||
 | 
									  "full",
 | 
				
			||||||
 | 
									  data.name + ".webp"))
 | 
				
			||||||
 | 
						await sharp(full_res)
 | 
				
			||||||
 | 
							.resize({ width: 800})
 | 
				
			||||||
 | 
							.toFormat('webp')
 | 
				
			||||||
 | 
							.toFile(path.join(IMAGE_DIR,
 | 
				
			||||||
 | 
								  "thumb",
 | 
				
			||||||
 | 
								  data.name + ".webp"))
 | 
				
			||||||
 | 
						await sharp(full_res)
 | 
				
			||||||
 | 
							.resize({ width: 20})
 | 
				
			||||||
 | 
							.toFormat('webp')
 | 
				
			||||||
 | 
							.toFile(path.join(IMAGE_DIR,
 | 
				
			||||||
 | 
								  "placeholder",
 | 
				
			||||||
 | 
								  data.name + ".webp"))
 | 
				
			||||||
	return new Response(JSON.stringify({msg: "Added image successfully"}),{
 | 
						return new Response(JSON.stringify({msg: "Added image successfully"}),{
 | 
				
			||||||
			    status: 200,
 | 
								    status: 200,
 | 
				
			||||||
  	});
 | 
					  	});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								static/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.5 KiB  | 
| 
		 Before Width: | Height: | Size: 209 KiB  | 
| 
		 Before Width: | Height: | Size: 229 KiB  | 
| 
		 Before Width: | Height: | Size: 190 KiB  | 
| 
		 Before Width: | Height: | Size: 502 KiB  | 
| 
		 Before Width: | Height: | Size: 388 KiB  | 
| 
		 Before Width: | Height: | Size: 264 KiB  | 
| 
		 Before Width: | Height: | Size: 127 KiB  | 
| 
		 Before Width: | Height: | Size: 236 KiB  | 
| 
		 Before Width: | Height: | Size: 458 KiB  | 
| 
		 Before Width: | Height: | Size: 439 KiB  | 
| 
		 Before Width: | Height: | Size: 228 KiB  | 
| 
		 Before Width: | Height: | Size: 489 KiB  | 
| 
		 Before Width: | Height: | Size: 300 KiB  | 
| 
		 Before Width: | Height: | Size: 295 KiB  | 
| 
		 Before Width: | Height: | Size: 122 KiB  | 
| 
		 Before Width: | Height: | Size: 263 KiB  | 
| 
		 Before Width: | Height: | Size: 354 KiB  | 
| 
		 Before Width: | Height: | Size: 419 KiB  | 
| 
		 Before Width: | Height: | Size: 284 KiB  | 
| 
		 Before Width: | Height: | Size: 200 KiB  | 
| 
		 Before Width: | Height: | Size: 270 KiB  | 
| 
		 Before Width: | Height: | Size: 495 KiB  | 
| 
		 Before Width: | Height: | Size: 270 KiB  | 
| 
		 Before Width: | Height: | Size: 315 KiB  | 
| 
		 Before Width: | Height: | Size: 360 KiB  | 
| 
		 Before Width: | Height: | Size: 408 KiB  | 
| 
		 Before Width: | Height: | Size: 289 KiB  | 
| 
		 Before Width: | Height: | Size: 236 KiB  | 
| 
		 Before Width: | Height: | Size: 383 KiB  | 
| 
		 Before Width: | Height: | Size: 300 KiB  | 
| 
		 Before Width: | Height: | Size: 286 KiB  | 
| 
		 Before Width: | Height: | Size: 300 KiB  | 
| 
		 Before Width: | Height: | Size: 582 KiB  | 
| 
		 Before Width: | Height: | Size: 326 KiB  | 
| 
		 Before Width: | Height: | Size: 98 KiB  | 
| 
		 Before Width: | Height: | Size: 192 KiB  | 
| 
		 Before Width: | Height: | Size: 329 KiB  | 
| 
		 Before Width: | Height: | Size: 260 KiB  | 
| 
		 Before Width: | Height: | Size: 207 KiB  | 
| 
		 Before Width: | Height: | Size: 176 KiB  | 
| 
		 Before Width: | Height: | Size: 127 KiB  | 
| 
		 Before Width: | Height: | Size: 162 KiB  | 
| 
		 Before Width: | Height: | Size: 214 KiB  | 
| 
		 Before Width: | Height: | Size: 326 KiB  | 
| 
		 Before Width: | Height: | Size: 263 KiB  | 
| 
		 Before Width: | Height: | Size: 188 KiB  | 
| 
		 Before Width: | Height: | Size: 246 KiB  | 
| 
		 Before Width: | Height: | Size: 511 KiB  | 
| 
		 Before Width: | Height: | Size: 283 KiB  | 
| 
		 Before Width: | Height: | Size: 395 KiB  | 
| 
		 Before Width: | Height: | Size: 135 KiB  | 
| 
		 Before Width: | Height: | Size: 231 KiB  | 
| 
		 Before Width: | Height: | Size: 288 KiB  | 
| 
		 Before Width: | Height: | Size: 93 KiB  | 
| 
		 Before Width: | Height: | Size: 224 KiB  | 
| 
		 Before Width: | Height: | Size: 286 KiB  | 
| 
		 Before Width: | Height: | Size: 204 KiB  | 
| 
		 Before Width: | Height: | Size: 287 KiB  | 
| 
		 Before Width: | Height: | Size: 220 KiB  | 
| 
		 Before Width: | Height: | Size: 510 KiB  | 
| 
		 Before Width: | Height: | Size: 230 KiB  | 
| 
		 Before Width: | Height: | Size: 315 KiB  | 
| 
		 Before Width: | Height: | Size: 145 KiB  | 
| 
		 Before Width: | Height: | Size: 144 KiB  | 
| 
		 Before Width: | Height: | Size: 226 KiB  | 
| 
		 Before Width: | Height: | Size: 275 KiB  | 
| 
		 Before Width: | Height: | Size: 625 KiB  | 
| 
		 Before Width: | Height: | Size: 461 KiB  | 
| 
		 Before Width: | Height: | Size: 180 KiB  | 
| 
		 Before Width: | Height: | Size: 295 KiB  | 
| 
		 Before Width: | Height: | Size: 452 KiB  | 
| 
		 Before Width: | Height: | Size: 45 KiB  | 
| 
		 Before Width: | Height: | Size: 266 KiB  | 
| 
		 Before Width: | Height: | Size: 326 KiB  | 
| 
		 Before Width: | Height: | Size: 312 KiB  | 
| 
		 Before Width: | Height: | Size: 167 KiB  | 
| 
		 Before Width: | Height: | Size: 220 KiB  | 
| 
		 Before Width: | Height: | Size: 270 KiB  | 
| 
		 Before Width: | Height: | Size: 258 KiB  | 
| 
		 Before Width: | Height: | Size: 236 KiB  | 
| 
		 Before Width: | Height: | Size: 339 KiB  | 
| 
		 Before Width: | Height: | Size: 521 KiB  | 
| 
		 Before Width: | Height: | Size: 360 KiB  | 
| 
		 Before Width: | Height: | Size: 354 KiB  | 
| 
		 Before Width: | Height: | Size: 197 KiB  | 
| 
		 Before Width: | Height: | Size: 196 KiB  | 
| 
		 Before Width: | Height: | Size: 497 KiB  | 
| 
		 Before Width: | Height: | Size: 225 KiB  | 
| 
		 Before Width: | Height: | Size: 330 KiB  | 
| 
		 Before Width: | Height: | Size: 330 KiB  | 
| 
		 Before Width: | Height: | Size: 409 KiB  | 
| 
		 Before Width: | Height: | Size: 58 KiB  | 
| 
		 Before Width: | Height: | Size: 372 KiB  |