Dependency refresh and cleanup
This commit is contained in:
parent
6300d6746a
commit
c70f9013f5
22 changed files with 1858 additions and 3392 deletions
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": "awzzm-vue",
|
||||
"rules": {
|
||||
"vue/no-reserved-component-names": "off"
|
||||
}
|
||||
}
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,8 +1,7 @@
|
|||
.idea/
|
||||
/.idea/
|
||||
/node_modules/
|
||||
/dist/
|
||||
/.vite-ssg-temp/
|
||||
|
||||
*.log
|
||||
.env
|
||||
|
||||
*.env
|
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
|||
16.11.0
|
||||
22
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="/node_modules/@fontsource/plus-jakarta-sans/400.css">
|
||||
<link rel="stylesheet" href="/node_modules/@fontsource/syne/700.css">
|
||||
<link rel="stylesheet" href="/node_modules/@fontsource/plus-jakarta-sans/700.css">
|
||||
<meta name="description" content="freelance software developer, graphic design enthusiast and hobby photographer">
|
||||
<meta name="description" content="freelance software developer, photographer, musician">
|
||||
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
|
|
12
netlify.toml
12
netlify.toml
|
@ -1,12 +0,0 @@
|
|||
[build.environment]
|
||||
# bypass npm auto install
|
||||
NPM_FLAGS = "--version"
|
||||
|
||||
[build]
|
||||
publish = "dist"
|
||||
command = "npx pnpm i --store=node_modules/.pnpm-store && npx pnpm run build"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
46
package.json
46
package.json
|
@ -1,34 +1,36 @@
|
|||
{
|
||||
"name": "moritzruth.de",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vite-ssg build",
|
||||
"start": "vite preview",
|
||||
"lint": "eslint . --fix --ignore-path .gitignore"
|
||||
"start": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^2.0.1",
|
||||
"@vue/compiler-sfc": "^3.1.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-awzzm-vue": "^2.0.1",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.3.7",
|
||||
"vite-plugin-pages": "^0.19.4",
|
||||
"vite-plugin-windicss": "^1.0.4",
|
||||
"windicss": "^3.2.1"
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/compiler-sfc": "^3.5.13",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.1.0",
|
||||
"vite-plugin-pages": "^0.32.4",
|
||||
"vite-plugin-windicss": "^1.9.4",
|
||||
"windicss": "^3.5.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/plus-jakarta-sans": "^4.5.0",
|
||||
"@fontsource/syne": "^4.5.0",
|
||||
"@iconify/json": "1.1.444",
|
||||
"@vueuse/core": "^7.2.2",
|
||||
"@vueuse/head": "^0.5.1",
|
||||
"blobs": "^2.2.1-beta.1",
|
||||
"ohmyfetch": "^0.4.11",
|
||||
"unplugin-icons": "^0.12.23",
|
||||
"vite-ssg": "^0.17.2",
|
||||
"vue": "^3.2.26",
|
||||
"vue-router": "^4.0.12"
|
||||
"@fontsource/plus-jakarta-sans": "^5.1.1",
|
||||
"@fontsource/syne": "^5.1.1",
|
||||
"@iconify/json": "^2.2.306",
|
||||
"@vueuse/core": "^12.5.0",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
"blobs": "^2.3.0-beta.2",
|
||||
"unplugin-icons": "^22.0.0",
|
||||
"vite-ssg": "^25.0.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
4885
pnpm-lock.yaml
generated
4885
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,2 +0,0 @@
|
|||
/.well-known/*
|
||||
Access-Control-Allow-Origin: *
|
|
@ -1,9 +0,0 @@
|
|||
# Old domain
|
||||
https://moritz-ruth.de/* https://moritzruth.de/:splat 301!
|
||||
|
||||
# External
|
||||
/matrix https://matrix.to/#/@moritz:deltaa.xyz
|
||||
/twitter https://twitter.com/moritzruth
|
||||
|
||||
# Internal
|
||||
/kontakt /contact
|
2
redirects.txt
Normal file
2
redirects.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
/kontakt /contact
|
||||
/matrix https://matrix.to/#/@moritz:deltaa.xyz
|
|
@ -1,52 +0,0 @@
|
|||
<template>
|
||||
<NotFoundPage v-if="post === null" object-name="post" back-target="/blog"/>
|
||||
<TopBarLayout v-else title="Blog" back-target="/blog">
|
||||
<article>
|
||||
<h1 class="font-bold text-3xl sm:text-3xl sm:text-center font-special">
|
||||
{{ post.title }}
|
||||
</h1>
|
||||
<XSpacer v="4"/>
|
||||
<div class="flex justify-center -sm:flex-col -sm:space-y-1 -sm:pt-2 sm:space-x-3 text-sm sm:text-base">
|
||||
<div>
|
||||
Published at {{ post.published_at.slice(0, 10) }}
|
||||
</div>
|
||||
<div class="-sm:hidden">
|
||||
|
|
||||
</div>
|
||||
<div>
|
||||
Reading time: {{ post.reading_time_minutes }} minute{{ post.reading_time_minutes === 1 ? "" : "s" }}
|
||||
</div>
|
||||
</div>
|
||||
<XSpacer v="8"/>
|
||||
<Prose v-html="html"/>
|
||||
</article>
|
||||
</TopBarLayout>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { useRoute } from "vue-router"
|
||||
import { getPostBySlug } from "../posts"
|
||||
import TopBarLayout from "./TopBarLayout.vue"
|
||||
import Prose from "./Prose.vue"
|
||||
import XSpacer from "./XSpacer.vue"
|
||||
import NotFoundPage from "./NotFoundPage.vue"
|
||||
|
||||
export default {
|
||||
name: "BlogPostPageContent",
|
||||
components: { NotFoundPage, XSpacer, Prose, TopBarLayout },
|
||||
async setup() {
|
||||
const route = useRoute()
|
||||
const post = await getPostBySlug(route.params.slug as string)
|
||||
|
||||
return {
|
||||
post,
|
||||
// Yep, that's awful code
|
||||
html: post?.body_html?.replaceAll("h2>", "h3>")?.replaceAll("h1>", "h2>")
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -3,10 +3,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { canvasPath as createBlobAnimation } from "blobs/v2/animate/index.js"
|
||||
import blobsLib from "blobs/v2/animate"
|
||||
import { ref, watchEffect } from "vue"
|
||||
import { useRafFn } from "@vueuse/core"
|
||||
|
||||
const createBlobAnimation = blobsLib.canvasPath
|
||||
|
||||
function getComponentsOfHexColor(hexColorString) {
|
||||
return [
|
||||
Number.parseInt(hexColorString.slice(1, 3), 16),
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
</style>
|
||||
|
||||
<script>
|
||||
import { $fetch } from "ohmyfetch"
|
||||
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
const waitForAnimationFrame = () => new Promise(resolve => requestAnimationFrame(resolve))
|
||||
|
||||
const asyncSetup = async src => {
|
||||
const blob = await $fetch(src)
|
||||
const response = await fetch(src)
|
||||
const blob = await response.blob()
|
||||
await waitForAnimationFrame() // makes sure the browser is idle
|
||||
return {
|
||||
objectUrl: URL.createObjectURL(blob)
|
||||
|
|
|
@ -12,14 +12,14 @@ export const photos: Photo[] = [
|
|||
title: "Late afternoon",
|
||||
altText: "A red house in London",
|
||||
date: "2019",
|
||||
description: "Taken in London during a sunny afternoon.\nSeems to be 65 Curzon St."
|
||||
description: "65 Curzon Street, Mayfair, London"
|
||||
},
|
||||
{
|
||||
file: "Martyrdom.webp",
|
||||
title: "Martyrdom",
|
||||
altText: "A church on a vineyard",
|
||||
date: "2019",
|
||||
description: "The Sankt-Laurentius church right by the Mosel river in Bremm (Germany)."
|
||||
description: "The Sankt-Laurentius church by the Mosel river in Bremm (Germany)."
|
||||
},
|
||||
{
|
||||
file: "Heat.webp",
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
<template>
|
||||
<NotFoundPage v-if="post === null" object-name="post" back-target="/blog"/>
|
||||
<TopBarLayout v-else
|
||||
title="Blog"
|
||||
back-target="/blog"
|
||||
no-set-title
|
||||
>
|
||||
<Head>
|
||||
<title>{{ post.title }} — Moritz Ruth</title>
|
||||
<meta name="keywords" :content="getKeywordsWithBase(post.tags).join(', ')">
|
||||
</Head>
|
||||
<article>
|
||||
<h1 class="font-bold text-3xl sm:text-3xl sm:text-center font-special">
|
||||
{{ post.title }}
|
||||
</h1>
|
||||
<XSpacer v="4"/>
|
||||
<div class="flex justify-center -sm:flex-col -sm:space-y-1 -sm:pt-2 sm:space-x-3 text-sm sm:text-base">
|
||||
<div>
|
||||
Published at {{ post.published_at.slice(0, 10) }}
|
||||
</div>
|
||||
<div class="-sm:hidden">
|
||||
|
|
||||
</div>
|
||||
<div>
|
||||
Reading time: {{ post.reading_time_minutes }} minute{{ post.reading_time_minutes === 1 ? "" : "s" }}
|
||||
</div>
|
||||
</div>
|
||||
<XSpacer v="8"/>
|
||||
<Prose v-html="html"/>
|
||||
</article>
|
||||
</TopBarLayout>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { useRoute } from "vue-router"
|
||||
import { Head } from "@vueuse/head"
|
||||
import TopBarLayout from "../../components/TopBarLayout.vue"
|
||||
import { getPostBySlug } from "../../posts"
|
||||
import Prose from "../../components/Prose.vue"
|
||||
import XSpacer from "../../components/XSpacer.vue"
|
||||
import NotFoundPage from "../../components/NotFoundPage.vue"
|
||||
import { getKeywordsWithBase } from "../../data"
|
||||
|
||||
export default {
|
||||
name: "BlogPostPage",
|
||||
components: { NotFoundPage, XSpacer, Prose, TopBarLayout, Head },
|
||||
async setup() {
|
||||
const route = useRoute()
|
||||
|
||||
const post = await getPostBySlug(route.params.slug as string)
|
||||
|
||||
return {
|
||||
post,
|
||||
// Yep, that's awful code
|
||||
html: post?.body_html?.replaceAll("h2>", "h3>")?.replaceAll("h1>", "h2>"),
|
||||
getKeywordsWithBase
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,65 +0,0 @@
|
|||
<template>
|
||||
<TopBarLayout title="Blog" back-target="/">
|
||||
<div class="fixed top-0 left-0 bottom-0 right-0 flex justify-center items-center">
|
||||
<BlurredBlobCanvas
|
||||
:blur="50"
|
||||
:size="400"
|
||||
:randomness="400"
|
||||
:minimum-duration="3000"
|
||||
:duration-variation="1000"
|
||||
:minimum-opacity="0.4"
|
||||
:opacity-variation="0"
|
||||
:colors="['#eb34cf', '#6577fc']"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-8 max-w-120 mx-auto">
|
||||
<router-link
|
||||
v-for="post in posts"
|
||||
:key="post.id"
|
||||
:to="`/blog/${post.slug}`"
|
||||
class="bg-light-300 bg-opacity-5 rounded-lg backdrop-blur-lg cursor-pointer hover:bg-opacity-10 focus-visible:bg-opacity-10 transform hover:scale-104
|
||||
transition duration-200 p-5 sm:p-7 flex flex-col overflow-hidden"
|
||||
>
|
||||
<div class="font-bold text-xl">
|
||||
{{ post.title }}
|
||||
</div>
|
||||
<XSpacer v="2"/>
|
||||
<div class="text-lg">
|
||||
{{ post.description }}
|
||||
</div>
|
||||
<XSpacer v="3"/>
|
||||
<div class="font-bold text-sm flex justify-between">
|
||||
<div>
|
||||
Published at {{ new Date(post.published_at).toLocaleDateString() }}
|
||||
</div>
|
||||
<div>
|
||||
{{ post.reading_time_minutes }} minute{{ post.reading_time_minutes === 1 ? "" : "s" }}
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</TopBarLayout>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import TopBarLayout from "../../components/TopBarLayout.vue"
|
||||
import { getPosts } from "../../posts"
|
||||
import XSpacer from "../../components/XSpacer.vue"
|
||||
import BlurredBlobCanvas from "../../components/BlurredBlobCanvas.vue"
|
||||
|
||||
export default {
|
||||
name: "BlogIndexPage",
|
||||
components: { BlurredBlobCanvas, XSpacer, TopBarLayout },
|
||||
async setup() {
|
||||
const posts = await getPosts()
|
||||
|
||||
return {
|
||||
posts
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -21,10 +21,11 @@
|
|||
<div class="text-lg sm:text-xl font-medium leading-8 pt-5">
|
||||
<p>
|
||||
I’m a freelance
|
||||
software developer,
|
||||
graphic design enthusiast and
|
||||
<router-link to="/photography" :class="$style.link">hobby photographer</router-link>
|
||||
from Europe.
|
||||
software developer
|
||||
from Europe.<br>
|
||||
I also occasionally take
|
||||
<router-link to="/photography" :class="$style.link">photos</router-link>
|
||||
and make music.
|
||||
</p>
|
||||
</div>
|
||||
<XSpacer v="10"/>
|
||||
|
@ -32,7 +33,7 @@
|
|||
</main>
|
||||
</div>
|
||||
<div class="relative lg:pr-20 -lg:pt-20 pb-10 mt-0">
|
||||
<nav class="absolute w-full pt-20 flex justify-center">
|
||||
<nav class="absolute w-full pt-5 flex justify-center">
|
||||
<BlurredBlobCanvas
|
||||
:blur="30"
|
||||
:size="300"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
This legal notice (Impressum) is a requirement of the German Act for Telemedia Services (Telemediengesetz).
|
||||
</p>
|
||||
<p>
|
||||
The declarations on this page only apply to this website (moritzruth.de) and all websites which reside under subdomains of moritzruth.de.
|
||||
The declarations on this page only apply to this website (moritzruth.de) and all websites which reside under subdomains of moritzruth.de and deltaa.xyz.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
|
|
21
src/posts.ts
21
src/posts.ts
|
@ -1,21 +0,0 @@
|
|||
/* eslint-disable camelcase */
|
||||
import { $fetch } from "ohmyfetch"
|
||||
|
||||
const USERNAME = "moritzruth"
|
||||
|
||||
export interface Post {
|
||||
id: number
|
||||
title: string
|
||||
slug: string
|
||||
description: string
|
||||
published_at: string
|
||||
reading_time_minutes: number
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface FullPost extends Post {
|
||||
body_html: string
|
||||
}
|
||||
|
||||
export const getPosts = () => $fetch<Post[]>(`https://dev.to/api/articles?username=${USERNAME}&per_page=1000`)
|
||||
export const getPostBySlug = (slug: string) => $fetch<FullPost>(`https://dev.to/api/articles/${USERNAME}/${slug}`).catch(() => null)
|
3
src/vite-env.d.ts
vendored
3
src/vite-env.d.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
/// <reference types="vite-plugin-pages/client"/>
|
||||
/// <reference types="unplugin-icons/types/vue"/>
|
||||
/// <reference types="vite/client"/>
|
|
@ -1,16 +1,30 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"target": "ES2021",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"strict": true,
|
||||
"declaration": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "preserve",
|
||||
"lib": ["esnext", "dom"],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"strictNullChecks": true,
|
||||
"downlevelIteration": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
"importsNotUsedAsValues": "error",
|
||||
"isolatedModules": true,
|
||||
"rootDir": "src",
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"stripInternal": true,
|
||||
"target": "esnext",
|
||||
"types": [
|
||||
"vite/client",
|
||||
"unplugin-icons/types/vue",
|
||||
"vite-plugin-pages/client"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.vue"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -7,17 +7,8 @@ import iconsPlugin from "unplugin-icons/vite"
|
|||
export default defineConfig({
|
||||
plugins: [
|
||||
vuePlugin(),
|
||||
pagesPlugin({
|
||||
syncIndex: false
|
||||
}),
|
||||
pagesPlugin(),
|
||||
windicssPlugin(),
|
||||
iconsPlugin()
|
||||
],
|
||||
ssgOptions: {
|
||||
formatting: "minify",
|
||||
format: "cjs",
|
||||
includedRoutes(routes) {
|
||||
return routes.filter(route => !route.includes(":") && route !== "/blog")
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue