moved from svelte -> php with apache

This commit is contained in:
Ludwig Lehnert 2025-04-15 15:50:44 +02:00
parent d20d0a8e08
commit 0f630689f3
43 changed files with 21447 additions and 1087 deletions

View File

@ -1 +0,0 @@
.gitignore

23
.gitignore vendored
View File

@ -1,23 +0,0 @@
node_modules
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
.npmrc
View File

@ -1 +0,0 @@
engine-strict=true

View File

@ -1,15 +1,6 @@
FROM node:18-alpine AS builder
FROM php:8.2-apache
WORKDIR /app
COPY . .
RUN npm install
RUN npx vite build
COPY www/ /var/www/html/
FROM nginx:latest
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]
RUN chown -R www-data:www-data /var/www/html
RUN a2enmod rewrite

View File

@ -1,38 +0,0 @@
# sv
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npx sv create
# create a new project in my-app
npx sv create my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.

217
bun.lock
View File

@ -1,217 +0,0 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "lehnert.dev",
"dependencies": {
"@sveltejs/adapter-static": "^3.0.8",
},
"devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^6.2.5",
},
},
},
"packages": {
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.2", "", { "os": "android", "cpu": "arm" }, "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.2", "", { "os": "android", "cpu": "x64" }, "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.2", "", { "os": "linux", "cpu": "arm" }, "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.2", "", { "os": "linux", "cpu": "x64" }, "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.2", "", { "os": "none", "cpu": "arm64" }, "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.2", "", { "os": "none", "cpu": "x64" }, "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.2", "", { "os": "win32", "cpu": "x64" }, "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.40.0", "", { "os": "android", "cpu": "arm" }, "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.40.0", "", { "os": "android", "cpu": "arm64" }, "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.40.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.40.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.40.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.40.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.40.0", "", { "os": "linux", "cpu": "arm" }, "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.40.0", "", { "os": "linux", "cpu": "arm" }, "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.40.0", "", { "os": "linux", "cpu": "none" }, "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.40.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.40.0", "", { "os": "linux", "cpu": "none" }, "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.40.0", "", { "os": "linux", "cpu": "none" }, "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.40.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.40.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.40.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.40.0", "", { "os": "win32", "cpu": "x64" }, "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ=="],
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
"@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@4.0.0", "", { "dependencies": { "import-meta-resolve": "^4.1.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ=="],
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.8", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg=="],
"@sveltejs/kit": ["@sveltejs/kit@2.20.5", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-zT/97KvVUo19jEGZa972ls7KICjPCB53j54TVxnEFT5VEwL16G+YFqRVwJbfxh7AmS7/Ptr1rKF7Qt4FBMDNlw=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.0.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.0", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.15", "vitefu": "^1.0.4" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw=="],
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="],
"esbuild": ["esbuild@0.25.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.2", "@esbuild/android-arm": "0.25.2", "@esbuild/android-arm64": "0.25.2", "@esbuild/android-x64": "0.25.2", "@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-x64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-x64": "0.25.2", "@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-x64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-x64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.2", "@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-x64": "0.25.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ=="],
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"esrap": ["esrap@1.4.6", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw=="],
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"rollup": ["rollup@4.40.0", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
"sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"svelte": ["svelte@5.26.2", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-e2TEcGK2YKVwDWYy5OsptVclYgDvfY1E/8IzPiOq63uG/GDo/j5VUYTC9EinQNraoZalbMWN+5f5TYC1QlAqOw=="],
"svelte-check": ["svelte-check@4.1.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg=="],
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"vite": ["vite@6.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw=="],
"vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="],
"zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
}
}

View File

@ -1,20 +0,0 @@
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
sendfile on;
server {
listen 3000;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
error_page 404 /404.html;
}
}

View File

@ -1,26 +0,0 @@
{
"name": "lehnert.dev",
"version": "0.0.1",
"devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^6.2.5"
},
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-static": "^3.0.8"
}
}

13
src/app.d.ts vendored
View File

@ -1,13 +0,0 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View File

@ -1,17 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
<title>lehnert.dev</title>
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@ -1 +0,0 @@
// place files you want to import through the `$lib` alias in this folder.

View File

