Created blog page, many improvements
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<script setup lang="ts">
|
||||
const config = useAppConfig()
|
||||
const meta = {
|
||||
title: 'Page not found',
|
||||
description:
|
||||
'The page you are looking for does not exist. It might have been removed, had its name changed, or is temporarily unavailable.',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: config.url,
|
||||
}
|
||||
|
||||
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: 'Page not found',
|
||||
htmlAttrs: {
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: '/favicon.ico',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const error = {
|
||||
http_code: 200,
|
||||
title: config.title,
|
||||
url: config.url,
|
||||
locale: config.locale || 'en',
|
||||
data: {
|
||||
path: '/error.vue',
|
||||
content: {
|
||||
title: 'Page not found!',
|
||||
description:
|
||||
"The page you are looking for does not exist. However, no 404 error has occurred, as you're browsing through a web application loaded into memory and HTTP error codes make no sense here.",
|
||||
},
|
||||
},
|
||||
slug: useRoute().params.slug,
|
||||
build: config.build,
|
||||
}
|
||||
const errorString = JSON.stringify(error, null, 2)
|
||||
const buffer = ref('')
|
||||
|
||||
const startTime = Date.now()
|
||||
const dt = 20
|
||||
|
||||
let i = 0
|
||||
|
||||
function type() {
|
||||
if (Date.now() > startTime + dt * i) buffer.value += errorString[i++]
|
||||
if (i < errorString.length) requestAnimationFrame(type)
|
||||
}
|
||||
|
||||
onMounted(() => setTimeout(type, 1500))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="alex">Page not found!</h3>
|
||||
<pre class="pre-wrap">{{ buffer }}<span class="blinker">▊</span></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.blinker {
|
||||
animation: blinker 1s steps(2, start) infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
from {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+14
-3
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
const config = useAppConfig()
|
||||
const age =
|
||||
new Date(Number(new Date()) - Number(new Date('2003-09-18'))).getFullYear() -
|
||||
1970
|
||||
@@ -6,8 +7,8 @@ const age =
|
||||
const meta = {
|
||||
title: 'About Me',
|
||||
description: `Nice to meet you!~ I'm Andrew, a ${age}-year-old developer from Kaluga, Russia.`,
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
url: 'https://enderman.ch/about',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: `${config.url}/about`,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
@@ -27,7 +28,7 @@ useSeoMeta({
|
||||
useHead({
|
||||
title: 'About',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
@@ -82,6 +83,16 @@ useHead({
|
||||
thinking for quite some time, and then concluded that adding «ch» at the
|
||||
end sounded pretty good. I was 14 at the time of making that decision.
|
||||
</li>
|
||||
<li>
|
||||
<strong
|
||||
>When will you make a new video? What happened to your
|
||||
schedule?</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
One day. Haven't been particularly motivated lately, but life's busy. 🙁
|
||||
I'm alive and well, though.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
const { slug } = useRoute().params
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article
|
||||
class="scrollbar fade-mask-sm d-flex flex-column gap-3 flex-grow-1 overflow-x-hidden overflow-y-auto py-sm-4 pe-sm-3"
|
||||
>
|
||||
<ContentDoc :path="`/blog/${slug}`">
|
||||
<template #default="{ doc }">
|
||||
<div class="post-preamble">
|
||||
<h3 class="alex">{{ doc.title }}</h3>
|
||||
<img
|
||||
draggable="false"
|
||||
:src="doc.thumbnail"
|
||||
:alt="doc.title"
|
||||
class="post-preamble-thumb rounded-4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="post-content">
|
||||
<ContentRenderer :value="doc" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #not-found>
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center gap-3"
|
||||
>
|
||||
<span>Blog post not found 🙁</span>
|
||||
<NuxtLink to="/blog" class="link-hi">Back to blog</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
</ContentDoc>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import { formatDate } from 'date-fns'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section>
|
||||
<h3 class="alex">Recent posts</h3>
|
||||
<ContentList
|
||||
:query="{
|
||||
path: '/blog',
|
||||
where: [{ draft: false }],
|
||||
limit: 3,
|
||||
sort: [{ created: -1 }],
|
||||
}"
|
||||
>
|
||||
<template #default="{ list }">
|
||||
<div class="d-grid gap-3">
|
||||
<NuxtLink
|
||||
v-for="post in list"
|
||||
:key="post._path"
|
||||
:to="post._path"
|
||||
class="no-decoration"
|
||||
>
|
||||
<article
|
||||
class="post-box d-flex flex-column flex-md-row gap-3 rounded-4 h-100"
|
||||
>
|
||||
<div class="post-thumb">
|
||||
<img
|
||||
draggable="false"
|
||||
:src="post.thumbnail"
|
||||
:alt="post.title"
|
||||
class="rounded-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="post-title alex">{{ post.title }}</h3>
|
||||
<p class="post-description">{{ post.description }}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6 d-flex flex-row gap-1 align-items-center">
|
||||
<iconify-icon icon="mdi:calendar" />
|
||||
<small class="nobr">
|
||||
{{ formatDate(post.created, 'LLLL do, y – HH:mm') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="post.updated" class="row">
|
||||
<div class="col-12 d-flex flex-row gap-1 align-items-center">
|
||||
<iconify-icon icon="mdi:pencil" />
|
||||
<small class="nobr">
|
||||
{{ formatDate(post.updated, 'LLLL do, y – HH:mm') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="post-pocket d-flex flex-row gap-1 align-items-center">
|
||||
<iconify-icon icon="mdi:clock-outline" />
|
||||
<small class="nobr font-monospace font-small">
|
||||
{{ post.readingTime.text }}
|
||||
</small>
|
||||
</div>
|
||||
</article>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #not-found>
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center gap-3"
|
||||
>
|
||||
<span>No posts found 🙁</span>
|
||||
<NuxtLink to="/" class="link-hi">Back to index</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
</ContentList>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
+6
-6
@@ -3,7 +3,7 @@ const config = useAppConfig()
|
||||
const meta = {
|
||||
title: 'Enderman',
|
||||
description: config.description,
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: config.url,
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ useHead({
|
||||
I'm <strong>Enderman</strong> – a software engineer, a malware
|
||||
enthusiast and most importantly, a weird tall creature. I have over 300K
|
||||
subscribers on
|
||||
<a class="link-hi" href="https://go.enderman.ch/youtube">YouTube</a> and
|
||||
<a class="link-hi" :href="`${config.shortener}/youtube`">YouTube</a> and
|
||||
over 20K followers on
|
||||
<a class="link-hi" href="https://go.enderman.ch/twitter">Twitter</a>.
|
||||
<a class="link-hi" :href="`${config.shortener}/twitter`">Twitter</a>.
|
||||
Sometimes I wish there were 48 hours in a day.
|
||||
</p>
|
||||
<div class="row g-3">
|
||||
@@ -57,7 +57,7 @@ useHead({
|
||||
<strong>C/C++</strong> for desktop applications and any common
|
||||
backend stack with framework-loaded TypeScript for web. You can take
|
||||
a look at my code on
|
||||
<a class="link-hi" href="https://go.enderman.ch/github">GitHub</a>.
|
||||
<a class="link-hi" :href="`${config.shortener}/github`">GitHub</a>.
|
||||
</li>
|
||||
<li>
|
||||
I have the most unnecessary, yet fascinating knowledge about
|
||||
@@ -70,7 +70,7 @@ useHead({
|
||||
<li>
|
||||
I research and analyze modern malware, educate computer users about
|
||||
it and preserve history. The repository can be found
|
||||
<a class="link-hi" href="https://go.enderman.ch/repository">here</a
|
||||
<a class="link-hi" :href="`${config.shortener}/repository`">here</a
|
||||
>.
|
||||
</li>
|
||||
<li>I make videos for you to enjoy!</li>
|
||||
@@ -86,7 +86,7 @@ useHead({
|
||||
<li>Philosophy</li>
|
||||
<li>Geopolitics</li>
|
||||
<li>
|
||||
<a class="link-hi" href="https://go.enderman.ch/chess">Chess</a>
|
||||
<a class="link-hi" :href="`${config.shortener}/chess`">Chess</a>
|
||||
</li>
|
||||
<li>Solitude</li>
|
||||
</ul>
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
// AI-Generated test data.
|
||||
const config = useAppConfig()
|
||||
const projects = [
|
||||
{
|
||||
name: 'MalwareWatch',
|
||||
@@ -25,7 +25,7 @@ const projects = [
|
||||
{
|
||||
name: 'Windows XP Keygen / UMSKT',
|
||||
description: 'A key generator for Windows XP.',
|
||||
url: 'https://go.enderman.ch/xpkeygen',
|
||||
url: `${config.shortener}/xpkeygen`,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -33,8 +33,8 @@ const meta = {
|
||||
title: "Enderman's Projects",
|
||||
description:
|
||||
"A display of my largest projects. Some of them aren't published on YouTube.",
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
url: 'https://enderman.ch/projects',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: `${config.url}/projects`,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
@@ -54,7 +54,7 @@ useSeoMeta({
|
||||
useHead({
|
||||
title: 'Projects',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
|
||||
+18
-15
@@ -1,8 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
const config = useAppConfig()
|
||||
const fqdn = config.url.split('//')[1]
|
||||
|
||||
const socials = [
|
||||
{
|
||||
name: 'YouTube',
|
||||
url: 'https://go.enderman.ch/youtube',
|
||||
url: `${config.shortener}/youtube`,
|
||||
icon: {
|
||||
name: 'logos:youtube-icon',
|
||||
color: 'white',
|
||||
@@ -10,7 +13,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'Andrew',
|
||||
url: 'https://go.enderman.ch/andrew',
|
||||
url: `${config.shortener}/andrew`,
|
||||
icon: {
|
||||
name: 'logos:youtube-icon',
|
||||
color: 'white',
|
||||
@@ -18,7 +21,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'Twitch',
|
||||
url: 'https://go.enderman.ch/twitch',
|
||||
url: `${config.shortener}/twitch`,
|
||||
icon: {
|
||||
name: 'fa:twitch',
|
||||
color: '#B68CFF',
|
||||
@@ -26,7 +29,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'TikTok',
|
||||
url: 'https://go.enderman.ch/tiktok',
|
||||
url: `${config.shortener}/tiktok`,
|
||||
icon: {
|
||||
name: 'logos:tiktok-icon',
|
||||
color: 'white',
|
||||
@@ -34,7 +37,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
url: 'https://go.enderman.ch/twitter',
|
||||
url: `${config.shortener}/twitter`,
|
||||
icon: {
|
||||
name: 'logos:twitter',
|
||||
color: 'white',
|
||||
@@ -42,7 +45,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
url: 'https://go.enderman.ch/telegram',
|
||||
url: `${config.shortener}/telegram`,
|
||||
icon: {
|
||||
name: 'logos:telegram',
|
||||
color: 'white',
|
||||
@@ -50,7 +53,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'Discord',
|
||||
url: 'https://go.enderman.ch/discord',
|
||||
url: `${config.shortener}/discord`,
|
||||
icon: {
|
||||
name: 'logos:discord-icon',
|
||||
color: 'white',
|
||||
@@ -58,7 +61,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
url: 'https://go.enderman.ch/github',
|
||||
url: `${config.shortener}/github`,
|
||||
icon: {
|
||||
name: 'fa:github',
|
||||
color: 'white',
|
||||
@@ -66,7 +69,7 @@ const socials = [
|
||||
},
|
||||
{
|
||||
name: 'Steam',
|
||||
url: 'https://go.enderman.ch/steam',
|
||||
url: `${config.shortener}/steam`,
|
||||
icon: {
|
||||
name: 'fa:steam',
|
||||
color: 'white',
|
||||
@@ -78,8 +81,8 @@ const meta = {
|
||||
title: 'Enderman Online',
|
||||
description:
|
||||
'Discover me on your favorite social media and find ways to contact me here.',
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
url: 'https://enderman.ch/projects',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: `${config.url}/projects`,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
@@ -99,7 +102,7 @@ useSeoMeta({
|
||||
useHead({
|
||||
title: 'Socials',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
@@ -140,11 +143,11 @@ useHead({
|
||||
Personal:
|
||||
<EMail
|
||||
class="link-hi"
|
||||
address="contact@enderman.ch"
|
||||
:address="`contact@${fqdn}`"
|
||||
subject="Hey Enderman!"
|
||||
/><br />
|
||||
Manager: <EMail class="link-hi" address="manager@enderman.ch" /><br />
|
||||
Abuse: <EMail class="link-hi" address="abuse@enderman.ch" />
|
||||
Manager: <EMail class="link-hi" :address="`manager@${fqdn}`" /><br />
|
||||
Abuse: <EMail class="link-hi" :address="`abuse@${fqdn}`" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user