153 lines
4.6 KiB
Vue
153 lines
4.6 KiB
Vue
<script setup lang="ts">
|
|
import { formatDate } from 'date-fns'
|
|
import { defineRouteRules } from '#imports'
|
|
|
|
const config = useAppConfig()
|
|
const meta = {
|
|
title: 'The Ender Chest',
|
|
description:
|
|
'A blog about software development, scientific research and Windows quirks.',
|
|
image: `${config.url}/images/chest.png`,
|
|
url: `${config.url}/blog`,
|
|
}
|
|
|
|
useSeoMeta({
|
|
title: meta.title,
|
|
description: meta.description,
|
|
ogTitle: meta.title,
|
|
ogDescription: meta.description,
|
|
ogImage: meta.image,
|
|
ogUrl: meta.url,
|
|
ogType: 'website',
|
|
twitterTitle: meta.title,
|
|
twitterDescription: meta.description,
|
|
twitterImage: meta.image,
|
|
twitterCard: 'summary',
|
|
})
|
|
|
|
useHead({
|
|
title: 'The Ender Chest',
|
|
htmlAttrs: {
|
|
lang: config.locale || 'en',
|
|
},
|
|
link: [
|
|
{
|
|
rel: 'icon',
|
|
type: 'image/x-icon',
|
|
href: '/favicon.ico',
|
|
},
|
|
],
|
|
})
|
|
|
|
defineRouteRules({
|
|
sitemap: {
|
|
changefreq: 'monthly',
|
|
priority: 0.7,
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<section>
|
|
<h3 class="mb-2">The Ender Chest</h3>
|
|
<p class="mb-4">
|
|
You're browsing the Ender Chest — a blog about software development,
|
|
scientific research and Windows quirks.
|
|
</p>
|
|
<h4 class="mb-2">Recent posts</h4>
|
|
<ContentList
|
|
:query="{
|
|
path: '/blog',
|
|
where: [{ draft: false }],
|
|
limit: 3,
|
|
sort: [{ created: -1 }],
|
|
}"
|
|
>
|
|
<template #default="{ list }">
|
|
<div class="grid gap-4">
|
|
<NuxtLink
|
|
v-for="post in list"
|
|
:key="post._path"
|
|
:to="post._path"
|
|
class="text-inherit hover:text-inherit no-underline"
|
|
>
|
|
<article
|
|
class="post-box relative flex flex-col md:flex-row gap-4 p-2 border-2 rounded-xl transition-ease"
|
|
>
|
|
<img
|
|
draggable="false"
|
|
:src="
|
|
post.thumbnail ??
|
|
`/images/blog/thumbnails/${post._path!.split('/').at(-1)}.png`
|
|
"
|
|
:alt="post.title"
|
|
class="md:flex-grow-[2] md:basis-0 xl:flex-grow-1 object-cover object-center aspect-[16/9] w-fit md:w-0 xl:w-fit h-full max-h-[300px] rounded-xl"
|
|
/>
|
|
<div class="md:flex-grow-[3] md:basis-0 xl:flex-grow-1 w-full">
|
|
<h3 class="post-title md:line-clamp-2 md:overflow-ellipsis">
|
|
{{ post.title }}
|
|
</h3>
|
|
<p
|
|
class="post-description mb-0 md:line-clamp-4 md:overflow-ellipsis"
|
|
>
|
|
{{ post.description }}
|
|
</p>
|
|
|
|
<div class="flex flex-row flex-wrap gap-2 py-2">
|
|
<small
|
|
v-for="(tag, index) in post.tags.slice(0, 3)"
|
|
:key="index"
|
|
class="post-tag px-2 py-1 rounded-xl border whitespace-nowrap"
|
|
>
|
|
{{ tag }}
|
|
</small>
|
|
<small
|
|
v-if="post.tags.length > 3"
|
|
class="post-tag px-2 py-1 rounded-xl border whitespace-nowrap"
|
|
>
|
|
{{ post.tags.length - 3 }} more
|
|
</small>
|
|
</div>
|
|
|
|
<div class="post-details py-2">
|
|
<div class="flex flex-row items-center gap-2">
|
|
<iconify-icon icon="mdi:calendar" />
|
|
<small class="whitespace-nowrap">
|
|
{{ formatDate(post.created, 'HH:mm • LLLL do, y') }}
|
|
</small>
|
|
</div>
|
|
<div
|
|
v-if="post.updated"
|
|
class="flex flex-row items-center gap-2"
|
|
>
|
|
<iconify-icon icon="mdi:pencil" />
|
|
<small class="whitespace-nowrap">
|
|
{{ formatDate(post.updated, 'HH:mm • LLLL do, y') }}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="post-pocket border-l border-t absolute bottom-0 right-0 uppercase rounded-tl-xl flex flex-row items-center gap-2 p-2"
|
|
>
|
|
<iconify-icon icon="mdi:clock-outline" />
|
|
<small class="whitespace-nowrap font-mono font-small">
|
|
{{ post.readingTime.text }}
|
|
</small>
|
|
</div>
|
|
</article>
|
|
</NuxtLink>
|
|
</div>
|
|
</template>
|
|
|
|
<template #not-found>
|
|
<div class="flex flex-col justify-center items-center gap-4">
|
|
<span>No posts found 🙁</span>
|
|
<NuxtLink to="/">Back to index</NuxtLink>
|
|
</div>
|
|
</template>
|
|
</ContentList>
|
|
</section>
|
|
</template>
|