@ -1,26 +0,0 @@
<slot />
<style>
:global(html, body) {
min-width: 100vw;
min-height: max(100%, 100vh);
}
:global(*) {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:global(body) {
background: linear-gradient(
135deg,
#1e1e2f 0,
#252547 99vh,
#252547 100vh
);
max-width: 100vw;
overflow-x: hidden;
}
</style>

View File

@ -1 +0,0 @@
export const prerender = true;

View File

@ -1,342 +0,0 @@
<script lang="ts">
import EMail from "$lib/assets/email.svg";
import Git from "$lib/assets/git.svg";
import GitHub from "$lib/assets/github.svg";
import Instagram from "$lib/assets/instagram.svg";
import LinkedIn from "$lib/assets/linkedin.svg";
import Star from "$lib/assets/star.svg";
import { onMount } from "svelte";
$effect(() => {
const indices = Array(50)
.fill(0)
.map((_, i) => i);
const points: [number, number][] = indices.map(() => [
Math.max(0.02, Math.min(0.98, Math.random())),
Math.max(0.02, Math.min(0.98, Math.random())),
]);
const sizes = indices.map(() => Math.max(Math.random(), 0.3) * 2);
const stars = points.map(([x, y], i) => {
const star = document.createElement("img");
star.src = Star;
star.classList.add("star");
star.style.width = `max(${sizes[i]}vw, ${sizes[i]}vh)`;
star.style.position = "absolute";
star.style.left = `${100 * x}vw`;
star.style.top = `${100 * y}vh`;
return star;
});
function chooseRandom<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
const distance = (a: [number, number], b: [number, number]) => {
return (
Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2)) /
Math.SQRT2
);
};
const connections = stars.map((_, i) => {
let others: number[] = [];
const choosable = [...indices]
.sort((a, b) => {
return (
distance(points[i], points[a]) -
distance(points[i], points[b])
);
})
.slice(0, 15);
const otherCount = Math.max(3, Math.floor(Math.random() * 10));
for (let i = 0; i < otherCount; i++) {
let o = i;
while (o === i || others.includes(o)) {
o = chooseRandom(choosable);
}
others.push(o);
}
return others;
});
let lidx: number = 0;
const lineInterval = setInterval(() => {
if (Math.random() < 0.3) return;
if (Math.random() < 0.05) lidx = chooseRandom(indices);
let iter = 0;
while (iter++ < 300) {
const nidx = chooseRandom(indices);
if (lidx === nidx) continue;
const d = distance(points[lidx], points[nidx]);
if ((d > 0.4 && Math.random() < 0.8) || d > 0.5) continue;
const line = document.getElementById(`${lidx}-${nidx}`);
if (!line) continue;
line.setAttribute("opacity", "1");
setTimeout(
() => line.setAttribute("opacity", "0"),
4500 + Math.random() * 2500,
);
lidx = nidx;
return;
}
// nothing found -> choose next random
lidx = chooseRandom(indices);
}, 650);
document.getElementById("stars")?.append(...stars);
const linesSVG = document.getElementById("stars-lines");
stars.forEach((_, i) => {
const [x1, y1] = points[i];
connections[i].forEach((conn) => {
const [x2, y2] = points[conn];
const line = document.createElementNS(
"http://www.w3.org/2000/svg",
"line",
);
line.setAttribute("x1", `${x1 * 100}%`);
line.setAttribute("y1", `${y1 * 100}%`);
line.setAttribute("x2", `${x2 * 100}%`);
line.setAttribute("y2", `${y2 * 100}%`);
line.setAttribute("stroke", "white");
line.setAttribute("stroke-width", "1.5");
line.id = `${i}-${conn}`;
line.setAttribute("opacity", "0");
linesSVG?.append(line);
});
});
const enterListeners = stars.map((_, i) => () => {
connections[i].forEach((conn) => {
const line = document.getElementById(`${i}-${conn}`);
line?.setAttribute("opacity", "1");
});
});
const leaveListeners = stars.map((_, i) => () => {
connections[i].forEach((conn) => {
const line = document.getElementById(`${i}-${conn}`);
line?.setAttribute("opacity", "0");
});
});
stars.forEach((star, i) => {
star.addEventListener("mouseenter", enterListeners[i]);
star.addEventListener("mouseleave", leaveListeners[i]);
});
const svgResizeListener = () => {
if (!linesSVG) return;
const w = linesSVG.clientWidth;
const h = linesSVG.clientHeight;
linesSVG.setAttribute("viewBox", `0 0 ${w} ${h}`);
};
svgResizeListener();
window.addEventListener("resize", svgResizeListener);
return () => {
clearInterval(lineInterval);
stars.forEach((star, i) => {
star.removeEventListener("mouseenter", enterListeners[i]);
star.removeEventListener("mouseleave", leaveListeners[i]);
});
window.removeEventListener("resize", svgResizeListener);
};
});
</script>
<svelte:head>
<meta
name="keywords"
content="Ludwig Lehnert,Nürnberg,Pegnitz,Nuremberg,Informatik,Computer,Copmuter Science"
/>
</svelte:head>
<svg
id="stars-lines"
aria-hidden="true"
style="position: absolute; z-index: 0; height: 100vh; width: 100vw;"
></svg>
<div
id="stars"
style="position: absolute; z-index: 1; height: 100vh; width: 100vw; overflow-x: hidden;"
></div>
<main style="position: relative; z-index: 2;">
<h1>Hey, I'm Ludwig 🚀</h1>
<p>Welcome to my digital playground.</p>
<div class="social-links">
<a target="_blank" href="https://instagram.com/lehlud">
<img src={Instagram} alt="" width="25" />
</a>
<a target="_blank" href="https://linkedin.com/in/ludwig-lehnert">
<img src={LinkedIn} alt="" width="25" />
</a>
<a target="_blank" href="mailto:info@lehnert.dev">
<img src={EMail} alt="" width="25" />
</a>
<a target="_blank" href="https://gitea.cloud.lehnert.dev/explore/repos">
<img src={Git} alt="" width="25" />
</a>
<a target="_blank" href="https://github.com/lehlud">
<img src={GitHub} alt="" width="25" />
</a>
</div>
<div class="legal-links">
<a href="/imprint">Imprint</a>
<a href="/privacy">Privacy Policy</a>
</div>
</main>
<div style="height: 400px;"></div>
<style>
h1 {
font-size: 3.5rem;
background: linear-gradient(90deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: glow 3s ease-in-out infinite alternate;
}
@keyframes glow {
from {
text-shadow: 0 0 10px #00ffff;
}
to {
text-shadow: 0 0 20px #ff00ff;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
:global(.star) {
animation: fadeIn 1s forwards;
transition-duration: 500ms;
translate: -50% -50%;
}
/* :global(#stars-lines) {
max-width: 100vw;
overflow-x: hidden;
} */
:global(#stars-lines line) {
transition-duration: 500ms;
}
:global(#stars-lines line[opacity="1"]) {
animation: strokeAnim 3s ease-in-out forwards;
}
@keyframes strokeAnim {
from {
stroke: #00ffff;
}
to {
stroke: #ff00ff;
}
}
p {
font-size: 1.2rem;
max-width: 600px;
margin-top: 1rem;
color: #ccc;
}
.social-links {
display: flex;
gap: 0.75rem;
margin-top: 1.5rem;
pointer-events: auto;
}
.social-links a {
padding: 0.75rem;
font-size: 1rem;
background: #00ffff;
color: #1e1e2f;
border: none;
border-radius: 999px;
cursor: pointer;
transition: background 0.3s ease;
display: grid;
place-items: center;
}
.social-links a:hover {
background: #ff00ff;
color: white;
}
.legal-links {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
pointer-events: auto;
}
.legal-links a {
color: rgb(255, 255, 255);
cursor: pointer;
transition: background 0.3s ease;
}
.legal-links a:hover {
opacity: 0.8;
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
font-family: "Inter", sans-serif;
text-align: center;
padding: 2rem;
pointer-events: none;
}
</style>

View File

@ -1,93 +0,0 @@
<script lang="ts">
</script>
<main>
<h1>404</h1>
<p>
Oops! The page you're looking for doesn't exist. It might have been
abducted by aliens 🛸
</p>
<a class="btn" href="/">Beam me back</a>
</main>
<style>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background: radial-gradient(
ellipse at center,
#1e1e2f 0%,
#0f0f1a 100%
);
color: white;
text-align: center;
font-family: "Inter", sans-serif;
overflow: hidden;
}
h1 {
font-size: 6rem;
margin: 0;
background: linear-gradient(90deg, #ff0066, #00ffff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: glitch 2s infinite;
}
@keyframes glitch {
0% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
25% {
text-shadow:
-2px 0 #ff00c8,
2px 0 #00fff9;
}
50% {
text-shadow:
2px 0 #00fff9,
-2px 0 #ff00c8;
}
75% {
text-shadow:
-1px 0 #00fff9,
1px 0 #ff00c8;
}
100% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
}
p {
font-size: 1.25rem;
margin: 1rem 0 2rem;
color: #ccc;
max-width: 600px;
}
.btn {
text-decoration: none;
background: #00ffff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 999px;
font-size: 1rem;
color: #1e1e2f;
cursor: pointer;
transition: background 0.3s ease;
}
.btn:hover {
background: #ff0066;
color: white;
}
</style>

View File

@ -1,92 +0,0 @@
<script lang="ts">
// Optional: add any TS logic here
</script>
<main>
<h1>Impressum<br />Imprint</h1>
<div class="legal-section">
<p><b>Angaben gemäß § 5 TMG:</b></p>
<p>
Ludwig Lehnert <br />
Zedernstr. 41 <br />
90441 Nürnberg <br />
Deutschland
</p>
<p>
<b>Kontakt:</b> <br />
<!-- Telefon: [Ihre Telefonnummer] <br /> -->
E-Mail: info@lehnert.dev
</p>
</div>
</main>
<style>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
font-family: "Inter", sans-serif;
text-align: center;
padding: 2rem;
pointer-events: none;
}
h1 {
font-size: 2.5rem;
background: linear-gradient(90deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 1rem;
animation: glitch 2s infinite;
}
@keyframes glitch {
0% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
25% {
text-shadow:
-2px 0 #ff00c8,
2px 0 #00fff9;
}
50% {
text-shadow:
2px 0 #00fff9,
-2px 0 #ff00c8;
}
75% {
text-shadow:
-1px 0 #00fff9,
1px 0 #ff00c8;
}
100% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
}
.legal-section {
text-align: left;
max-width: 700px;
margin: 2rem auto;
padding: 1rem 2rem;
background: rgba(255, 255, 255, 0.05);
border-left: 4px solid #00ffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.legal-section p {
font-size: 1rem;
margin-bottom: 1rem;
color: #ccc;
line-height: 1.6;
}
</style>

View File

@ -1,120 +0,0 @@
<script lang="ts">
// Optional: add any TS logic here
</script>
<main>
<h1>Datenschutzerklärung<br />Privacy Policy</h1>
<div class="legal-section">
<p><strong>Deutsch:</strong></p>
<p>
Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Beim
Besuch dieser Website werden automatisch Server-Logfiles erfasst
(z.B. IP-Adresse, Uhrzeit des Zugriffs), die zur Gewährleistung des
Betriebs und zur Sicherheit der Website notwendig sind. Diese Daten
werden für maximal 90 Tage gespeichert und danach automatisch
gelöscht.
</p>
<p>
Diese Website wird über die Hetzner Cloud gehostet. Es gelten
zusätzlich die Datenschutzbestimmungen der Hetzner Online GmbH: <a
href="https://www.hetzner.com/de/legal/privacy-policy/"
target="_blank">Hetzner Datenschutzerklärung</a
>.
</p>
<p><strong>English:</strong></p>
<p>
We take the protection of your personal data very seriously. When
visiting this website, server log files (e.g. IP address, access
time) are automatically collected for the purpose of operation and
security. These logs are stored for a maximum of 90 days and then
automatically deleted.
</p>
<p>
This website is hosted via Hetzner Cloud. The privacy policy of
Hetzner Online GmbH also applies: <a
href="https://www.hetzner.com/legal/privacy-policy"
target="_blank">Hetzner Privacy Policy</a
>.
</p>
</div>
</main>
<style>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
font-family: "Inter", sans-serif;
text-align: center;
padding: 2rem;
pointer-events: none;
}
h1 {
font-size: 2.5rem;
background: linear-gradient(90deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 1rem;
animation: glitch 2s infinite;
}
@keyframes glitch {
0% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
25% {
text-shadow:
-2px 0 #ff00c8,
2px 0 #00fff9;
}
50% {
text-shadow:
2px 0 #00fff9,
-2px 0 #ff00c8;
}
75% {
text-shadow:
-1px 0 #00fff9,
1px 0 #ff00c8;
}
100% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
}
.legal-section {
text-align: left;
max-width: 700px;
margin: 2rem auto;
padding: 1rem 2rem;
background: rgba(255, 255, 255, 0.05);
border-left: 4px solid #00ffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.legal-section p {
font-size: 1rem;
margin-bottom: 1rem;
color: #ccc;
line-height: 1.6;
}
a {
color: white;
}
a:hover {
opacity: 0.8;
}
</style>

View File

@ -1,18 +0,0 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

View File

@ -1,19 +0,0 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@ -1,6 +0,0 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});

38
www/.htaccess Normal file
View File

@ -0,0 +1,38 @@
RewriteEngine On
# -------------------------------------------
# 1. Rewrite /blog/<ID> to /blog.php?id=<ID>
# -------------------------------------------
RewriteRule ^blog/([^/]+)/?$ /blog.php?id=$1 [L,QSA]
# ----------------------------------------------------
# 2. Rewrite /lib/* or /blog/* to /error.php?code=404
# with 404 response code (internal rewriting)
# ----------------------------------------------------
RewriteCond %{REQUEST_URI} ^/(lib|blog)(/.*)?$
RewriteRule ^ - [R=404,L]
ErrorDocument 404 /error.php?code=404
# ----------------------------------------------------
# 3. Custom error handler for all other errors
# Sends proper status code to client
# ----------------------------------------------------
ErrorDocument 400 /error.php?code=400
ErrorDocument 401 /error.php?code=401
ErrorDocument 403 /error.php?code=403
ErrorDocument 500 /error.php?code=500
ErrorDocument 502 /error.php?code=502
ErrorDocument 503 /error.php?code=503
# RewriteEngine On
# ErrorDocument 404 /404.php
# RewriteRule ^b/([0-9]+)$ /blog.php?id=$1 [QSA,L]
# RewriteRule ^/lib - [R=404,L]
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^.*$ - [R=404,L]

View File

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 192 B

View File

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 604 B

View File

Before

Width:  |  Height:  |  Size: 826 B

After

Width:  |  Height:  |  Size: 826 B

View File

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

View File

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 483 B

View File

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 146 B

70
www/blog.php Normal file
View File

@ -0,0 +1,70 @@
<?php
require_once __DIR__ . '/lib/_index.php';
$id = (int) $_GET['id'];
$path = __DIR__ . "/blog/$id.svg";
if (!file_exists($path)) {
http_response_code(404);
require __DIR__ . '/404.php';
exit;
}
$svg = file_get_contents($path);
if (str_starts_with($svg, '<?xml')) {
$svg = preg_replace('/^\s*<\?xml[^>]+>\s*/', '', $svg);
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
<base target="_blank">
<title>Blog</title>
<style>
html,
body {
min-height: max(100%, 100vh);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
main {
display: flex;
justify-content: center;
}
main>svg {
width: min(95vw, 1000px);
height: auto;
}
main>svg a {
cursor: pointer;
}
main>svg a:hover {
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<?= $svg ?>
</main>
</body>
</html>

7
www/blog/0.json Normal file
View File

@ -0,0 +1,7 @@
{
"links": {
"google": "https://google.com",
"handwritten.blog": "https://handwritten.blog",
"blog-lehnert.dev": "mailto:blog@lehnert.dev"
}
}

20490
www/blog/0.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 1.6 MiB

135
www/error.php Normal file
View File

@ -0,0 +1,135 @@
<?php
require_once __DIR__ . '/lib/_index.php';
$code = 500;
if (isset($_GET['code'])) {
$code = (int) $_GET['code'];
}
$msg = '🪐 Unknown error! Something went wrong in the fabric of spacetime.';
$action = 'Stabilize systems';
$dest = $_SERVER['REQUEST_URI'] ?? '/';
switch ($code) {
case 400:
$msg = '🛰️ Bad request! Your signal got scrambled mid-transmission—packet lost in space.';
$action = 'Resend request';
break;
case 401:
$msg = '🔒 Unauthorized! You need proper credentials to dock with this system.';
$action = 'Initiate login';
$dest = '/login';
break;
case 403:
$msg = '🚫 Forbidden! This space sector is read-only for your clearance level.';
$action = 'Return to ship';
$dest = '/';
break;
case 404:
$msg = '🛸 Page not found! It mightve been abducted or deleted by rogue AIs.';
$action = 'Beam me back';
$dest = '/';
break;
case 500:
$msg = '💥 System failure! The mainframe just had a meltdown and is crying in binary 😢 (01001100...).';
$action = 'Reboot core';
break;
case 501:
$msg = '🧪 Not implemented! This function is still in alpha testing on Mars.';
$action = 'Go back safely';
break;
case 502:
$msg = '🌐 Bad gateway! The relay between galaxies misrouted your request.';
$action = 'Retry transmission';
break;
case 503:
$msg = '☕ Offline! Our servers are recharging on the dark side of the moon.';
$action = 'Check back soon';
break;
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
<title>Oops!</title>
<style>
<?= default_styles() ?>
<?= common_styles() ?>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
text-align: center;
font-family: "Inter", sans-serif;
overflow: hidden;
}
h1 {
font-size: 6rem;
margin: 0;
background: linear-gradient(90deg, #ff0066, #00ffff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: glitch 2s infinite;
}
p {
font-size: 1.25rem;
margin: 1rem 0 2rem;
color: #ccc;
max-width: 600px;
}
.btn {
text-decoration: none;
background: #00ffff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 999px;
font-size: 1rem;
color: #1e1e2f;
cursor: pointer;
transition: background 0.3s ease;
}
.btn:hover {
background: #ff0066;
color: white;
}
</style>
</head>
<body>
<main>
<h1><?= $code ?></h1>
<p><?= $msg ?></p>
<a class="btn" href="<?= $dest ?>"><?= $action ?></a>
</main>
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

82
www/imprint/index.php Normal file
View File

@ -0,0 +1,82 @@
<?php
require_once __DIR__ . '/../lib/_index.php';
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
<title>Privacy Policy</title>
<style>
<?= default_styles() ?>
<?= common_styles() ?>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
font-family: "Inter", sans-serif;
text-align: center;
padding: 2rem;
}
h1 {
font-size: 2.5rem;
background: linear-gradient(90deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 1rem;
animation: glitch 2s infinite;
}
.legal-section {
text-align: left;
max-width: 700px;
margin: 2rem auto;
padding: 1rem 2rem;
background: rgba(255, 255, 255, 0.05);
border-left: 4px solid #00ffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.legal-section p {
font-size: 1rem;
margin-bottom: 1rem;
color: #ccc;
line-height: 1.6;
}
</style>
</head>
<body>
<main>
<h1>Impressum<br />Imprint</h1>
<div class="legal-section">
<p><b>Angaben gemäß § 5 TMG:</b></p>
<p>
Ludwig Lehnert <br />
Zedernstr. 41 <br />
90441 Nürnberg <br />
Deutschland
</p>
<p>
<b>Kontakt:</b> <br />
<!-- Telefon: [Ihre Telefonnummer] <br /> -->
E-Mail: info@lehnert.dev
</p>
</div>
</main>
</body>
</html>

159
www/index.php Normal file
View File

@ -0,0 +1,159 @@
<?php
require_once __DIR__ . "/lib/_index.php";
[$points, $edges, $sizes] = stars_random();
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
<title>lehnert.dev</title>
<meta name="description" content="Welcome to my digital playground.">
<meta name="keywords" content="Ludwig Lehnert,Nürnberg,Pegnitz,Nuremberg,Informatik,Computer,Copmuter Science" />
<style>
<?= default_styles() ?>
<?= common_styles() ?>
<?= stars_styles() ?>
#stars-edges {
z-index: 0;
}
#stars-container {
z-index: 1;
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
font-family: "Inter", sans-serif;
text-align: center;
padding: 2rem;
pointer-events: none;
}
h1 {
font-size: 3.5rem;
background: linear-gradient(90deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: glow 3s ease-in-out infinite alternate;
}
p {
font-size: 1.2rem;
max-width: 600px;
margin-top: 1rem;
color: #ccc;
}
.social-links {
display: flex;
gap: 0.75rem;
margin-top: 1.5rem;
pointer-events: auto;
}
.social-links a {
padding: 0.75rem;
font-size: 1rem;
background: #00ffff;
color: #1e1e2f;
border: none;
border-radius: 999px;
cursor: pointer;
transition: background 0.3s ease;
display: grid;
place-items: center;
}
.social-links a:hover {
background: #ff00ff;
color: white;
}
.legal-links {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
pointer-events: auto;
}
.legal-links a {
color: rgb(255, 255, 255);
cursor: pointer;
transition: background 0.3s ease;
}
.legal-links a:hover {
opacity: 0.8;
}
@keyframes glow {
from {
text-shadow: 0 0 10px #00ffff;
}
to {
text-shadow: 0 0 20px #ff00ff;
}
}
</style>
</head>
<body>
<?= stars_edges_svg($points, $edges) ?>
<?= stars_container($points, $sizes) ?>
<script>
<?= stars_script($points, $edges, $sizes) ?>
</script>
<main style="position: relative; z-index: 2;">
<h1>Hey, I'm Ludwig 🚀</h1>
<p>Welcome to my digital playground.</p>
<div class="social-links">
<a target="_blank" href="https://instagram.com/lehlud">
<img src="/assets/instagram.svg" alt="" width="25" />
</a>
<a target="_blank" href="https://linkedin.com/in/ludwig-lehnert">
<img src="/assets/linkedin.svg" alt="" width="25" />
</a>
<a target="_blank" href="mailto:info@lehnert.dev">
<img src="/assets/email.svg" alt="" width="25" />
</a>
<a target="_blank" href="https://gitea.cloud.lehnert.dev/explore/repos">
<img src="/assets/git.svg" alt="" width="25" />
</a>
<a target="_blank" href="https://github.com/lehlud">
<img src="/assets/github.svg" alt="" width="25" />
</a>
</div>
<div class="legal-links">
<a href="/imprint">Imprint</a>
<a href="/privacy">Privacy Policy</a>
</div>
</main>
</body>
</html>

5
www/lib/_index.php Normal file
View File

@ -0,0 +1,5 @@
<?php
require_once __DIR__ . '/style.php';
require_once __DIR__ . '/stars.php';
require_once __DIR__ . '/fill.php';

41
www/lib/css/common.css Normal file
View File

@ -0,0 +1,41 @@
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes glitch {
0% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
25% {
text-shadow:
-2px 0 #ff00c8,
2px 0 #00fff9;
}
50% {
text-shadow:
2px 0 #00fff9,
-2px 0 #ff00c8;
}
75% {
text-shadow:
-1px 0 #00fff9,
1px 0 #ff00c8;
}
100% {
text-shadow:
2px 0 #ff00c8,
-2px 0 #00fff9;
}
}

21
www/lib/css/default.css Normal file
View File

@ -0,0 +1,21 @@
html,
body {
/* min-width: 100vw; */
min-height: max(100%, 100vh);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: linear-gradient(135deg,
#1e1e2f 0,
#252547 99vh,
#252547 100vh);
max-width: 100vw;
overflow-x: hidden;
}

37
www/lib/css/stars.css Normal file
View File

@ -0,0 +1,37 @@
#stars-edges {
position: absolute;
height: 100vh;
width: 100vw;
overflow-x: hidden;
}
#stars-edges>* {
transition-duration: 500ms;
}
#stars-edges>*[opacity="1"] {
animation: edgesStrokeColor 3s ease-in-out forwards;
}
#stars-container {
position: absolute;
height: 100vh;
width: 100vw;
overflow-x: hidden;
}
#stars-container>* {
animation: fadeIn 1s forwards;
transition-duration: 500ms;
}
@keyframes edgesStrokeColor {
from {
stroke: #00ffff;
}
to {
stroke: #ff00ff;
}
}

