moved from svelte -> php with apache
| @ -1 +0,0 @@ | ||||
| .gitignore | ||||
							
								
								
									
										23
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -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-* | ||||
							
								
								
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						| @ -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 | ||||
|  | ||||
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @ -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
									
									
									
									
									
								
							
							
						
						| @ -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=="], | ||||
|   } | ||||
| } | ||||
							
								
								
									
										20
									
								
								nginx.conf
									
									
									
									
									
								
							
							
						
						| @ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @ -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
									
									
								
							
							
						
						| @ -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 {}; | ||||
							
								
								
									
										17
									
								
								src/app.html
									
									
									
									
									
								
							
							
						
						| @ -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> | ||||
| @ -1 +0,0 @@ | ||||
| // place files you want to import through the `$lib` alias in this folder.
 | ||||
| @ -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> | ||||
| @ -1 +0,0 @@ | ||||
| export const prerender = true; | ||||
| @ -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> | ||||
| @ -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> | ||||
| @ -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> | ||||
| @ -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> | ||||
| @ -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; | ||||
| @ -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 | ||||
| } | ||||
| @ -1,6 +0,0 @@ | ||||
| import { sveltekit } from '@sveltejs/kit/vite'; | ||||
| import { defineConfig } from 'vite'; | ||||
| 
 | ||||
| export default defineConfig({ | ||||
| 	plugins: [sveltekit()] | ||||
| }); | ||||
							
								
								
									
										38
									
								
								www/.htaccess
									
									
									
									
									
										Normal 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] | ||||
| Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B | 
| Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 604 B | 
| Before Width: | Height: | Size: 826 B After Width: | Height: | Size: 826 B | 
| Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B | 
| Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 483 B | 
| Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B | 
							
								
								
									
										70
									
								
								www/blog.php
									
									
									
									
									
										Normal 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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 MiB | 
							
								
								
									
										135
									
								
								www/error.php
									
									
									
									
									
										Normal 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 might’ve 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> | ||||
| Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										82
									
								
								www/imprint/index.php
									
									
									
									
									
										Normal 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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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> | ||||