10
www/lib/fill.php Normal file
View File

@ -0,0 +1,10 @@
<?php
function fill_js(string $js, array $params = []): string
{
foreach ($params as $key => $value) {
$js = str_replace('__' . $key . '__()', $value, $js);
}
return $js;
}

74
www/lib/js/stars.js Normal file
View File

@ -0,0 +1,74 @@
const indices = __INDICES__();
const points = __POINTS__();
const edges = __EDGES__();
const sizes = __SIZES__();
const starsEdges = document.getElementById('stars-edges');
const starsEdgesResize = () => {
const w = starsEdges.clientWidth;
const h = starsEdges.clientHeight;
starsEdges.setAttribute('viewBox', `0 0 ${w} ${h}`);
};
starsEdgesResize();
window.addEventListener('resize', starsEdgesResize);
const stars = [...indices];
document.querySelectorAll('#stars-container>*').forEach((star, i) => {
const currEdges = indices.map((o) => {
const [x, y] = (i > o) ? [o, i] : [i, o];
return [document.getElementById(`${x}-${y}`), o];
}).filter(([e]) => !!e);
stars[i] = [star, currEdges];
});
stars.forEach(([star, edges]) => {
star.addEventListener('mouseenter', () => {
edges.forEach(([edge]) => {
edge.setAttribute('opacity', '1');
});
});
star.addEventListener('mouseleave', () => {
edges.forEach(([edge]) => {
edge.setAttribute('opacity', '0');
});
});
});
function chooseRandom(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
let lidx = 0;
const activeEdges = [];
setInterval(() => {
if (Math.random() < 0.3) return;
if (Math.random() < 0.03) lidx = chooseRandom(indices);
let iter = 0;
while (iter++ < 300) {
const [, ledges] = stars[lidx];
let edge, nidx, iter2 = 0;
do {
[edge, nidx] = chooseRandom(ledges);
} while ((edge === undefined || activeEdges.includes(edge)) && iter2++ < 100);
activeEdges.push(edge);
edge.setAttribute("opacity", "1");
setTimeout(() => {
edge.setAttribute("opacity", "0");
activeEdges.splice(activeEdges.indexOf(edge), 1);
}, 4500 + Math.random() * 2500);
lidx = nidx;
return;
}
}, 650);

155
www/lib/stars.php Normal file
View File

@ -0,0 +1,155 @@
<?php
require_once __DIR__ . '/_index.php';
function random_float()
{
return mt_rand() / mt_getrandmax();
}
function choose_random(array $a)
{
return $a[mt_rand() % count($a)];
}
function new_edges()
{
return [];
}
function has_edge(array $edges, array $edge)
{
foreach ($edges as $e) {
if ($e[0] === $edge[0] && $e[1] === $edge[1])
return true;
if ($e[1] === $edge[0] && $e[0] === $edge[1])
return true;
}
return false;
}
function add_edge(array &$edges, array $edge)
{
if (has_edge($edges, $edge))
return;
array_push($edges, $edge);
}
function stars_distance(array $a, array $b)
{
return sqrt(pow($a[0] - $b[0], 2) + pow($a[1] - $b[1], 2)) / sqrt(2);
}
function stars_random(int $amount = 50)
{
$indices = range(0, $amount - 1);
$points = array_map(function () {
return [
round(max(0.02, min(0.98, random_float())), 3),
round(max(0.02, min(0.98, random_float())), 3),
];
}, $indices);
$sizes = array_map(function () {
return round(0.5 + random_float() * 1.2, 3);
}, $indices);
$edges = new_edges();
foreach ($indices as $i) {
$choosable = $indices;
usort($choosable, function ($a, $b) use ($points, $i) {
$cmp = stars_distance($points[$i], $points[$a]) - stars_distance($points[$i], $points[$b]);
if ($cmp < 0)
return -1;
if ($cmp > 0)
return 1;
return 0;
});
$choosable = array_slice($choosable, 0, 10);
$max = 2 + mt_rand() % 2;
for ($j = 0; $j < $max; $j++) {
$o = $i;
$iter = 0;
while ($o === $i || has_edge($edges, [$i, $o])) {
$o = choose_random($choosable);
if ($iter++ > 100)
break;
}
if ($iter <= 100)
add_edge($edges, [$i, $o]);
}
}
return [$points, $edges, $sizes];
}
function stars_container(array $points, array $sizes)
{
$html = '<div id="stars-container" aria-hidden="true">';
$count = count($points);
for ($i = 0; $i < $count; $i++) {
$width = 'max(' . $sizes[$i] . 'vw, ' . $sizes[$i] . 'vh)';
$left = ($points[$i][0] * 100) . 'vw';
$top = ($points[$i][1] * 100) . 'vh';
$html .= "<img src=\"/assets/star.svg\" style=\"position: absolute; width: $width; left: $left; top: $top; translate: -50% -50%;\" />";
}
$html .= '</div>';
return $html;
}
function stars_edges_svg(array $points, array $edges)
{
$html = '<svg id="stars-edges" aria-hidden="true">';
foreach ($edges as [$a, $b]) {
if ($a > $b) {
$tmp = $a;
$a = $b;
$b = $tmp;
}
[$x1, $y1] = $points[$a];
[$x2, $y2] = $points[$b];
$x1 *= 100;
$y1 *= 100;
$x2 *= 100;
$y2 *= 100;
$html .= "<line id=\"$a-$b\" x1=\"$x1%\" y1=\"$y1%\" x2=\"$x2%\" y2=\"$y2%\" stroke=\"white\" stroke-width=\"1.5\" opacity=\"0\" />";
}
$html .= '</svg>';
return $html;
}
function stars_script(array $points, array $edges, array $sizes)
{
$js = file_get_contents(__DIR__ . '/js/stars.js');
return fill_js($js, [
'INDICES' => json_encode(range(0, count($points) - 1)),
'POINTS' => json_encode($points),
'EDGES' => json_encode($edges),
'SIZES' => json_encode($sizes),
]);
}
function stars_styles()
{
return file_get_contents(__DIR__ . '/css/stars.css');
}

11
www/lib/style.php Normal file
View File

@ -0,0 +1,11 @@
<?php
function default_styles()
{
return file_get_contents(__DIR__ . '/css/default.css');
}
function common_styles()
{
return file_get_contents(__DIR__ . '/css/common.css');
}

108
www/privacy/index.php Normal file
View File

@ -0,0 +1,108 @@
<?php
require_once __DIR__ . '/../lib/_index.php';
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
<title>Privacy Policy</title>
<style>
<?= default_styles() ?>
<?= common_styles() ?>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
color: white;
font-family: "Inter", sans-serif;
text-align: center;
padding: 2rem;
}
h1 {
font-size: 2.5rem;
background: linear-gradient(90deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 1rem;
animation: glitch 2s infinite;
}
.legal-section {
text-align: left;
max-width: 700px;
margin: 2rem auto;
padding: 1rem 2rem;
background: rgba(255, 255, 255, 0.05);
border-left: 4px solid #00ffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.legal-section p {
font-size: 1rem;
margin-bottom: 1rem;
color: #ccc;
line-height: 1.6;
}
a {
color: white;
}
a:hover {
opacity: 0.8;
}
</style>
</head>
<body>
<main>
<h1>Datenschutzerklärung<br />Privacy Policy</h1>
<div class="legal-section">
<p><strong>Deutsch:</strong></p>
<p>
Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Beim
Besuch dieser Website werden automatisch Server-Logfiles erfasst
(z.B. IP-Adresse, Uhrzeit des Zugriffs), die zur Gewährleistung des
Betriebs und zur Sicherheit der Website notwendig sind. Diese Daten
werden für maximal 90 Tage gespeichert und danach automatisch
gelöscht.
</p>
<p>
Diese Website wird über die Hetzner Cloud gehostet. Es gelten
zusätzlich die Datenschutzbestimmungen der Hetzner Online GmbH: <a
href="https://www.hetzner.com/de/legal/privacy-policy/" target="_blank">Hetzner
Datenschutzerklärung</a>.
</p>
<p><strong>English:</strong></p>
<p>
We take the protection of your personal data very seriously. When
visiting this website, server log files (e.g. IP address, access
time) are automatically collected for the purpose of operation and
security. These logs are stored for a maximum of 90 days and then
automatically deleted.
</p>
<p>
This website is hosted via Hetzner Cloud. The privacy policy of
Hetzner Online GmbH also applies: <a href="https://www.hetzner.com/legal/privacy-policy"
target="_blank">Hetzner Privacy Policy</a>.
</p>
</div>
</main>
</body>
</html>