data endpoint for entity 90008 (aka. a website)

Compare changes

Choose any two refs to compare.

Changed files
+6527 -6420
.forgejo
workflows
eunomia
src
static
nix
src
static
-28
.forgejo/workflows/build.yaml
··· 1 - name: build website 2 - 3 - on: 4 - push: 5 - workflow_dispatch: 6 - 7 - jobs: 8 - build: 9 - runs-on: lixpine 10 - steps: 11 - - uses: actions/checkout@v4 12 - with: 13 - github-server-url: 'https://git.gaze.systems' 14 - - uses: https://github.com/cachix/cachix-action@v16 15 - with: 16 - name: gazesys-infra 17 - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 18 - - run: nix show-config 19 - - run: nix build . -L --show-trace 20 - deploy: 21 - runs-on: lixpine 22 - needs: build 23 - steps: 24 - - name: trigger deploy 25 - env: 26 - PAT: ${{ secrets.PAT }} 27 - run: | 28 - curl -X POST "https://git.gaze.systems/api/v1/repos/90008/ark/actions/workflows/deploy.yaml/dispatches" -H "Accept: application/json" -H "Authorization: token $PAT" --json '{"ref": "terra"}'
-1
.gitattributes
··· 1 -
+10 -10
.gitignore
··· 3 3 # Output 4 4 .output 5 5 .vercel 6 - /.svelte-kit 7 - /build 8 - /visitcount 9 - /bouncecount 10 - /legitvisitcount 11 - /fakevisitcount 12 - /distancetravelled 13 - /notes 14 - /note 15 - /data 6 + .svelte-kit 7 + build 8 + visitcount 9 + bouncecount 10 + legitvisitcount 11 + fakevisitcount 12 + distancetravelled 13 + notes 14 + note 15 + data 16 16 17 17 # OS 18 18 .DS_Store
-4
.prettierignore
··· 1 - # Package Managers 2 - package-lock.json 3 - pnpm-lock.yaml 4 - yarn.lock
-8
.prettierrc
··· 1 - { 2 - "useTabs": true, 3 - "singleQuote": true, 4 - "trailingComma": "none", 5 - "printWidth": 100, 6 - "plugins": ["prettier-plugin-svelte"], 7 - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 8 - }
-9
README.md
··· 1 - code for one of its (90008) human-facing data endpoints (aka a website.) 2 - 3 - - the website itself uses sveltekit (w/ typescript) and tailwindcss. it's served with bun. 4 - - the logs and guestbook show posts from its bsky account and a guestbook account respectively, hosted on its pds. 5 - - it's deployed to a server with nix, so it's packaged with nix (see flake.nix). 6 - 7 - notes to itself: 8 - 9 - - don't use tags starting with h- (same goes for p-) in root layout or page, this causes hcard parsers to trip up
+4 -1
deno.json
··· 1 - {} 1 + { 2 + "workspace": ["./eunomia"], 3 + "nodeModulesDir": "auto" 4 + }
+288 -537
deno.lock
··· 1 1 { 2 2 "version": "5", 3 3 "specifiers": { 4 - "npm:@jsr/std__toml@*": "1.0.11", 5 - "npm:@neodrag/svelte@^2.3.3": "2.3.3_svelte@5.45.2__acorn@8.15.0", 4 + "npm:@darkvisitors/sdk@^1.6.0": "1.6.0", 5 + "npm:@jsr/std__toml@1.0.11": "1.0.11", 6 + "npm:@neodrag/svelte@^2.3.3": "2.3.3_svelte@5.46.0__acorn@8.15.0", 6 7 "npm:@rowanmanning/feed-parser@^2.1.1": "2.1.1", 7 8 "npm:@skyware/bot@0.4": "0.4.0", 8 - "npm:@sveltejs/adapter-node@^5.4.0": "5.4.0_@sveltejs+kit@2.49.0__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.45.2____acorn@8.15.0___vite@7.2.4____@types+node@24.10.1____picomatch@4.0.3___@types+node@24.10.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__acorn@8.15.0__@types+node@24.10.1_rollup@4.53.3_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1", 9 - "npm:@sveltejs/kit@^2.49.0": "2.49.0_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_acorn@8.15.0_@types+node@24.10.1", 10 - "npm:@sveltejs/vite-plugin-svelte@^6.1.3": "6.2.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1", 11 - "npm:@tailwindcss/forms@~0.5.10": "0.5.10_tailwindcss@3.4.18__postcss@8.5.6__jiti@1.21.7", 12 - "npm:@tailwindcss/typography@~0.5.16": "0.5.19_tailwindcss@3.4.18__postcss@8.5.6__jiti@1.21.7", 9 + "npm:@sveltejs/adapter-node@^5.4.0": "5.4.0_@sveltejs+kit@2.49.2__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.46.0____acorn@8.15.0___vite@7.3.0____@types+node@25.0.3____picomatch@4.0.3___@types+node@25.0.3__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__acorn@8.15.0__@types+node@25.0.3_rollup@4.53.3_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3", 10 + "npm:@sveltejs/kit@^2.49.2": "2.49.2_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_acorn@8.15.0_@types+node@25.0.3", 11 + "npm:@sveltejs/vite-plugin-svelte@^6.2.1": "6.2.1_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3", 12 + "npm:@tailwindcss/forms@~0.5.11": "0.5.11_tailwindcss@3.4.19__postcss@8.5.6__jiti@1.21.7", 13 + "npm:@tailwindcss/typography@~0.5.19": "0.5.19_tailwindcss@3.4.19__postcss@8.5.6__jiti@1.21.7", 13 14 "npm:@types/deno@^2.5.0": "2.5.0", 14 15 "npm:@types/eslint@^9.6.1": "9.6.1", 15 16 "npm:@types/node-schedule@^2.1.8": "2.1.8", 16 - "npm:@types/node@^24.10.1": "24.10.1", 17 - "npm:autoprefixer@^10.4.22": "10.4.22_postcss@8.5.6", 18 - "npm:eslint-config-prettier@^10.1.8": "10.1.8_eslint@9.39.1", 19 - "npm:eslint-plugin-svelte@^3.13.0": "3.13.0_eslint@9.39.1_svelte@5.45.2__acorn@8.15.0_postcss@8.5.6", 20 - "npm:eslint@^9.39.1": "9.39.1", 17 + "npm:@types/node@^25.0.3": "25.0.3", 18 + "npm:autoprefixer@^10.4.23": "10.4.23_postcss@8.5.6", 19 + "npm:eslint-config-prettier@^10.1.8": "10.1.8_eslint@9.39.2", 20 + "npm:eslint-plugin-svelte@^3.13.1": "3.13.1_eslint@9.39.2_svelte@5.46.0__acorn@8.15.0_postcss@8.5.6", 21 + "npm:eslint@^9.39.2": "9.39.2", 21 22 "npm:globals@^16.5.0": "16.5.0", 22 - "npm:mdsvex@~0.12.6": "0.12.6_svelte@5.45.2__acorn@8.15.0", 23 - "npm:nanoid@^5.1.5": "5.1.6", 23 + "npm:mdsvex@~0.12.6": "0.12.6_svelte@5.46.0__acorn@8.15.0", 24 + "npm:nanoid@^5.1.6": "5.1.6", 24 25 "npm:node-fetch@^3.3.2": "3.3.2", 25 26 "npm:postcss@^8.5.6": "8.5.6", 26 - "npm:prettier-plugin-svelte@^3.4.0": "3.4.0_prettier@3.7.1_svelte@5.45.2__acorn@8.15.0", 27 - "npm:prettier@^3.7.1": "3.7.1", 27 + "npm:prettier-plugin-svelte@^3.4.1": "3.4.1_prettier@3.7.4_svelte@5.46.0__acorn@8.15.0", 28 + "npm:prettier@^3.7.4": "3.7.4", 28 29 "npm:prometheus-remote-write@~0.5.1": "0.5.1_node-fetch@3.3.2", 29 30 "npm:robots-parser@^3.0.1": "3.0.1", 30 - "npm:steamgriddb@^2.2.0": "2.2.1", 31 - "npm:svelte-check@^4.3.4": "4.3.4_svelte@5.45.2__acorn@8.15.0_typescript@5.9.3", 32 - "npm:svelte@^5.45.2": "5.45.2_acorn@8.15.0", 33 - "npm:sveltekit-rate-limiter@0.7": "0.7.0_@sveltejs+kit@2.49.0__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.45.2____acorn@8.15.0___vite@7.2.4____@types+node@24.10.1____picomatch@4.0.3___@types+node@24.10.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__acorn@8.15.0__@types+node@24.10.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1", 34 - "npm:tailwindcss@^3.4.17": "3.4.18_postcss@8.5.6_jiti@1.21.7", 31 + "npm:steamgriddb@^2.2.1": "2.2.1", 32 + "npm:svelte-check@^4.3.5": "4.3.5_svelte@5.46.0__acorn@8.15.0_typescript@5.9.3", 33 + "npm:svelte@^5.46.0": "5.46.0_acorn@8.15.0", 34 + "npm:sveltekit-rate-limiter@0.7": "0.7.0_@sveltejs+kit@2.49.2__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.46.0____acorn@8.15.0___vite@7.3.0____@types+node@25.0.3____picomatch@4.0.3___@types+node@25.0.3__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__acorn@8.15.0__@types+node@25.0.3_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3", 35 + "npm:tailwindcss@^3.4.19": "3.4.19_postcss@8.5.6_jiti@1.21.7", 35 36 "npm:toad-scheduler@^3.1.0": "3.1.0", 36 37 "npm:tslib@^2.8.1": "2.8.1", 37 - "npm:typescript-eslint@^8.48.0": "8.48.0_eslint@9.39.1_typescript@5.9.3_@typescript-eslint+parser@8.48.0__eslint@9.39.1__typescript@5.9.3", 38 - "npm:typescript-svelte-plugin@~0.3.50": "0.3.50_svelte@5.45.2__acorn@8.15.0_typescript@5.9.3", 39 - "npm:typescript@^5.9.2": "5.9.3", 40 - "npm:vite@^7.2.4": "7.2.4_@types+node@24.10.1_picomatch@4.0.3" 38 + "npm:typescript-eslint@^8.50.0": "8.50.0_eslint@9.39.2_typescript@5.9.3_@typescript-eslint+parser@8.50.0__eslint@9.39.2__typescript@5.9.3", 39 + "npm:typescript-svelte-plugin@~0.3.50": "0.3.50_svelte@5.46.0__acorn@8.15.0_typescript@5.9.3", 40 + "npm:typescript@^5.9.3": "5.9.3", 41 + "npm:vite@^7.3.0": "7.3.0_@types+node@25.0.3_picomatch@4.0.3" 41 42 }, 42 43 "npm": { 43 44 "@alloc/quick-lru@5.2.0": { 44 45 "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" 45 46 }, 46 - "@atcute/atproto@3.1.7": { 47 - "integrity": "sha512-3Ym8qaVZg2vf8qw0KO1aue39z/5oik5J+UDoSes1vr8ddw40UVLA5sV4bXSKmLnhzQHiLLgoVZXe4zaKfozPoQ==", 47 + "@atcute/atproto@3.1.9": { 48 + "integrity": "sha512-DyWwHCTdR4hY2BPNbLXgVmm7lI+fceOwWbE4LXbGvbvVtSn+ejSVFaAv01Ra3kWDha0whsOmbJL8JP0QPpf1+w==", 48 49 "dependencies": [ 49 50 "@atcute/lexicons" 50 51 ] ··· 56 57 "@atcute/lexicons" 57 58 ] 58 59 }, 59 - "@atcute/bluesky@3.2.6": { 60 - "integrity": "sha512-jUSSTW5Th1vy0bWBazVHuhGQ3Xz4cX648WvLNpYDv7WPzlFzIWm6cnQCbUToQ+uK3K4WyVuuqYtZqqI0f4wWUQ==", 60 + "@atcute/bluesky@3.2.11": { 61 + "integrity": "sha512-AboS6y4t+zaxIq7E4noue10csSpIuk/Uwo30/l6GgGBDPXrd7STw8Yb5nGZQP+TdG/uC8/c2mm7UnY65SDOh6A==", 61 62 "dependencies": [ 62 63 "@atcute/atproto", 63 64 "@atcute/lexicons" 64 65 ] 65 66 }, 66 - "@atcute/car@3.1.2": { 67 - "integrity": "sha512-OZoi1C20Nj8aDRM/A5JeeQMLsQRm6/B7PqVI7T2tyoojiBsL+Vm42QRKxtTsJg+VFaTnWhOzQbf08GZpf2YW4Q==", 67 + "@atcute/car@3.1.3": { 68 + "integrity": "sha512-WJ13bAEt7TjDMVi09ubjLtvhdljbWInGm9Kfy7Y6NhrmiyC/aZYaA/zHX/bHI6xv1c/h3SQduWqxOr4ae49eqA==", 68 69 "dependencies": [ 69 70 "@atcute/cbor", 70 71 "@atcute/cid", 71 72 "@atcute/uint8array", 72 73 "@atcute/varint", 73 - "yocto-queue@1.2.1" 74 + "yocto-queue@1.2.2" 74 75 ] 75 76 }, 76 - "@atcute/cbor@2.2.6": { 77 - "integrity": "sha512-pDfsn/vPTmgeXZiZdyc5vCGCPSxWlfTUIGFMCd5SroAgoLk1v9xxF7R/8+gt1lj1OKAwCwhS0doVmtLjqqzdbA==", 77 + "@atcute/cbor@2.2.8": { 78 + "integrity": "sha512-UzOAN9BuN6JCXgn0ryV8qZuRJUDrNqrbLd6EFM8jc6RYssjRyGRxNy6RZ1NU/07Hd8Tq/0pz8+nQiMu5Zai5uw==", 78 79 "dependencies": [ 79 80 "@atcute/cid", 80 81 "@atcute/multibase", 81 82 "@atcute/uint8array" 82 83 ] 83 84 }, 84 - "@atcute/cid@2.2.4": { 85 - "integrity": "sha512-6RUMyt7rp6KOSb4TWwifOZURnFrGgKqYyjVkYjiAcscZWgJpJxwoCUCdonxCfxhQtB0yJ+WlfqNXicGB+Pe94A==", 85 + "@atcute/cid@2.2.6": { 86 + "integrity": "sha512-bTAHHbJ24p+E//V4KCS4xdmd39o211jJswvqQOevj7vk+5IYcgDLx1ryZWZ1sEPOo9x875li/kj5gpKL14RDwQ==", 86 87 "dependencies": [ 87 88 "@atcute/multibase", 88 89 "@atcute/uint8array" 89 90 ] 90 91 }, 91 - "@atcute/client@4.0.4": { 92 - "integrity": "sha512-0vkYe6HcGAef8FS4dlGMqCCPG4I4Lve1R8Amk8UEviUVofiqlv1WGoeez9CJFL8G/7vhcgVV9rPTHLJEjZ4RdQ==", 92 + "@atcute/client@4.1.0": { 93 + "integrity": "sha512-AYhSu3RSDA2VDkVGOmad320NRbUUUf5pCFWJcOzlk25YC/4kyzmMFfpzhf1jjjEcY+anNBXGGhav/kKB1evggQ==", 93 94 "dependencies": [ 94 95 "@atcute/identity", 95 96 "@atcute/lexicons" 96 97 ] 97 98 }, 98 - "@atcute/identity@1.1.1": { 99 - "integrity": "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q==", 99 + "@atcute/identity@1.1.3": { 100 + "integrity": "sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng==", 100 101 "dependencies": [ 101 102 "@atcute/lexicons", 102 103 "@badrap/valita" 103 104 ] 104 105 }, 105 - "@atcute/lexicons@1.2.2": { 106 - "integrity": "sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA==", 106 + "@atcute/lexicons@1.2.5": { 107 + "integrity": "sha512-9yO9WdgxW8jZ7SbzUycH710z+JmsQ9W9n5S6i6eghYju32kkluFmgBeS47r8e8p2+Dv4DemS7o/3SUGsX9FR5Q==", 107 108 "dependencies": [ 108 109 "@standard-schema/spec", 109 110 "esm-env" ··· 115 116 "@atcute/uint8array" 116 117 ] 117 118 }, 118 - "@atcute/ozone@3.1.9": { 119 - "integrity": "sha512-7FoEA1IUdhZzuSQrTY8IzoBLV5g83pEuetjQo++TMANOJwQxTuUXSxo2tg0OfSQ1MaVM9WZ5pgc/6A9ANUzWAQ==", 119 + "@atcute/ozone@3.1.12": { 120 + "integrity": "sha512-YMsyZtEG6n3+JfU/2Hdl1niZ7mCS9qM3hfNV1ihGRkJniDAtGxcQDRa2wf0Gd9GrhoIaLhawl2wtcqZqyutpVg==", 120 121 "dependencies": [ 121 122 "@atcute/atproto", 122 123 "@atcute/bluesky", ··· 132 133 "@badrap/valita@0.4.6": { 133 134 "integrity": "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==" 134 135 }, 135 - "@esbuild/aix-ppc64@0.25.10": { 136 - "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", 137 - "os": ["aix"], 138 - "cpu": ["ppc64"] 136 + "@darkvisitors/sdk@1.6.0": { 137 + "integrity": "sha512-KfAO7Dzg/EGZGNRUVpjK8dBzOe9xQI2bXF9aq8JcEk8BiOIAn+e4Vf+ceVMhOBB8PkuLvRBnJwfADAYXNL0iFg==" 139 138 }, 140 - "@esbuild/aix-ppc64@0.25.12": { 141 - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", 139 + "@esbuild/aix-ppc64@0.27.2": { 140 + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", 142 141 "os": ["aix"], 143 142 "cpu": ["ppc64"] 144 143 }, 145 - "@esbuild/android-arm64@0.25.10": { 146 - "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", 144 + "@esbuild/android-arm64@0.27.2": { 145 + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", 147 146 "os": ["android"], 148 147 "cpu": ["arm64"] 149 148 }, 150 - "@esbuild/android-arm64@0.25.12": { 151 - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", 152 - "os": ["android"], 153 - "cpu": ["arm64"] 154 - }, 155 - "@esbuild/android-arm@0.25.10": { 156 - "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", 157 - "os": ["android"], 158 - "cpu": ["arm"] 159 - }, 160 - "@esbuild/android-arm@0.25.12": { 161 - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", 149 + "@esbuild/android-arm@0.27.2": { 150 + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", 162 151 "os": ["android"], 163 152 "cpu": ["arm"] 164 153 }, 165 - "@esbuild/android-x64@0.25.10": { 166 - "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", 154 + "@esbuild/android-x64@0.27.2": { 155 + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", 167 156 "os": ["android"], 168 157 "cpu": ["x64"] 169 158 }, 170 - "@esbuild/android-x64@0.25.12": { 171 - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", 172 - "os": ["android"], 173 - "cpu": ["x64"] 174 - }, 175 - "@esbuild/darwin-arm64@0.25.10": { 176 - "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", 177 - "os": ["darwin"], 178 - "cpu": ["arm64"] 179 - }, 180 - "@esbuild/darwin-arm64@0.25.12": { 181 - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", 159 + "@esbuild/darwin-arm64@0.27.2": { 160 + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", 182 161 "os": ["darwin"], 183 162 "cpu": ["arm64"] 184 163 }, 185 - "@esbuild/darwin-x64@0.25.10": { 186 - "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", 187 - "os": ["darwin"], 188 - "cpu": ["x64"] 189 - }, 190 - "@esbuild/darwin-x64@0.25.12": { 191 - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", 164 + "@esbuild/darwin-x64@0.27.2": { 165 + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", 192 166 "os": ["darwin"], 193 167 "cpu": ["x64"] 194 168 }, 195 - "@esbuild/freebsd-arm64@0.25.10": { 196 - "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", 169 + "@esbuild/freebsd-arm64@0.27.2": { 170 + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", 197 171 "os": ["freebsd"], 198 172 "cpu": ["arm64"] 199 173 }, 200 - "@esbuild/freebsd-arm64@0.25.12": { 201 - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", 202 - "os": ["freebsd"], 203 - "cpu": ["arm64"] 204 - }, 205 - "@esbuild/freebsd-x64@0.25.10": { 206 - "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", 174 + "@esbuild/freebsd-x64@0.27.2": { 175 + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", 207 176 "os": ["freebsd"], 208 177 "cpu": ["x64"] 209 178 }, 210 - "@esbuild/freebsd-x64@0.25.12": { 211 - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", 212 - "os": ["freebsd"], 213 - "cpu": ["x64"] 214 - }, 215 - "@esbuild/linux-arm64@0.25.10": { 216 - "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", 217 - "os": ["linux"], 218 - "cpu": ["arm64"] 219 - }, 220 - "@esbuild/linux-arm64@0.25.12": { 221 - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", 179 + "@esbuild/linux-arm64@0.27.2": { 180 + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", 222 181 "os": ["linux"], 223 182 "cpu": ["arm64"] 224 183 }, 225 - "@esbuild/linux-arm@0.25.10": { 226 - "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", 184 + "@esbuild/linux-arm@0.27.2": { 185 + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", 227 186 "os": ["linux"], 228 187 "cpu": ["arm"] 229 188 }, 230 - "@esbuild/linux-arm@0.25.12": { 231 - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", 232 - "os": ["linux"], 233 - "cpu": ["arm"] 234 - }, 235 - "@esbuild/linux-ia32@0.25.10": { 236 - "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", 189 + "@esbuild/linux-ia32@0.27.2": { 190 + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", 237 191 "os": ["linux"], 238 192 "cpu": ["ia32"] 239 193 }, 240 - "@esbuild/linux-ia32@0.25.12": { 241 - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", 242 - "os": ["linux"], 243 - "cpu": ["ia32"] 244 - }, 245 - "@esbuild/linux-loong64@0.25.10": { 246 - "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", 194 + "@esbuild/linux-loong64@0.27.2": { 195 + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", 247 196 "os": ["linux"], 248 197 "cpu": ["loong64"] 249 198 }, 250 - "@esbuild/linux-loong64@0.25.12": { 251 - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", 252 - "os": ["linux"], 253 - "cpu": ["loong64"] 254 - }, 255 - "@esbuild/linux-mips64el@0.25.10": { 256 - "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", 199 + "@esbuild/linux-mips64el@0.27.2": { 200 + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", 257 201 "os": ["linux"], 258 202 "cpu": ["mips64el"] 259 203 }, 260 - "@esbuild/linux-mips64el@0.25.12": { 261 - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", 262 - "os": ["linux"], 263 - "cpu": ["mips64el"] 264 - }, 265 - "@esbuild/linux-ppc64@0.25.10": { 266 - "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", 204 + "@esbuild/linux-ppc64@0.27.2": { 205 + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", 267 206 "os": ["linux"], 268 207 "cpu": ["ppc64"] 269 208 }, 270 - "@esbuild/linux-ppc64@0.25.12": { 271 - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", 272 - "os": ["linux"], 273 - "cpu": ["ppc64"] 274 - }, 275 - "@esbuild/linux-riscv64@0.25.10": { 276 - "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", 277 - "os": ["linux"], 278 - "cpu": ["riscv64"] 279 - }, 280 - "@esbuild/linux-riscv64@0.25.12": { 281 - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", 209 + "@esbuild/linux-riscv64@0.27.2": { 210 + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", 282 211 "os": ["linux"], 283 212 "cpu": ["riscv64"] 284 213 }, 285 - "@esbuild/linux-s390x@0.25.10": { 286 - "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", 287 - "os": ["linux"], 288 - "cpu": ["s390x"] 289 - }, 290 - "@esbuild/linux-s390x@0.25.12": { 291 - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", 214 + "@esbuild/linux-s390x@0.27.2": { 215 + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", 292 216 "os": ["linux"], 293 217 "cpu": ["s390x"] 294 218 }, 295 - "@esbuild/linux-x64@0.25.10": { 296 - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", 219 + "@esbuild/linux-x64@0.27.2": { 220 + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", 297 221 "os": ["linux"], 298 222 "cpu": ["x64"] 299 223 }, 300 - "@esbuild/linux-x64@0.25.12": { 301 - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", 302 - "os": ["linux"], 303 - "cpu": ["x64"] 304 - }, 305 - "@esbuild/netbsd-arm64@0.25.10": { 306 - "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", 307 - "os": ["netbsd"], 308 - "cpu": ["arm64"] 309 - }, 310 - "@esbuild/netbsd-arm64@0.25.12": { 311 - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", 224 + "@esbuild/netbsd-arm64@0.27.2": { 225 + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", 312 226 "os": ["netbsd"], 313 227 "cpu": ["arm64"] 314 228 }, 315 - "@esbuild/netbsd-x64@0.25.10": { 316 - "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", 229 + "@esbuild/netbsd-x64@0.27.2": { 230 + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", 317 231 "os": ["netbsd"], 318 232 "cpu": ["x64"] 319 233 }, 320 - "@esbuild/netbsd-x64@0.25.12": { 321 - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", 322 - "os": ["netbsd"], 323 - "cpu": ["x64"] 324 - }, 325 - "@esbuild/openbsd-arm64@0.25.10": { 326 - "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", 234 + "@esbuild/openbsd-arm64@0.27.2": { 235 + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", 327 236 "os": ["openbsd"], 328 237 "cpu": ["arm64"] 329 238 }, 330 - "@esbuild/openbsd-arm64@0.25.12": { 331 - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", 332 - "os": ["openbsd"], 333 - "cpu": ["arm64"] 334 - }, 335 - "@esbuild/openbsd-x64@0.25.10": { 336 - "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", 239 + "@esbuild/openbsd-x64@0.27.2": { 240 + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", 337 241 "os": ["openbsd"], 338 242 "cpu": ["x64"] 339 243 }, 340 - "@esbuild/openbsd-x64@0.25.12": { 341 - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", 342 - "os": ["openbsd"], 343 - "cpu": ["x64"] 344 - }, 345 - "@esbuild/openharmony-arm64@0.25.10": { 346 - "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", 244 + "@esbuild/openharmony-arm64@0.27.2": { 245 + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", 347 246 "os": ["openharmony"], 348 247 "cpu": ["arm64"] 349 248 }, 350 - "@esbuild/openharmony-arm64@0.25.12": { 351 - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", 352 - "os": ["openharmony"], 353 - "cpu": ["arm64"] 354 - }, 355 - "@esbuild/sunos-x64@0.25.10": { 356 - "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", 249 + "@esbuild/sunos-x64@0.27.2": { 250 + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", 357 251 "os": ["sunos"], 358 252 "cpu": ["x64"] 359 253 }, 360 - "@esbuild/sunos-x64@0.25.12": { 361 - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", 362 - "os": ["sunos"], 363 - "cpu": ["x64"] 364 - }, 365 - "@esbuild/win32-arm64@0.25.10": { 366 - "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", 254 + "@esbuild/win32-arm64@0.27.2": { 255 + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", 367 256 "os": ["win32"], 368 257 "cpu": ["arm64"] 369 258 }, 370 - "@esbuild/win32-arm64@0.25.12": { 371 - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", 372 - "os": ["win32"], 373 - "cpu": ["arm64"] 374 - }, 375 - "@esbuild/win32-ia32@0.25.10": { 376 - "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", 377 - "os": ["win32"], 378 - "cpu": ["ia32"] 379 - }, 380 - "@esbuild/win32-ia32@0.25.12": { 381 - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", 259 + "@esbuild/win32-ia32@0.27.2": { 260 + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", 382 261 "os": ["win32"], 383 262 "cpu": ["ia32"] 384 263 }, 385 - "@esbuild/win32-x64@0.25.10": { 386 - "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", 387 - "os": ["win32"], 388 - "cpu": ["x64"] 389 - }, 390 - "@esbuild/win32-x64@0.25.12": { 391 - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", 264 + "@esbuild/win32-x64@0.27.2": { 265 + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", 392 266 "os": ["win32"], 393 267 "cpu": ["x64"] 394 268 }, 395 - "@eslint-community/eslint-utils@4.9.0_eslint@9.39.1": { 269 + "@eslint-community/eslint-utils@4.9.0_eslint@9.39.2": { 396 270 "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", 397 271 "dependencies": [ 398 272 "eslint", ··· 436 310 "strip-json-comments" 437 311 ] 438 312 }, 439 - "@eslint/js@9.39.1": { 440 - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==" 313 + "@eslint/js@9.39.2": { 314 + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==" 441 315 }, 442 316 "@eslint/object-schema@2.1.7": { 443 317 "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==" ··· 506 380 ], 507 381 "tarball": "https://npm.jsr.io/~/11/@jsr/std__toml/1.0.11.tgz" 508 382 }, 509 - "@neodrag/svelte@2.3.3_svelte@5.45.2__acorn@8.15.0": { 383 + "@neodrag/svelte@2.3.3_svelte@5.46.0__acorn@8.15.0": { 510 384 "integrity": "sha512-avXzhrilsBsnMFljhVAQ7h+6hbSIrvRCJ61GCiGbGISkC1QOhjDCNvPZo2+7KVwiYrnUBx4NRH0kTIqrcxv9Lg==", 511 385 "dependencies": [ 512 386 "svelte" ··· 528 402 "@nodelib/fs.scandir", 529 403 "fastq" 530 404 ] 531 - }, 532 - "@pkgjs/parseargs@0.11.0": { 533 - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==" 534 405 }, 535 406 "@polka/url@1.0.0-next.29": { 536 407 "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==" ··· 621 492 "rollup" 622 493 ] 623 494 }, 624 - "@rollup/rollup-android-arm-eabi@4.52.4": { 625 - "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", 626 - "os": ["android"], 627 - "cpu": ["arm"] 628 - }, 629 495 "@rollup/rollup-android-arm-eabi@4.53.3": { 630 496 "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", 631 497 "os": ["android"], 632 498 "cpu": ["arm"] 633 499 }, 634 - "@rollup/rollup-android-arm64@4.52.4": { 635 - "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", 636 - "os": ["android"], 637 - "cpu": ["arm64"] 638 - }, 639 500 "@rollup/rollup-android-arm64@4.53.3": { 640 501 "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", 641 502 "os": ["android"], 642 503 "cpu": ["arm64"] 643 504 }, 644 - "@rollup/rollup-darwin-arm64@4.52.4": { 645 - "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", 646 - "os": ["darwin"], 647 - "cpu": ["arm64"] 648 - }, 649 505 "@rollup/rollup-darwin-arm64@4.53.3": { 650 506 "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", 651 507 "os": ["darwin"], 652 508 "cpu": ["arm64"] 653 509 }, 654 - "@rollup/rollup-darwin-x64@4.52.4": { 655 - "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", 656 - "os": ["darwin"], 657 - "cpu": ["x64"] 658 - }, 659 510 "@rollup/rollup-darwin-x64@4.53.3": { 660 511 "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", 661 512 "os": ["darwin"], 662 513 "cpu": ["x64"] 663 514 }, 664 - "@rollup/rollup-freebsd-arm64@4.52.4": { 665 - "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", 666 - "os": ["freebsd"], 667 - "cpu": ["arm64"] 668 - }, 669 515 "@rollup/rollup-freebsd-arm64@4.53.3": { 670 516 "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", 671 517 "os": ["freebsd"], 672 518 "cpu": ["arm64"] 673 519 }, 674 - "@rollup/rollup-freebsd-x64@4.52.4": { 675 - "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", 676 - "os": ["freebsd"], 677 - "cpu": ["x64"] 678 - }, 679 520 "@rollup/rollup-freebsd-x64@4.53.3": { 680 521 "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", 681 522 "os": ["freebsd"], 682 523 "cpu": ["x64"] 683 524 }, 684 - "@rollup/rollup-linux-arm-gnueabihf@4.52.4": { 685 - "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", 686 - "os": ["linux"], 687 - "cpu": ["arm"] 688 - }, 689 525 "@rollup/rollup-linux-arm-gnueabihf@4.53.3": { 690 526 "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", 691 - "os": ["linux"], 692 - "cpu": ["arm"] 693 - }, 694 - "@rollup/rollup-linux-arm-musleabihf@4.52.4": { 695 - "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", 696 527 "os": ["linux"], 697 528 "cpu": ["arm"] 698 529 }, ··· 701 532 "os": ["linux"], 702 533 "cpu": ["arm"] 703 534 }, 704 - "@rollup/rollup-linux-arm64-gnu@4.52.4": { 705 - "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", 706 - "os": ["linux"], 707 - "cpu": ["arm64"] 708 - }, 709 535 "@rollup/rollup-linux-arm64-gnu@4.53.3": { 710 536 "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", 711 537 "os": ["linux"], 712 538 "cpu": ["arm64"] 713 539 }, 714 - "@rollup/rollup-linux-arm64-musl@4.52.4": { 715 - "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", 716 - "os": ["linux"], 717 - "cpu": ["arm64"] 718 - }, 719 540 "@rollup/rollup-linux-arm64-musl@4.53.3": { 720 541 "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", 721 542 "os": ["linux"], 722 543 "cpu": ["arm64"] 723 544 }, 724 - "@rollup/rollup-linux-loong64-gnu@4.52.4": { 725 - "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", 726 - "os": ["linux"], 727 - "cpu": ["loong64"] 728 - }, 729 545 "@rollup/rollup-linux-loong64-gnu@4.53.3": { 730 546 "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", 731 547 "os": ["linux"], 732 548 "cpu": ["loong64"] 733 - }, 734 - "@rollup/rollup-linux-ppc64-gnu@4.52.4": { 735 - "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", 736 - "os": ["linux"], 737 - "cpu": ["ppc64"] 738 549 }, 739 550 "@rollup/rollup-linux-ppc64-gnu@4.53.3": { 740 551 "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", 741 552 "os": ["linux"], 742 553 "cpu": ["ppc64"] 743 554 }, 744 - "@rollup/rollup-linux-riscv64-gnu@4.52.4": { 745 - "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", 746 - "os": ["linux"], 747 - "cpu": ["riscv64"] 748 - }, 749 555 "@rollup/rollup-linux-riscv64-gnu@4.53.3": { 750 556 "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", 751 557 "os": ["linux"], 752 558 "cpu": ["riscv64"] 753 559 }, 754 - "@rollup/rollup-linux-riscv64-musl@4.52.4": { 755 - "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", 756 - "os": ["linux"], 757 - "cpu": ["riscv64"] 758 - }, 759 560 "@rollup/rollup-linux-riscv64-musl@4.53.3": { 760 561 "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", 761 562 "os": ["linux"], 762 563 "cpu": ["riscv64"] 763 564 }, 764 - "@rollup/rollup-linux-s390x-gnu@4.52.4": { 765 - "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", 766 - "os": ["linux"], 767 - "cpu": ["s390x"] 768 - }, 769 565 "@rollup/rollup-linux-s390x-gnu@4.53.3": { 770 566 "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", 771 567 "os": ["linux"], 772 568 "cpu": ["s390x"] 773 569 }, 774 - "@rollup/rollup-linux-x64-gnu@4.52.4": { 775 - "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", 776 - "os": ["linux"], 777 - "cpu": ["x64"] 778 - }, 779 570 "@rollup/rollup-linux-x64-gnu@4.53.3": { 780 571 "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", 781 - "os": ["linux"], 782 - "cpu": ["x64"] 783 - }, 784 - "@rollup/rollup-linux-x64-musl@4.52.4": { 785 - "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", 786 572 "os": ["linux"], 787 573 "cpu": ["x64"] 788 574 }, ··· 791 577 "os": ["linux"], 792 578 "cpu": ["x64"] 793 579 }, 794 - "@rollup/rollup-openharmony-arm64@4.52.4": { 795 - "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", 796 - "os": ["openharmony"], 797 - "cpu": ["arm64"] 798 - }, 799 580 "@rollup/rollup-openharmony-arm64@4.53.3": { 800 581 "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", 801 582 "os": ["openharmony"], 802 583 "cpu": ["arm64"] 803 584 }, 804 - "@rollup/rollup-win32-arm64-msvc@4.52.4": { 805 - "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", 806 - "os": ["win32"], 807 - "cpu": ["arm64"] 808 - }, 809 585 "@rollup/rollup-win32-arm64-msvc@4.53.3": { 810 586 "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", 811 587 "os": ["win32"], 812 588 "cpu": ["arm64"] 813 589 }, 814 - "@rollup/rollup-win32-ia32-msvc@4.52.4": { 815 - "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", 816 - "os": ["win32"], 817 - "cpu": ["ia32"] 818 - }, 819 590 "@rollup/rollup-win32-ia32-msvc@4.53.3": { 820 591 "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", 821 592 "os": ["win32"], 822 593 "cpu": ["ia32"] 823 594 }, 824 - "@rollup/rollup-win32-x64-gnu@4.52.4": { 825 - "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", 826 - "os": ["win32"], 827 - "cpu": ["x64"] 828 - }, 829 595 "@rollup/rollup-win32-x64-gnu@4.53.3": { 830 596 "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", 831 - "os": ["win32"], 832 - "cpu": ["x64"] 833 - }, 834 - "@rollup/rollup-win32-x64-msvc@4.52.4": { 835 - "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", 836 597 "os": ["win32"], 837 598 "cpu": ["x64"] 838 599 }, ··· 892 653 "acorn" 893 654 ] 894 655 }, 895 - "@sveltejs/adapter-node@5.4.0_@sveltejs+kit@2.49.0__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.45.2____acorn@8.15.0___vite@7.2.4____@types+node@24.10.1____picomatch@4.0.3___@types+node@24.10.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__acorn@8.15.0__@types+node@24.10.1_rollup@4.53.3_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1": { 656 + "@sveltejs/adapter-node@5.4.0_@sveltejs+kit@2.49.2__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.46.0____acorn@8.15.0___vite@7.3.0____@types+node@25.0.3____picomatch@4.0.3___@types+node@25.0.3__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__acorn@8.15.0__@types+node@25.0.3_rollup@4.53.3_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3": { 896 657 "integrity": "sha512-NMsrwGVPEn+J73zH83Uhss/hYYZN6zT3u31R3IHAn3MiKC3h8fjmIAhLfTSOeNHr5wPYfjjMg8E+1gyFgyrEcQ==", 897 658 "dependencies": [ 898 659 "@rollup/plugin-commonjs", ··· 902 663 "rollup" 903 664 ] 904 665 }, 905 - "@sveltejs/kit@2.49.0_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_acorn@8.15.0_@types+node@24.10.1": { 906 - "integrity": "sha512-oH8tXw7EZnie8FdOWYrF7Yn4IKrqTFHhXvl8YxXxbKwTMcD/5NNCryUSEXRk2ZR4ojnub0P8rNrsVGHXWqIDtA==", 666 + "@sveltejs/kit@2.49.2_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_acorn@8.15.0_@types+node@25.0.3": { 667 + "integrity": "sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==", 907 668 "dependencies": [ 908 669 "@standard-schema/spec", 909 670 "@sveltejs/acorn-typescript", ··· 924 685 ], 925 686 "bin": true 926 687 }, 927 - "@sveltejs/vite-plugin-svelte-inspector@5.0.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1": { 688 + "@sveltejs/vite-plugin-svelte-inspector@5.0.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3": { 928 689 "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", 929 690 "dependencies": [ 930 691 "@sveltejs/vite-plugin-svelte", ··· 933 694 "vite" 934 695 ] 935 696 }, 936 - "@sveltejs/vite-plugin-svelte@6.2.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1": { 697 + "@sveltejs/vite-plugin-svelte@6.2.1_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3": { 937 698 "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", 938 699 "dependencies": [ 939 700 "@sveltejs/vite-plugin-svelte-inspector", ··· 945 706 "vitefu" 946 707 ] 947 708 }, 948 - "@tailwindcss/forms@0.5.10_tailwindcss@3.4.18__postcss@8.5.6__jiti@1.21.7": { 949 - "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", 709 + "@tailwindcss/forms@0.5.11_tailwindcss@3.4.19__postcss@8.5.6__jiti@1.21.7": { 710 + "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==", 950 711 "dependencies": [ 951 712 "mini-svg-data-uri", 952 713 "tailwindcss" 953 714 ] 954 715 }, 955 - "@tailwindcss/typography@0.5.19_tailwindcss@3.4.18__postcss@8.5.6__jiti@1.21.7": { 716 + "@tailwindcss/typography@0.5.19_tailwindcss@3.4.19__postcss@8.5.6__jiti@1.21.7": { 956 717 "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", 957 718 "dependencies": [ 958 719 "postcss-selector-parser@6.0.10", ··· 987 748 "@types/node-schedule@2.1.8": { 988 749 "integrity": "sha512-k00g6Yj/oUg/CDC+MeLHUzu0+OFxWbIqrFfDiLi6OPKxTujvpv29mHGM8GtKr7B+9Vv92FcK/8mRqi1DK5f3hA==", 989 750 "dependencies": [ 990 - "@types/node@24.2.0" 751 + "@types/node@22.15.15" 991 752 ] 992 753 }, 993 - "@types/node@24.10.1": { 994 - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", 754 + "@types/node@22.15.15": { 755 + "integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==", 995 756 "dependencies": [ 996 - "undici-types@7.16.0" 757 + "undici-types@6.21.0" 997 758 ] 998 759 }, 999 - "@types/node@24.2.0": { 1000 - "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", 760 + "@types/node@25.0.3": { 761 + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", 1001 762 "dependencies": [ 1002 - "undici-types@7.10.0" 763 + "undici-types@7.16.0" 1003 764 ] 1004 765 }, 1005 766 "@types/resolve@1.20.2": { ··· 1008 769 "@types/unist@2.0.11": { 1009 770 "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" 1010 771 }, 1011 - "@typescript-eslint/eslint-plugin@8.48.0_@typescript-eslint+parser@8.48.0__eslint@9.39.1__typescript@5.9.3_eslint@9.39.1_typescript@5.9.3": { 1012 - "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", 772 + "@typescript-eslint/eslint-plugin@8.50.0_@typescript-eslint+parser@8.50.0__eslint@9.39.2__typescript@5.9.3_eslint@9.39.2_typescript@5.9.3": { 773 + "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==", 1013 774 "dependencies": [ 1014 775 "@eslint-community/regexpp", 1015 776 "@typescript-eslint/parser", ··· 1018 779 "@typescript-eslint/utils", 1019 780 "@typescript-eslint/visitor-keys", 1020 781 "eslint", 1021 - "graphemer", 1022 782 "ignore@7.0.5", 1023 783 "natural-compare", 1024 784 "ts-api-utils", 1025 785 "typescript" 1026 786 ] 1027 787 }, 1028 - "@typescript-eslint/parser@8.48.0_eslint@9.39.1_typescript@5.9.3": { 1029 - "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", 788 + "@typescript-eslint/parser@8.50.0_eslint@9.39.2_typescript@5.9.3": { 789 + "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", 1030 790 "dependencies": [ 1031 791 "@typescript-eslint/scope-manager", 1032 792 "@typescript-eslint/types", ··· 1037 797 "typescript" 1038 798 ] 1039 799 }, 1040 - "@typescript-eslint/project-service@8.48.0_typescript@5.9.3": { 1041 - "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", 800 + "@typescript-eslint/project-service@8.50.0_typescript@5.9.3": { 801 + "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==", 1042 802 "dependencies": [ 1043 803 "@typescript-eslint/tsconfig-utils", 1044 804 "@typescript-eslint/types", ··· 1046 806 "typescript" 1047 807 ] 1048 808 }, 1049 - "@typescript-eslint/scope-manager@8.48.0": { 1050 - "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", 809 + "@typescript-eslint/scope-manager@8.50.0": { 810 + "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==", 1051 811 "dependencies": [ 1052 812 "@typescript-eslint/types", 1053 813 "@typescript-eslint/visitor-keys" 1054 814 ] 1055 815 }, 1056 - "@typescript-eslint/tsconfig-utils@8.48.0_typescript@5.9.3": { 1057 - "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", 816 + "@typescript-eslint/tsconfig-utils@8.50.0_typescript@5.9.3": { 817 + "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==", 1058 818 "dependencies": [ 1059 819 "typescript" 1060 820 ] 1061 821 }, 1062 - "@typescript-eslint/type-utils@8.48.0_eslint@9.39.1_typescript@5.9.3": { 1063 - "integrity": "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==", 822 + "@typescript-eslint/type-utils@8.50.0_eslint@9.39.2_typescript@5.9.3": { 823 + "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==", 1064 824 "dependencies": [ 1065 825 "@typescript-eslint/types", 1066 826 "@typescript-eslint/typescript-estree", ··· 1071 831 "typescript" 1072 832 ] 1073 833 }, 1074 - "@typescript-eslint/types@8.48.0": { 1075 - "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==" 834 + "@typescript-eslint/types@8.50.0": { 835 + "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==" 1076 836 }, 1077 - "@typescript-eslint/typescript-estree@8.48.0_typescript@5.9.3": { 1078 - "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", 837 + "@typescript-eslint/typescript-estree@8.50.0_typescript@5.9.3": { 838 + "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==", 1079 839 "dependencies": [ 1080 840 "@typescript-eslint/project-service", 1081 841 "@typescript-eslint/tsconfig-utils", ··· 1089 849 "typescript" 1090 850 ] 1091 851 }, 1092 - "@typescript-eslint/utils@8.48.0_eslint@9.39.1_typescript@5.9.3": { 1093 - "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", 852 + "@typescript-eslint/utils@8.50.0_eslint@9.39.2_typescript@5.9.3": { 853 + "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==", 1094 854 "dependencies": [ 1095 855 "@eslint-community/eslint-utils", 1096 856 "@typescript-eslint/scope-manager", ··· 1100 860 "typescript" 1101 861 ] 1102 862 }, 1103 - "@typescript-eslint/visitor-keys@8.48.0": { 1104 - "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", 863 + "@typescript-eslint/visitor-keys@8.50.0": { 864 + "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==", 1105 865 "dependencies": [ 1106 866 "@typescript-eslint/types", 1107 867 "eslint-visitor-keys@4.2.1" ··· 1154 914 "asynckit@0.4.0": { 1155 915 "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 1156 916 }, 1157 - "autoprefixer@10.4.22_postcss@8.5.6": { 1158 - "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", 917 + "autoprefixer@10.4.23_postcss@8.5.6": { 918 + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", 1159 919 "dependencies": [ 1160 920 "browserslist", 1161 921 "caniuse-lite", 1162 922 "fraction.js", 1163 - "normalize-range", 1164 923 "picocolors", 1165 924 "postcss", 1166 925 "postcss-value-parser" 1167 926 ], 1168 927 "bin": true 1169 928 }, 1170 - "axios@1.12.2": { 1171 - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", 929 + "axios@1.13.2": { 930 + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", 1172 931 "dependencies": [ 1173 932 "follow-redirects", 1174 933 "form-data", ··· 1181 940 "balanced-match@1.0.2": { 1182 941 "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 1183 942 }, 1184 - "baseline-browser-mapping@2.8.31": { 1185 - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", 943 + "baseline-browser-mapping@2.9.11": { 944 + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", 1186 945 "bin": true 1187 946 }, 1188 947 "binary-extensions@2.3.0": { ··· 1207 966 "fill-range" 1208 967 ] 1209 968 }, 1210 - "browserslist@4.28.0": { 1211 - "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", 969 + "browserslist@4.28.1": { 970 + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", 1212 971 "dependencies": [ 1213 972 "baseline-browser-mapping", 1214 973 "caniuse-lite", ··· 1231 990 "camelcase-css@2.0.1": { 1232 991 "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" 1233 992 }, 1234 - "caniuse-lite@1.0.30001757": { 1235 - "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==" 993 + "caniuse-lite@1.0.30001761": { 994 + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==" 1236 995 }, 1237 996 "chalk@4.1.2": { 1238 997 "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", ··· 1345 1104 "gopd" 1346 1105 ] 1347 1106 }, 1348 - "electron-to-chromium@1.5.262": { 1349 - "integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==" 1107 + "electron-to-chromium@1.5.267": { 1108 + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==" 1350 1109 }, 1351 1110 "es-define-property@1.0.1": { 1352 1111 "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" ··· 1369 1128 "hasown" 1370 1129 ] 1371 1130 }, 1372 - "esbuild@0.25.12": { 1373 - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", 1131 + "esbuild@0.27.2": { 1132 + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", 1374 1133 "optionalDependencies": [ 1375 - "@esbuild/aix-ppc64@0.25.12", 1376 - "@esbuild/android-arm@0.25.12", 1377 - "@esbuild/android-arm64@0.25.12", 1378 - "@esbuild/android-x64@0.25.12", 1379 - "@esbuild/darwin-arm64@0.25.12", 1380 - "@esbuild/darwin-x64@0.25.12", 1381 - "@esbuild/freebsd-arm64@0.25.12", 1382 - "@esbuild/freebsd-x64@0.25.12", 1383 - "@esbuild/linux-arm@0.25.12", 1384 - "@esbuild/linux-arm64@0.25.12", 1385 - "@esbuild/linux-ia32@0.25.12", 1386 - "@esbuild/linux-loong64@0.25.12", 1387 - "@esbuild/linux-mips64el@0.25.12", 1388 - "@esbuild/linux-ppc64@0.25.12", 1389 - "@esbuild/linux-riscv64@0.25.12", 1390 - "@esbuild/linux-s390x@0.25.12", 1391 - "@esbuild/linux-x64@0.25.12", 1392 - "@esbuild/netbsd-arm64@0.25.12", 1393 - "@esbuild/netbsd-x64@0.25.12", 1394 - "@esbuild/openbsd-arm64@0.25.12", 1395 - "@esbuild/openbsd-x64@0.25.12", 1396 - "@esbuild/openharmony-arm64@0.25.12", 1397 - "@esbuild/sunos-x64@0.25.12", 1398 - "@esbuild/win32-arm64@0.25.12", 1399 - "@esbuild/win32-ia32@0.25.12", 1400 - "@esbuild/win32-x64@0.25.12" 1134 + "@esbuild/aix-ppc64", 1135 + "@esbuild/android-arm", 1136 + "@esbuild/android-arm64", 1137 + "@esbuild/android-x64", 1138 + "@esbuild/darwin-arm64", 1139 + "@esbuild/darwin-x64", 1140 + "@esbuild/freebsd-arm64", 1141 + "@esbuild/freebsd-x64", 1142 + "@esbuild/linux-arm", 1143 + "@esbuild/linux-arm64", 1144 + "@esbuild/linux-ia32", 1145 + "@esbuild/linux-loong64", 1146 + "@esbuild/linux-mips64el", 1147 + "@esbuild/linux-ppc64", 1148 + "@esbuild/linux-riscv64", 1149 + "@esbuild/linux-s390x", 1150 + "@esbuild/linux-x64", 1151 + "@esbuild/netbsd-arm64", 1152 + "@esbuild/netbsd-x64", 1153 + "@esbuild/openbsd-arm64", 1154 + "@esbuild/openbsd-x64", 1155 + "@esbuild/openharmony-arm64", 1156 + "@esbuild/sunos-x64", 1157 + "@esbuild/win32-arm64", 1158 + "@esbuild/win32-ia32", 1159 + "@esbuild/win32-x64" 1401 1160 ], 1402 1161 "scripts": true, 1403 1162 "bin": true ··· 1408 1167 "escape-string-regexp@4.0.0": { 1409 1168 "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" 1410 1169 }, 1411 - "eslint-config-prettier@10.1.8_eslint@9.39.1": { 1170 + "eslint-config-prettier@10.1.8_eslint@9.39.2": { 1412 1171 "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", 1413 1172 "dependencies": [ 1414 1173 "eslint" 1415 1174 ], 1416 1175 "bin": true 1417 1176 }, 1418 - "eslint-plugin-svelte@3.13.0_eslint@9.39.1_svelte@5.45.2__acorn@8.15.0_postcss@8.5.6": { 1419 - "integrity": "sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg==", 1177 + "eslint-plugin-svelte@3.13.1_eslint@9.39.2_svelte@5.46.0__acorn@8.15.0_postcss@8.5.6": { 1178 + "integrity": "sha512-Ng+kV/qGS8P/isbNYVE3sJORtubB+yLEcYICMkUWNaDTb0SwZni/JhAYXh/Dz/q2eThUwWY0VMPZ//KYD1n3eQ==", 1420 1179 "dependencies": [ 1421 1180 "@eslint-community/eslint-utils", 1422 1181 "@jridgewell/sourcemap-codec", ··· 1448 1207 "eslint-visitor-keys@4.2.1": { 1449 1208 "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==" 1450 1209 }, 1451 - "eslint@9.39.1": { 1452 - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", 1210 + "eslint@9.39.2": { 1211 + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", 1453 1212 "dependencies": [ 1454 1213 "@eslint-community/eslint-utils", 1455 1214 "@eslint-community/regexpp", ··· 1505 1264 "estraverse" 1506 1265 ] 1507 1266 }, 1508 - "esrap@2.2.0": { 1509 - "integrity": "sha512-WBmtxe7R9C5mvL4n2le8nMUe4mD5V9oiK2vJpQ9I3y20ENPUomPcphBXE8D1x/Bm84oN1V+lOfgXxtqmxTp3Xg==", 1267 + "esrap@2.2.1": { 1268 + "integrity": "sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==", 1510 1269 "dependencies": [ 1511 1270 "@jridgewell/sourcemap-codec" 1512 1271 ] ··· 1548 1307 "fast-levenshtein@2.0.6": { 1549 1308 "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" 1550 1309 }, 1551 - "fast-xml-parser@5.3.0": { 1552 - "integrity": "sha512-gkWGshjYcQCF+6qtlrqBqELqNqnt4CxruY6UVAWWnqb3DQ6qaNFEIKqzYep1XzHLM/QtrHVCxyPOtTk4LTQ7Aw==", 1310 + "fast-xml-parser@5.3.2": { 1311 + "integrity": "sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA==", 1553 1312 "dependencies": [ 1554 1313 "strnum" 1555 1314 ], ··· 1609 1368 "follow-redirects@1.15.11": { 1610 1369 "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" 1611 1370 }, 1612 - "form-data@4.0.4": { 1613 - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", 1371 + "form-data@4.0.5": { 1372 + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", 1614 1373 "dependencies": [ 1615 1374 "asynckit", 1616 1375 "combined-stream", ··· 1679 1438 "gopd@1.2.0": { 1680 1439 "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" 1681 1440 }, 1682 - "graphemer@1.4.0": { 1683 - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" 1684 - }, 1685 1441 "has-flag@4.0.0": { 1686 1442 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 1687 1443 }, ··· 1833 1589 "math-intrinsics@1.1.0": { 1834 1590 "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" 1835 1591 }, 1836 - "mdsvex@0.12.6_svelte@5.45.2__acorn@8.15.0": { 1592 + "mdsvex@0.12.6_svelte@5.46.0__acorn@8.15.0": { 1837 1593 "integrity": "sha512-pupx2gzWh3hDtm/iDW4WuCpljmyHbHi34r7ktOqpPGvyiM4MyfNgdJ3qMizXdgCErmvYC9Nn/qyjePy+4ss9Wg==", 1838 1594 "dependencies": [ 1839 1595 "@types/mdast", ··· 1929 1685 "normalize-path@3.0.0": { 1930 1686 "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 1931 1687 }, 1932 - "normalize-range@0.1.2": { 1933 - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" 1934 - }, 1935 1688 "object-assign@4.1.1": { 1936 1689 "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1937 1690 }, ··· 2090 1843 "prelude-ls@1.2.1": { 2091 1844 "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" 2092 1845 }, 2093 - "prettier-plugin-svelte@3.4.0_prettier@3.7.1_svelte@5.45.2__acorn@8.15.0": { 2094 - "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", 1846 + "prettier-plugin-svelte@3.4.1_prettier@3.7.4_svelte@5.46.0__acorn@8.15.0": { 1847 + "integrity": "sha512-xL49LCloMoZRvSwa6IEdN2GV6cq2IqpYGstYtMT+5wmml1/dClEoI0MZR78MiVPpu6BdQFfN0/y73yO6+br5Pg==", 2095 1848 "dependencies": [ 2096 1849 "prettier", 2097 1850 "svelte" 2098 1851 ] 2099 1852 }, 2100 - "prettier@3.7.1": { 2101 - "integrity": "sha512-RWKXE4qB3u5Z6yz7omJkjWwmTfLdcbv44jUVHC5NpfXwFGzvpQM798FGv/6WNK879tc+Cn0AAyherCl1KjbyZQ==", 1853 + "prettier@3.7.4": { 1854 + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", 2102 1855 "bin": true 2103 1856 }, 2104 1857 "prism-svelte@0.4.7": { ··· 2107 1860 "prismjs@1.30.0": { 2108 1861 "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==" 2109 1862 }, 2110 - "prometheus-remote-write@0.5.1": { 2111 - "integrity": "sha512-wCQk35u4EWIzGQyCeBKKaVdUefiTyFA8X/SU8B5KQX82k4vwDQW4LMb8pYmyBLZWAUBHcxSoacwosPRE2VvW+w==", 2112 - "dependencies": [ 2113 - "protobufjs", 2114 - "snappyjs" 2115 - ] 2116 - }, 2117 1863 "prometheus-remote-write@0.5.1_node-fetch@3.3.2": { 2118 1864 "integrity": "sha512-wCQk35u4EWIzGQyCeBKKaVdUefiTyFA8X/SU8B5KQX82k4vwDQW4LMb8pYmyBLZWAUBHcxSoacwosPRE2VvW+w==", 2119 1865 "dependencies": [ ··· 2138 1884 "@protobufjs/path", 2139 1885 "@protobufjs/pool", 2140 1886 "@protobufjs/utf8", 2141 - "@types/node@24.2.0", 1887 + "@types/node@25.0.3", 2142 1888 "long" 2143 1889 ], 2144 1890 "scripts": true ··· 2152 1898 "queue-microtask@1.2.3": { 2153 1899 "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 2154 1900 }, 2155 - "quick-lru@7.2.0": { 2156 - "integrity": "sha512-fG4L8TlD1CacJiGMGPxM1/K8l/GaKL2eFQZ6DWAjxZYxSf07DkumbC/Mhh+u/NHvxkfQVL25By0pxBS8QE9ZrQ==" 1901 + "quick-lru@7.3.0": { 1902 + "integrity": "sha512-k9lSsjl36EJdK7I06v7APZCbyGT2vMTsYSRX1Q2nbYmnkBqgUhRkAuzH08Ciotteu/PLJmIF2+tti7o3C/ts2g==" 2157 1903 }, 2158 1904 "rate-limit-threshold@0.1.5": { 2159 1905 "integrity": "sha512-75vpvXC/ZqQJrFDp0dVtfoXZi8kxQP2eBuxVYFvGDfnHhcgE+ZG870u4ItQhWQh54Y6nNwOaaq5g3AL9n27lTg==" ··· 2197 1943 "@types/estree" 2198 1944 ], 2199 1945 "optionalDependencies": [ 2200 - "@rollup/rollup-android-arm-eabi@4.53.3", 2201 - "@rollup/rollup-android-arm64@4.53.3", 2202 - "@rollup/rollup-darwin-arm64@4.53.3", 2203 - "@rollup/rollup-darwin-x64@4.53.3", 2204 - "@rollup/rollup-freebsd-arm64@4.53.3", 2205 - "@rollup/rollup-freebsd-x64@4.53.3", 2206 - "@rollup/rollup-linux-arm-gnueabihf@4.53.3", 2207 - "@rollup/rollup-linux-arm-musleabihf@4.53.3", 2208 - "@rollup/rollup-linux-arm64-gnu@4.53.3", 2209 - "@rollup/rollup-linux-arm64-musl@4.53.3", 2210 - "@rollup/rollup-linux-loong64-gnu@4.53.3", 2211 - "@rollup/rollup-linux-ppc64-gnu@4.53.3", 2212 - "@rollup/rollup-linux-riscv64-gnu@4.53.3", 2213 - "@rollup/rollup-linux-riscv64-musl@4.53.3", 2214 - "@rollup/rollup-linux-s390x-gnu@4.53.3", 2215 - "@rollup/rollup-linux-x64-gnu@4.53.3", 2216 - "@rollup/rollup-linux-x64-musl@4.53.3", 2217 - "@rollup/rollup-openharmony-arm64@4.53.3", 2218 - "@rollup/rollup-win32-arm64-msvc@4.53.3", 2219 - "@rollup/rollup-win32-ia32-msvc@4.53.3", 2220 - "@rollup/rollup-win32-x64-gnu@4.53.3", 2221 - "@rollup/rollup-win32-x64-msvc@4.53.3", 1946 + "@rollup/rollup-android-arm-eabi", 1947 + "@rollup/rollup-android-arm64", 1948 + "@rollup/rollup-darwin-arm64", 1949 + "@rollup/rollup-darwin-x64", 1950 + "@rollup/rollup-freebsd-arm64", 1951 + "@rollup/rollup-freebsd-x64", 1952 + "@rollup/rollup-linux-arm-gnueabihf", 1953 + "@rollup/rollup-linux-arm-musleabihf", 1954 + "@rollup/rollup-linux-arm64-gnu", 1955 + "@rollup/rollup-linux-arm64-musl", 1956 + "@rollup/rollup-linux-loong64-gnu", 1957 + "@rollup/rollup-linux-ppc64-gnu", 1958 + "@rollup/rollup-linux-riscv64-gnu", 1959 + "@rollup/rollup-linux-riscv64-musl", 1960 + "@rollup/rollup-linux-s390x-gnu", 1961 + "@rollup/rollup-linux-x64-gnu", 1962 + "@rollup/rollup-linux-x64-musl", 1963 + "@rollup/rollup-openharmony-arm64", 1964 + "@rollup/rollup-win32-arm64-msvc", 1965 + "@rollup/rollup-win32-ia32-msvc", 1966 + "@rollup/rollup-win32-x64-gnu", 1967 + "@rollup/rollup-win32-x64-msvc", 2222 1968 "fsevents" 2223 1969 ], 2224 1970 "bin": true ··· 2302 2048 "supports-preserve-symlinks-flag@1.0.0": { 2303 2049 "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" 2304 2050 }, 2305 - "svelte-check@4.3.4_svelte@5.45.2__acorn@8.15.0_typescript@5.9.3": { 2306 - "integrity": "sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==", 2051 + "svelte-check@4.3.5_svelte@5.46.0__acorn@8.15.0_typescript@5.9.3": { 2052 + "integrity": "sha512-e4VWZETyXaKGhpkxOXP+B/d0Fp/zKViZoJmneZWe/05Y2aqSKj3YN2nLfYPJBQ87WEiY4BQCQ9hWGu9mPT1a1Q==", 2307 2053 "dependencies": [ 2308 2054 "@jridgewell/trace-mapping", 2309 2055 "chokidar@4.0.3", ··· 2315 2061 ], 2316 2062 "bin": true 2317 2063 }, 2318 - "svelte-eslint-parser@1.4.0_svelte@5.45.2__acorn@8.15.0_postcss@8.5.6": { 2319 - "integrity": "sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA==", 2064 + "svelte-eslint-parser@1.4.1_svelte@5.46.0__acorn@8.15.0_postcss@8.5.6": { 2065 + "integrity": "sha512-1eqkfQ93goAhjAXxZiu1SaKI9+0/sxp4JIWQwUpsz7ybehRE5L8dNuz7Iry7K22R47p5/+s9EM+38nHV2OlgXA==", 2320 2066 "dependencies": [ 2321 2067 "eslint-scope", 2322 2068 "eslint-visitor-keys@4.2.1", ··· 2330 2076 "svelte" 2331 2077 ] 2332 2078 }, 2333 - "svelte2tsx@0.7.45_svelte@5.45.2__acorn@8.15.0_typescript@5.9.3": { 2079 + "svelte2tsx@0.7.45_svelte@5.46.0__acorn@8.15.0_typescript@5.9.3": { 2334 2080 "integrity": "sha512-cSci+mYGygYBHIZLHlm/jYlEc1acjAHqaQaDFHdEBpUueM9kSTnPpvPtSl5VkJOU1qSJ7h1K+6F/LIUYiqC8VA==", 2335 2081 "dependencies": [ 2336 2082 "dedent-js", ··· 2339 2085 "typescript" 2340 2086 ] 2341 2087 }, 2342 - "svelte@5.45.2_acorn@8.15.0": { 2343 - "integrity": "sha512-yyXdW2u3H0H/zxxWoGwJoQlRgaSJLp+Vhktv12iRw2WRDlKqUPT54Fi0K/PkXqrdkcQ98aBazpy0AH4BCBVfoA==", 2088 + "svelte@5.46.0_acorn@8.15.0": { 2089 + "integrity": "sha512-ZhLtvroYxUxr+HQJfMZEDRsGsmU46x12RvAv/zi9584f5KOX7bUrEbhPJ7cKFmUvZTJXi/CFZUYwDC6M1FigPw==", 2344 2090 "dependencies": [ 2345 2091 "@jridgewell/remapping", 2346 2092 "@jridgewell/sourcemap-codec", ··· 2359 2105 "zimmerframe" 2360 2106 ] 2361 2107 }, 2362 - "sveltekit-rate-limiter@0.7.0_@sveltejs+kit@2.49.0__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.45.2____acorn@8.15.0___vite@7.2.4____@types+node@24.10.1____picomatch@4.0.3___@types+node@24.10.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__acorn@8.15.0__@types+node@24.10.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.45.2___acorn@8.15.0__vite@7.2.4___@types+node@24.10.1___picomatch@4.0.3__@types+node@24.10.1_svelte@5.45.2__acorn@8.15.0_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1": { 2108 + "sveltekit-rate-limiter@0.7.0_@sveltejs+kit@2.49.2__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.46.0____acorn@8.15.0___vite@7.3.0____@types+node@25.0.3____picomatch@4.0.3___@types+node@25.0.3__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__acorn@8.15.0__@types+node@25.0.3_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.46.0___acorn@8.15.0__vite@7.3.0___@types+node@25.0.3___picomatch@4.0.3__@types+node@25.0.3_svelte@5.46.0__acorn@8.15.0_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3": { 2363 2109 "integrity": "sha512-aQI8Y1dTWKsB4YiZMBYORMDwy2SaFA2J5t848dEPVNkXWzhqrHqHUBb7QiNiLWqAeWvOJBwD+MZnsbmvbhGQdg==", 2364 2110 "dependencies": [ 2365 2111 "@isaacs/ttlcache", 2366 2112 "@sveltejs/kit" 2367 2113 ] 2368 2114 }, 2369 - "tailwindcss@3.4.18_postcss@8.5.6_jiti@1.21.7": { 2370 - "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", 2115 + "tailwindcss@3.4.19_postcss@8.5.6_jiti@1.21.7": { 2116 + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", 2371 2117 "dependencies": [ 2372 2118 "@alloc/quick-lru", 2373 2119 "arg", ··· 2449 2195 "prelude-ls" 2450 2196 ] 2451 2197 }, 2452 - "typescript-eslint@8.48.0_eslint@9.39.1_typescript@5.9.3_@typescript-eslint+parser@8.48.0__eslint@9.39.1__typescript@5.9.3": { 2453 - "integrity": "sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==", 2198 + "typescript-eslint@8.50.0_eslint@9.39.2_typescript@5.9.3_@typescript-eslint+parser@8.50.0__eslint@9.39.2__typescript@5.9.3": { 2199 + "integrity": "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==", 2454 2200 "dependencies": [ 2455 2201 "@typescript-eslint/eslint-plugin", 2456 2202 "@typescript-eslint/parser", ··· 2460 2206 "typescript" 2461 2207 ] 2462 2208 }, 2463 - "typescript-svelte-plugin@0.3.50_svelte@5.45.2__acorn@8.15.0_typescript@5.9.3": { 2209 + "typescript-svelte-plugin@0.3.50_svelte@5.46.0__acorn@8.15.0_typescript@5.9.3": { 2464 2210 "integrity": "sha512-CD6jMNAYJwqCyQ5zZBDRuveeJvAgIogLwXMf5eXAl4K36wD8W+Npw49h6j5fXnpd7SKcG3uptGpeCGETED6WSA==", 2465 2211 "dependencies": [ 2466 2212 "@jridgewell/sourcemap-codec", ··· 2471 2217 "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 2472 2218 "bin": true 2473 2219 }, 2474 - "undici-types@7.10.0": { 2475 - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" 2220 + "undici-types@6.21.0": { 2221 + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" 2476 2222 }, 2477 2223 "undici-types@7.16.0": { 2478 2224 "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" ··· 2501 2247 "unist-util-visit-parents" 2502 2248 ] 2503 2249 }, 2504 - "update-browserslist-db@1.1.4_browserslist@4.28.0": { 2505 - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", 2250 + "update-browserslist-db@1.2.3_browserslist@4.28.1": { 2251 + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", 2506 2252 "dependencies": [ 2507 2253 "browserslist", 2508 2254 "escalade", ··· 2526 2272 "unist-util-stringify-position" 2527 2273 ] 2528 2274 }, 2529 - "vite@7.2.4_@types+node@24.10.1_picomatch@4.0.3": { 2530 - "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", 2275 + "vite@7.3.0_@types+node@25.0.3_picomatch@4.0.3": { 2276 + "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", 2531 2277 "dependencies": [ 2532 - "@types/node@24.10.1", 2278 + "@types/node@25.0.3", 2533 2279 "esbuild", 2534 2280 "fdir", 2535 2281 "picomatch@4.0.3", ··· 2541 2287 "fsevents" 2542 2288 ], 2543 2289 "optionalPeers": [ 2544 - "@types/node@24.10.1" 2290 + "@types/node@25.0.3" 2545 2291 ], 2546 2292 "bin": true 2547 2293 }, 2548 - "vitefu@1.1.1_vite@7.2.4__@types+node@24.10.1__picomatch@4.0.3_@types+node@24.10.1": { 2294 + "vitefu@1.1.1_vite@7.3.0__@types+node@25.0.3__picomatch@4.0.3_@types+node@25.0.3": { 2549 2295 "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", 2550 2296 "dependencies": [ 2551 2297 "vite" ··· 2573 2319 "yocto-queue@0.1.0": { 2574 2320 "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" 2575 2321 }, 2576 - "yocto-queue@1.2.1": { 2577 - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==" 2322 + "yocto-queue@1.2.2": { 2323 + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==" 2578 2324 }, 2579 2325 "zimmerframe@1.1.4": { 2580 2326 "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==" 2581 2327 } 2582 2328 }, 2583 2329 "workspace": { 2584 - "packageJson": { 2585 - "dependencies": [ 2586 - "npm:@jsr/std__toml@*", 2587 - "npm:@neodrag/svelte@^2.3.3", 2588 - "npm:@rowanmanning/feed-parser@^2.1.1", 2589 - "npm:@skyware/bot@0.4", 2590 - "npm:@sveltejs/adapter-node@^5.4.0", 2591 - "npm:@sveltejs/kit@^2.49.0", 2592 - "npm:@sveltejs/vite-plugin-svelte@^6.1.3", 2593 - "npm:@tailwindcss/forms@~0.5.10", 2594 - "npm:@tailwindcss/typography@~0.5.16", 2595 - "npm:@types/deno@^2.5.0", 2596 - "npm:@types/eslint@^9.6.1", 2597 - "npm:@types/node-schedule@^2.1.8", 2598 - "npm:@types/node@^24.10.1", 2599 - "npm:autoprefixer@^10.4.22", 2600 - "npm:eslint-config-prettier@^10.1.8", 2601 - "npm:eslint-plugin-svelte@^3.13.0", 2602 - "npm:eslint@^9.39.1", 2603 - "npm:globals@^16.5.0", 2604 - "npm:mdsvex@~0.12.6", 2605 - "npm:nanoid@^5.1.5", 2606 - "npm:node-fetch@^3.3.2", 2607 - "npm:postcss@^8.5.6", 2608 - "npm:prettier-plugin-svelte@^3.4.0", 2609 - "npm:prettier@^3.7.1", 2610 - "npm:prometheus-remote-write@~0.5.1", 2611 - "npm:robots-parser@^3.0.1", 2612 - "npm:steamgriddb@^2.2.0", 2613 - "npm:svelte-check@^4.3.4", 2614 - "npm:svelte@^5.45.2", 2615 - "npm:sveltekit-rate-limiter@0.7", 2616 - "npm:tailwindcss@^3.4.17", 2617 - "npm:toad-scheduler@^3.1.0", 2618 - "npm:tslib@^2.8.1", 2619 - "npm:typescript-eslint@^8.48.0", 2620 - "npm:typescript-svelte-plugin@~0.3.50", 2621 - "npm:typescript@^5.9.2", 2622 - "npm:vite@^7.2.4" 2623 - ] 2330 + "members": { 2331 + "eunomia": { 2332 + "packageJson": { 2333 + "dependencies": [ 2334 + "npm:@darkvisitors/sdk@^1.6.0", 2335 + "npm:@jsr/std__toml@1.0.11", 2336 + "npm:@neodrag/svelte@^2.3.3", 2337 + "npm:@rowanmanning/feed-parser@^2.1.1", 2338 + "npm:@skyware/bot@0.4", 2339 + "npm:@sveltejs/adapter-node@^5.4.0", 2340 + "npm:@sveltejs/kit@^2.49.2", 2341 + "npm:@sveltejs/vite-plugin-svelte@^6.2.1", 2342 + "npm:@tailwindcss/forms@~0.5.11", 2343 + "npm:@tailwindcss/typography@~0.5.19", 2344 + "npm:@types/deno@^2.5.0", 2345 + "npm:@types/eslint@^9.6.1", 2346 + "npm:@types/node-schedule@^2.1.8", 2347 + "npm:@types/node@^25.0.3", 2348 + "npm:autoprefixer@^10.4.23", 2349 + "npm:eslint-config-prettier@^10.1.8", 2350 + "npm:eslint-plugin-svelte@^3.13.1", 2351 + "npm:eslint@^9.39.2", 2352 + "npm:globals@^16.5.0", 2353 + "npm:mdsvex@~0.12.6", 2354 + "npm:nanoid@^5.1.6", 2355 + "npm:node-fetch@^3.3.2", 2356 + "npm:postcss@^8.5.6", 2357 + "npm:prettier-plugin-svelte@^3.4.1", 2358 + "npm:prettier@^3.7.4", 2359 + "npm:prometheus-remote-write@~0.5.1", 2360 + "npm:robots-parser@^3.0.1", 2361 + "npm:steamgriddb@^2.2.1", 2362 + "npm:svelte-check@^4.3.5", 2363 + "npm:svelte@^5.46.0", 2364 + "npm:sveltekit-rate-limiter@0.7", 2365 + "npm:tailwindcss@^3.4.19", 2366 + "npm:toad-scheduler@^3.1.0", 2367 + "npm:tslib@^2.8.1", 2368 + "npm:typescript-eslint@^8.50.0", 2369 + "npm:typescript-svelte-plugin@~0.3.50", 2370 + "npm:typescript@^5.9.3", 2371 + "npm:vite@^7.3.0" 2372 + ] 2373 + } 2374 + } 2624 2375 } 2625 2376 } 2626 2377 }
-33
eslint.config.js
··· 1 - import js from '@eslint/js'; 2 - import ts from 'typescript-eslint'; 3 - import svelte from 'eslint-plugin-svelte'; 4 - import prettier from 'eslint-config-prettier'; 5 - import globals from 'globals'; 6 - 7 - /** @type {import('eslint').Linter.FlatConfig[]} */ 8 - export default [ 9 - js.configs.recommended, 10 - ...ts.configs.recommended, 11 - ...svelte.configs['flat/recommended'], 12 - prettier, 13 - ...svelte.configs['flat/prettier'], 14 - { 15 - languageOptions: { 16 - globals: { 17 - ...globals.browser, 18 - ...globals.node 19 - } 20 - } 21 - }, 22 - { 23 - files: ['**/*.svelte'], 24 - languageOptions: { 25 - parserOptions: { 26 - parser: ts.parser 27 - } 28 - } 29 - }, 30 - { 31 - ignores: ['build/', '.svelte-kit/', 'dist/'] 32 - } 33 - ];
+4
eunomia/.prettierignore
··· 1 + # Package Managers 2 + package-lock.json 3 + pnpm-lock.yaml 4 + yarn.lock
+8
eunomia/.prettierrc
··· 1 + { 2 + "useTabs": true, 3 + "singleQuote": true, 4 + "trailingComma": "none", 5 + "printWidth": 100, 6 + "plugins": ["prettier-plugin-svelte"], 7 + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 8 + }
+9
eunomia/README.md
··· 1 + code for one of its (90008) human-facing data endpoints (aka a website.) 2 + 3 + - the website itself uses sveltekit (w/ typescript) and tailwindcss. it's served with bun. 4 + - the logs and guestbook show posts from its bsky account and a guestbook account respectively, hosted on its pds. 5 + - it's deployed to a server with nix, so it's packaged with nix (see flake.nix). 6 + 7 + notes to itself: 8 + 9 + - don't use tags starting with h- (same goes for p-) in root layout or page, this causes hcard parsers to trip up
+1
eunomia/deno.json
··· 1 + {}
+33
eunomia/eslint.config.js
··· 1 + import js from '@eslint/js'; 2 + import ts from 'typescript-eslint'; 3 + import svelte from 'eslint-plugin-svelte'; 4 + import prettier from 'eslint-config-prettier'; 5 + import globals from 'globals'; 6 + 7 + /** @type {import('eslint').Linter.FlatConfig[]} */ 8 + export default [ 9 + js.configs.recommended, 10 + ...ts.configs.recommended, 11 + ...svelte.configs['flat/recommended'], 12 + prettier, 13 + ...svelte.configs['flat/prettier'], 14 + { 15 + languageOptions: { 16 + globals: { 17 + ...globals.browser, 18 + ...globals.node 19 + } 20 + } 21 + }, 22 + { 23 + files: ['**/*.svelte'], 24 + languageOptions: { 25 + parserOptions: { 26 + parser: ts.parser 27 + } 28 + } 29 + }, 30 + { 31 + ignores: ['build/', '.svelte-kit/', 'dist/'] 32 + } 33 + ];
+60
eunomia/package.json
··· 1 + { 2 + "name": "eunomia", 3 + "version": "0.0.1", 4 + "private": true, 5 + "scripts": { 6 + "dev": "vite dev", 7 + "build": "vite build", 8 + "preview": "vite preview" 9 + }, 10 + "devDependencies": { 11 + "@sveltejs/adapter-node": "^5.4.0", 12 + "@sveltejs/kit": "^2.49.2", 13 + "@sveltejs/vite-plugin-svelte": "^6.2.1", 14 + "@tailwindcss/forms": "^0.5.11", 15 + "@tailwindcss/typography": "^0.5.19", 16 + "@types/deno": "^2.5.0", 17 + "@types/eslint": "^9.6.1", 18 + "@types/node": "^25.0.3", 19 + "autoprefixer": "^10.4.23", 20 + "eslint": "^9.39.2", 21 + "eslint-config-prettier": "^10.1.8", 22 + "eslint-plugin-svelte": "^3.13.1", 23 + "globals": "^16.5.0", 24 + "mdsvex": "^0.12.6", 25 + "postcss": "^8.5.6", 26 + "prettier": "^3.7.4", 27 + "prettier-plugin-svelte": "^3.4.1", 28 + "svelte": "^5.46.0", 29 + "svelte-check": "^4.3.5", 30 + "sveltekit-rate-limiter": "^0.7.0", 31 + "tailwindcss": "^3.4.19", 32 + "tslib": "^2.8.1", 33 + "typescript": "^5.9.3", 34 + "typescript-eslint": "^8.50.0", 35 + "typescript-svelte-plugin": "^0.3.50", 36 + "vite": "^7.3.0" 37 + }, 38 + "type": "module", 39 + "dependencies": { 40 + "@darkvisitors/sdk": "^1.6.0", 41 + "@neodrag/svelte": "^2.3.3", 42 + "@rowanmanning/feed-parser": "^2.1.1", 43 + "@skyware/bot": "^0.4.0", 44 + "@std/toml": "npm:@jsr/std__toml@1.0.11", 45 + "@types/node-schedule": "^2.1.8", 46 + "nanoid": "^5.1.6", 47 + "node-fetch": "^3.3.2", 48 + "prometheus-remote-write": "^0.5.1", 49 + "robots-parser": "^3.0.1", 50 + "steamgriddb": "^2.2.1", 51 + "toad-scheduler": "^3.1.0" 52 + }, 53 + "trustedDependencies": [ 54 + "@sveltejs/kit", 55 + "esbuild", 56 + "protobufjs", 57 + "sharp", 58 + "svelte-preprocess" 59 + ] 60 + }
+6
eunomia/postcss.config.js
··· 1 + export default { 2 + plugins: { 3 + tailwindcss: {}, 4 + autoprefixer: {}, 5 + }, 6 + }
+13
eunomia/src/app.d.ts
··· 1 + // See https://kit.svelte.dev/docs/types#app 2 + // for information about these interfaces 3 + declare global { 4 + namespace App { 5 + // interface Error {} 6 + // interface Locals {} 7 + // interface PageData {} 8 + // interface PageState {} 9 + // interface Platform {} 10 + } 11 + } 12 + 13 + export {};
+12
eunomia/src/app.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <link rel="icon" href="%sveltekit.assets%/icons/gaze_site.webp" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 + %sveltekit.head% 8 + </head> 9 + <body class="md:overflow-hidden"> 10 + <div style="display: contents">%sveltekit.body%</div> 11 + </body> 12 + </html>
+19
eunomia/src/components/_window_layout.svelte
··· 1 + <script lang="ts"> 2 + import Window from './window.svelte'; 3 + import '../styles/app.css'; 4 + 5 + interface Props { 6 + title: string; 7 + sticky: boolean; 8 + prose?: boolean; 9 + children?: import('svelte').Snippet; 10 + } 11 + 12 + let { title, sticky, prose = true, children }: Props = $props(); 13 + </script> 14 + 15 + <Window {title} {sticky}> 16 + <div class={prose ? 'prose prose-ralsei leading-6 prose-ul:leading-5' : ''}> 17 + {@render children?.()} 18 + </div> 19 + </Window>
+75
eunomia/src/components/eye.svelte
··· 1 + <script lang="ts"> 2 + import { genDollcode } from '$lib/dollcode'; 3 + 4 + interface Props { 5 + top: number; 6 + left: number; 7 + kind?: string; 8 + visits: number[]; 9 + id: string; 10 + } 11 + 12 + let { top, left, kind = 'normal', visits }: Props = $props(); 13 + 14 + let rotation = $state((Math.random() - 0.5) * 0.4); 15 + const opacity = Math.min(Math.random() * 0.3 + 0.4, 0.7); 16 + 17 + let closed = $state(false); 18 + let look = $state('forward'); 19 + const looks = ['left', 'forward', 'right']; 20 + const pickLook = $derived(() => { 21 + const pickable = looks.filter((l) => { 22 + return l !== look; 23 + }); 24 + return pickable.at(Math.floor(Math.random() * pickable.length)) ?? 'forward'; 25 + }); 26 + const randomizeLook = () => { 27 + look = pickLook(); 28 + rotation = (Math.random() - 0.5) * 0.4; 29 + setTimeout(randomizeLook, 2000 + Math.random() * 6000); 30 + }; 31 + 32 + let src = $derived(closed ? `/eyes/closed.webp` : `/eyes/${kind}_${look}.webp`); 33 + 34 + // generate dollcode based on time, but mod by 3 hours 35 + const timeDollcode = genDollcode((visits[0] / 1000) % (60 * 60 * 24)); 36 + const visitsDollcode = genDollcode(visits.length); 37 + 38 + randomizeLook(); 39 + </script> 40 + 41 + <!-- svelte-ignore a11y_mouse_events_have_key_events --> 42 + <!-- svelte-ignore a11y_no_static_element_interactions --> 43 + <div 44 + class="group flex gap-4 items-center scale-[0.75]" 45 + style=" 46 + position: fixed; 47 + top: {top}vh; 48 + left: {left}%; 49 + opacity: {opacity}; 50 + flex-direction: column-reverse; 51 + " 52 + onmouseover={() => { 53 + closed = true; 54 + }} 55 + onmouseleave={() => { 56 + closed = false; 57 + }} 58 + > 59 + <span class="eye-text !text-base">{visitsDollcode}</span> 60 + <!-- svelte-ignore a11y_missing_attribute --> 61 + <img class="w-24 eye-image" style="transform: rotate({rotation}rad);" {src} /> 62 + <span class="eye-text">{timeDollcode}</span> 63 + </div> 64 + 65 + <style lang="postcss"> 66 + .eye-text { 67 + @apply text-sm [font-family:Doll_Mono] opacity-30 group-hover:opacity-80; 68 + } 69 + .eye-image { 70 + @apply opacity-50 group-hover:opacity-100; 71 + image-rendering: pixelated !important; 72 + filter: drop-shadow(4px 4px 0 theme(colors.ralsei.green.light)) 73 + drop-shadow(-4px -4px 0 theme(colors.ralsei.pink.neon)); 74 + } 75 + </style>
+31
eunomia/src/components/navButton.svelte
··· 1 + <script lang="ts"> 2 + interface Props { 3 + highlight?: boolean; 4 + name: string; 5 + href: string; 6 + iconUri: string; 7 + } 8 + 9 + let { highlight = false, name, href, iconUri }: Props = $props(); 10 + </script> 11 + 12 + <a 13 + class=" 14 + max-w-36 p-0.5 pr-1.5 border-ralsei-white border-4 15 + {highlight 16 + ? 'min-w-36 bg-gradient-to-l to-ralsei-pink-neon/30 from-ralsei-pink-regular/20 from-30% border-ridge motion-safe:animate-pulse hover:animate-none' 17 + : 'w-fit border-double hover:border-solid animate-bounce-reverse hover:underline'} 18 + flex gap-1 items-center justify-center align-middle text-center 19 + {highlight ? 'text-ralsei-pink-regular app-selected-route' : 'text-ralsei-green-light'} 20 + " 21 + title={name} 22 + href="/{href}" 23 + data-sveltekit-preload-data="tap" 24 + > 25 + <img class="max-w-4" style="image-rendering: pixelated;" src={iconUri} alt={name} /> 26 + <div 27 + class="font-monospace text-base/3 overflow-hidden text-ellipsis text-nowrap [text-decoration-line:inherit]" 28 + > 29 + {name} 30 + </div> 31 + </a>
+127
eunomia/src/components/note.svelte
··· 1 + <script module lang="ts"> 2 + import type { Post } from '@skyware/bot'; 3 + 4 + export interface OutgoingLink { 5 + name: string; 6 + link: string; 7 + } 8 + 9 + export interface NoteData { 10 + content: string; 11 + published: number; 12 + hasMedia: boolean; 13 + hasQuote: boolean; 14 + outgoingLinks?: OutgoingLink[]; 15 + purposeAction?: string; 16 + children?: NoteData[]; 17 + depth?: number; 18 + } 19 + 20 + export const flattenNotes = (note: NoteData, currentDepth: number = 0): NoteData[] => { 21 + note.depth = currentDepth; 22 + const flattened = [note]; 23 + if (note.children) { 24 + note.children.forEach((child) => { 25 + flattened.push(...flattenNotes(child, currentDepth + 1)); 26 + }); 27 + } 28 + return flattened; 29 + }; 30 + 31 + export const noteFromBskyPost = (post: Post): NoteData => { 32 + return { 33 + content: post.text, 34 + published: post.createdAt.getTime(), 35 + outgoingLinks: [{ name: 'bsky', link: post.uri }], 36 + hasMedia: 37 + (post.embed?.isImages() || post.embed?.isVideo() || post.embed?.isRecordWithMedia()) ?? 38 + false, 39 + hasQuote: (post.embed?.isRecord() || post.embed?.isRecordWithMedia()) ?? false 40 + }; 41 + }; 42 + </script> 43 + 44 + <script lang="ts"> 45 + import Token from './token.svelte'; 46 + import { renderDate, renderRelativeDate } from '$lib/dateFmt'; 47 + 48 + interface Props { 49 + rootNote: NoteData; 50 + isHighlighted?: boolean; 51 + onlyContent?: boolean; 52 + showOutgoing?: boolean; 53 + mapOutgoingNames?: Record<string, string>; 54 + } 55 + 56 + let { 57 + rootNote, 58 + isHighlighted = false, 59 + onlyContent = false, 60 + showOutgoing = true, 61 + mapOutgoingNames = {} 62 + }: Props = $props(); 63 + 64 + const getOutgoingLink = ({ name, link }: { name: string; link: string }) => { 65 + if (name.startsWith('bsky')) { 66 + // Parse the atproto URI to extract DID and rkey 67 + const match = link.match(/at:\/\/(did:[^/]+)\/[^/]+\/([^/]+)/); 68 + if (match && match.length >= 3) { 69 + // eslint-disable-next-line @typescript-eslint/no-unused-vars 70 + const [_, did, rkey] = match; 71 + link = `https://bsky.app/profile/${did}/post/${rkey}`; 72 + } 73 + if (name === 'bsky-reply') { 74 + return ['reply', link]; 75 + } else { 76 + return [name, link]; 77 + } 78 + } 79 + return [name, link]; 80 + }; 81 + // this is ASS this should be a tailwind class 82 + const getTextShadowStyle = (color: string) => { 83 + return `text-shadow: 0 0 1px theme(colors.ralsei.black), 0 0 5px ${color};`; 84 + }; 85 + const outgoingLinkColors: Record<string, string> = { 86 + bsky: 'rgb(0, 133, 255)', 87 + reply: 'rgb(0, 133, 255)' 88 + }; 89 + </script> 90 + 91 + {#each flattenNotes(rootNote) as note} 92 + <p class="m-0 max-w-[70ch] text-wrap break-words leading-tight align-middle"> 93 + {#if note.depth ?? 0 > 0} 94 + <span class="inline-block">|{'=='.repeat(note.depth ?? 0)}</span>&gt; 95 + {/if} 96 + {#if !onlyContent} 97 + {#if (note.purposeAction ?? '').length > 0} 98 + <Token v="({note.purposeAction!})" small={!isHighlighted} funct /> 99 + {/if} 100 + {#if note.purposeAction !== 'reply'} 101 + <Token 102 + title={renderDate(note.published)} 103 + v={renderRelativeDate(note.published)} 104 + small={!isHighlighted} 105 + /> 106 + {/if} 107 + {/if} 108 + <Token v={note.content} str /> 109 + {#if note.hasMedia}<Token v="-contains media-" keywd small />{/if} 110 + {#if note.hasQuote}<Token v="-contains quote-" keywd small />{/if} 111 + {#if showOutgoing} 112 + {#each (note.outgoingLinks ?? []).map(getOutgoingLink) as [name, link]} 113 + {@const color = outgoingLinkColors[name]} 114 + {@const viewName = mapOutgoingNames[name] ?? name} 115 + {#if viewName.length > 0} 116 + <span class="text-sm" 117 + ><Token v="(" punct /><a 118 + class="hover:motion-safe:animate-squiggle hover:underline" 119 + style="color: {color};{getTextShadowStyle(color)}" 120 + href={link}>{viewName}</a 121 + ><Token v=")" punct /></span 122 + > 123 + {/if} 124 + {/each} 125 + {/if} 126 + </p> 127 + {/each}
+311
eunomia/src/components/pet.svelte
··· 1 + <script module lang="ts"> 2 + import { get, writable } from 'svelte/store'; 3 + 4 + export const localDistanceTravelled = writable(0.0); 5 + export const localBounces = writable(0); 6 + </script> 7 + 8 + <script lang="ts"> 9 + import { draggable } from '@neodrag/svelte'; 10 + import { browser } from '$app/environment'; 11 + 12 + interface Props { 13 + apiToken: string; 14 + } 15 + 16 + let { apiToken }: Props = $props(); 17 + 18 + let lastDragged = 0; 19 + let mouseX = 0; 20 + let mouseY = 0; 21 + 22 + let position = $state({ x: 0, y: 0 }); 23 + let rotation = $state(0.0); 24 + let sprite = $state('/pet/idle.webp'); 25 + let flip = $state(false); 26 + let dragged = $state(false); 27 + 28 + let targetX = 120; 29 + let speed = 10.0; 30 + let tickRate = 20; 31 + let delta = 1.0 / tickRate; 32 + 33 + let strideRadius = 4.0; 34 + let strideAngle = 0; 35 + 36 + const turnStrideWheel = (by: number) => { 37 + strideAngle += by / strideRadius; 38 + if (strideAngle > Math.PI * 2) { 39 + strideAngle -= Math.PI * 2; 40 + } else if (strideAngle < 0) { 41 + strideAngle += Math.PI * 2; 42 + } 43 + }; 44 + 45 + let targetRotation = 0.0; 46 + let rotationVelocity = 0.0; 47 + let springStiffness = 20.0; // How quickly rotation returns to target 48 + let springDamping = 0.2; // Damping factor to prevent oscillation 49 + 50 + const updateRotationSpring = () => { 51 + // Spring physics: calculate force based on distance from target 52 + const springForce = (targetRotation - rotation) * springStiffness; 53 + 54 + // Apply damping to velocity 55 + rotationVelocity = rotationVelocity * (1 - springDamping) + springForce * delta; 56 + 57 + // Update rotation based on velocity 58 + rotation += rotationVelocity * delta; 59 + 60 + // If we're very close to target and barely moving, just snap to target 61 + if (Math.abs(rotation - targetRotation) < 0.01 && Math.abs(rotationVelocity) < 0.01) { 62 + rotation = targetRotation; 63 + rotationVelocity = 0; 64 + } 65 + }; 66 + 67 + // Add spring update to the move function 68 + if (browser) setInterval(updateRotationSpring, tickRate); 69 + 70 + const moveTowards = (from: number, to: number, by: number) => { 71 + let d = (to - from) * 1.0; 72 + let l = Math.abs(d); 73 + let s = Math.sign(d); 74 + let moveBy = s * Math.min(l, by) * delta; 75 + return moveBy; 76 + }; 77 + 78 + // Physics constants 79 + let velocityX = 0; 80 + let velocityY = 0; 81 + let gravity = 200.0; // Gravity strength (positive because -Y is up) 82 + let friction = 0.96; // Air friction 83 + let groundFriction = 0.9; // Ground friction 84 + let bounciness = 0.8; // How much energy is preserved on bounce 85 + 86 + const sendBounceMetrics = () => { 87 + fetch(`/_api/pet/bounce?_token=${apiToken}`); 88 + localBounces.set(get(localBounces) + 1); 89 + }; 90 + 91 + let deltaTravelled = 0.0; 92 + let deltaTravelledTotal = 0.0; 93 + const updateDistanceTravelled = () => { 94 + if (deltaTravelled > 0.1 || deltaTravelled < -0.1) { 95 + localDistanceTravelled.update((n) => { 96 + n += deltaTravelled; 97 + return n; 98 + }); 99 + deltaTravelledTotal += deltaTravelled; 100 + } 101 + deltaTravelled = 0.0; 102 + }; 103 + 104 + const sendTotalDistance = () => { 105 + fetch(`/_api/pet/distance?_token=${apiToken}`, { 106 + method: 'POST', 107 + body: deltaTravelledTotal.toString() 108 + }); 109 + deltaTravelledTotal = 0.0; 110 + }; 111 + 112 + // sending every 5 seconds is probably reliable enough 113 + if (browser) setInterval(sendTotalDistance, 1000 * 5); 114 + 115 + const move = () => { 116 + if (dragged) return; 117 + 118 + // Apply physics when pet is in motion 119 + if (velocityX !== 0 || velocityY !== 0 || position.y !== 0) { 120 + // Apply gravity (remember negative Y is upward) 121 + velocityY += gravity * delta; 122 + 123 + // Apply friction 124 + const fric = position.y === 0 ? groundFriction : friction; 125 + velocityX *= fric; 126 + velocityY *= fric; 127 + 128 + // Update position 129 + const moveX = velocityX * delta; 130 + const moveY = velocityY * delta; 131 + position.x += moveX; 132 + position.y += moveY; 133 + 134 + deltaTravelled += Math.sqrt(moveX ** 2 + moveY ** 2); 135 + updateDistanceTravelled(); 136 + 137 + // Handle window boundaries 138 + const viewportWidth = window.innerWidth; 139 + 140 + // Bounce off sides 141 + if (position.x < 0) { 142 + position.x = 0; 143 + velocityX = -velocityX * bounciness; 144 + sendBounceMetrics(); 145 + } else if (position.x > viewportWidth) { 146 + position.x = viewportWidth; 147 + velocityX = -velocityX * bounciness; 148 + sendBounceMetrics(); 149 + } 150 + 151 + // Bounce off bottom (floor) 152 + if (position.y > 0) { 153 + position.y = 0; 154 + velocityY = -velocityY * bounciness; 155 + // Only bounce if velocity is significant 156 + if (Math.abs(velocityY) < 80) { 157 + velocityY = 0; 158 + position.y = 0; 159 + } else { 160 + sendBounceMetrics(); 161 + } 162 + } 163 + 164 + // reset velocity 165 + if (Math.abs(velocityX) < 5 && Math.abs(velocityY) < 5) { 166 + velocityX = 0; 167 + velocityY = 0; 168 + } 169 + 170 + // Update flip based on velocity 171 + if (Math.abs(velocityX) > 0.5) { 172 + flip = velocityX < 0; 173 + } 174 + 175 + targetRotation = velocityX * 0.02 + velocityY * 0.01; 176 + 177 + return; 178 + } 179 + 180 + // Normal movement when not physics-based 181 + let moveByX = moveTowards( 182 + position.x, 183 + targetX, 184 + speed * ((self.innerWidth ?? 1600.0) / 1600.0) 185 + ); 186 + position.x += moveByX; 187 + 188 + turnStrideWheel(moveByX); 189 + 190 + flip = moveByX < 0.0; 191 + if (moveByX > 0.1 || moveByX < -0.1) { 192 + sprite = strideAngle % Math.PI < Math.PI * 0.5 ? '/pet/walk1.webp' : '/pet/walk2.webp'; 193 + } else { 194 + sprite = '/pet/idle.webp'; 195 + } 196 + 197 + deltaTravelled += Math.abs(moveByX); 198 + updateDistanceTravelled(); 199 + }; 200 + 201 + if (browser) setInterval(move, tickRate); 202 + 203 + const shake = (event: DeviceMotionEvent) => { 204 + const accel = event.acceleration ?? event.accelerationIncludingGravity; 205 + if (accel === null || accel.x === null || accel.y === null) return; 206 + if (Math.abs(accel.x) + Math.abs(accel.y) < 40.0) return; 207 + // make it so that it amplifies motion proportionally to the window size 208 + const windowRatio = (window.innerWidth * 1.0) / (window.innerHeight * 1.0); 209 + velocityX += accel.x * windowRatio * 5.0; 210 + velocityY += accel.y * (1.0 / windowRatio) * 5.0; 211 + sprite = '/pet/pick.webp'; 212 + }; 213 + 214 + if (browser) self.ondevicemotion = shake; 215 + 216 + // this is for ios 217 + const askForShakePermission = () => { 218 + if ( 219 + typeof DeviceMotionEvent !== 'undefined' && 220 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 221 + typeof (DeviceMotionEvent as any).requestPermission === 'function' 222 + ) { 223 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 224 + (DeviceMotionEvent as any) 225 + .requestPermission() 226 + .then((permissionState: string) => { 227 + if (permissionState === 'granted') { 228 + self.ondevicemotion = shake; 229 + } 230 + }) 231 + .catch(console.error); 232 + } 233 + }; 234 + 235 + const pickNewTargetX = () => { 236 + const viewportWidth = self.innerWidth || null; 237 + if (viewportWidth !== null && Math.abs(position.x - targetX) < 5) { 238 + targetX = Math.max( 239 + Math.min( 240 + targetX + (Math.random() - 0.5) * (viewportWidth * 0.5), 241 + viewportWidth * 0.9 242 + ), 243 + viewportWidth * 0.1 244 + ); 245 + } 246 + // Set a random interval for the next target update (between 4-10 seconds) 247 + const randomDelay = Math.floor(Math.random() * 6000) + 4000; 248 + setTimeout(pickNewTargetX, randomDelay); 249 + }; 250 + 251 + // Start the process 252 + if (browser) setTimeout(pickNewTargetX, 1000); 253 + </script> 254 + 255 + <!-- svelte-ignore a11y_missing_attribute --> 256 + <div 257 + use:draggable={{ 258 + position, 259 + applyUserSelectHack: true, 260 + handle: 'img', 261 + bounds: { 262 + bottom: (window.innerHeight / 100) * 5 263 + }, 264 + onDragStart: () => { 265 + sprite = '/pet/pick.webp'; 266 + dragged = true; 267 + }, 268 + onDrag: ({ offsetX, offsetY, event }) => { 269 + position.x = offsetX; 270 + position.y = offsetY; 271 + const mouseXD = event.movementX * delta; 272 + const mouseYD = event.movementY * delta; 273 + deltaTravelled += Math.sqrt(mouseXD ** 2 + mouseYD ** 2); 274 + // reset mouse movement if it's not moving in the same direction so it doesnt accumulate its weird!@!@ 275 + mouseX = Math.sign(mouseXD) != Math.sign(mouseX) ? mouseXD : mouseX + mouseXD; 276 + mouseY = Math.sign(mouseYD) != Math.sign(mouseY) ? mouseYD : mouseY + mouseYD; 277 + rotationVelocity += mouseXD + mouseYD; 278 + lastDragged = Date.now(); 279 + }, 280 + onDragEnd: () => { 281 + // reset mouse movement if we stopped for longer than some time 282 + if (Date.now() - lastDragged > 50) { 283 + mouseX = 0.0; 284 + mouseY = 0.0; 285 + } 286 + // apply velocity based on rotation since we already keep track of that 287 + velocityX = mouseX * 70.0; 288 + velocityY = mouseY * 50.0; 289 + updateDistanceTravelled(); 290 + // reset mouse movement we dont want it to accumulate 291 + mouseX = 0.0; 292 + mouseY = 0.0; 293 + dragged = false; 294 + } 295 + }} 296 + class="fixed bottom-[4.6vh] z-[1000] hover:animate-squiggle" 297 + style="cursor: url('/icons/gaze.webp'), pointer;" 298 + > 299 + <!-- svelte-ignore a11y_no_noninteractive_element_interactions --> 300 + <!-- svelte-ignore a11y_click_events_have_key_events --> 301 + <img 302 + draggable="false" 303 + onclick={askForShakePermission} 304 + style=" 305 + image-rendering: pixelated !important; 306 + transform: rotate({rotation}rad) scaleX({flip ? -1 : 1}); 307 + filter: invert(100%) drop-shadow(2px 2px 0 black) drop-shadow(-2px -2px 0 black); 308 + " 309 + src={sprite} 310 + /> 311 + </div>
+32
eunomia/src/components/token.svelte
··· 1 + <script lang="ts"> 2 + 3 + 4 + interface Props { 5 + punct?: boolean; 6 + keywd?: boolean; 7 + funct?: boolean; 8 + str?: boolean; 9 + small?: boolean; 10 + v: string; 11 + title?: string; 12 + } 13 + 14 + let { 15 + punct = false, 16 + keywd = false, 17 + funct = false, 18 + str = false, 19 + small = false, 20 + title, 21 + v 22 + }: Props = $props(); 23 + 24 + const ty = 25 + punct ? "punctuation" 26 + : keywd ? "keyword" 27 + : funct ? "function" 28 + : str ? "string" 29 + : "" 30 + </script> 31 + 32 + <span {title} class="token {ty} {small ? "text-sm" : ""}">{v}</span>
+34
eunomia/src/components/tooltip.svelte
··· 1 + <script lang="ts"> 2 + import Window from './window.svelte'; 3 + 4 + interface Props { 5 + x?: string; 6 + y?: string; 7 + targetY?: string; 8 + targetX?: string; 9 + tooltipContent?: import('svelte').Snippet; 10 + children?: import('svelte').Snippet; 11 + style?: string; 12 + } 13 + 14 + let { 15 + x = 'translate-x-none', 16 + y = 'translate-y-full', 17 + targetY = 'group-hover:-translate-y-[105%]', 18 + targetX = 'group-hover:-translate-x-2/4', 19 + tooltipContent, 20 + children, 21 + style = '' 22 + }: Props = $props(); 23 + </script> 24 + 25 + <div class="group" {style}> 26 + <div 27 + class="z-10 absolute scale-0 transition-all [transition-timing-function:cubic-bezier(0.4,0,0.2,1.6)] [transition-duration:300ms] opacity-0 group-hover:scale-100 group-hover:opacity-100 {y} {x} {targetY} {targetX}" 28 + > 29 + <Window tooltip> 30 + {#if tooltipContent}{@render tooltipContent()}{:else}Hello world!{/if} 31 + </Window> 32 + </div> 33 + {@render children?.()} 34 + </div>
+124
eunomia/src/components/window.svelte
··· 1 + <script lang="ts"> 2 + import { highestZIndex, isMobile } from '$lib/window.ts'; 3 + import { draggable } from '@neodrag/svelte'; 4 + 5 + interface Props { 6 + title?: string; 7 + iconUri?: string; 8 + id?: string; 9 + sticky?: boolean; 10 + entry?: boolean; 11 + removePadding?: boolean; 12 + center?: boolean; 13 + layered?: boolean; 14 + style?: string; 15 + tooltip?: boolean; 16 + children?: import('svelte').Snippet; 17 + } 18 + 19 + let { 20 + title = undefined, 21 + iconUri = '', 22 + id = '', 23 + sticky = false, 24 + entry = false, 25 + removePadding = false, 26 + center = false, 27 + layered = false, 28 + style = '', 29 + tooltip = false, 30 + children 31 + }: Props = $props(); 32 + 33 + const scaleKeyframes = [ 34 + 'window-open', 35 + 'window-open-vertical', 36 + 'window-open-vertical', 37 + 'window-open-horizontal', 38 + 'window-open-horizontal', 39 + 'window-open-move-up', 40 + 'window-open-move-down', 41 + 'window-open-move-left', 42 + 'window-open-move-right' 43 + ]; 44 + let chosenKeyframe = $derived( 45 + scaleKeyframes.at(Math.floor(Math.random() * scaleKeyframes.length)) 46 + ); 47 + 48 + const isOnMobile = isMobile(); 49 + const _draggable = isOnMobile ? () => {} : draggable; 50 + 51 + const focusWindow = (node: HTMLElement) => { 52 + if (isOnMobile) return; 53 + $highestZIndex += 1; 54 + node.style.zIndex = $highestZIndex.toString(); 55 + }; 56 + </script> 57 + 58 + <!-- svelte-ignore a11y_no_static_element_interactions --> 59 + <!-- svelte-ignore a11y_click_events_have_key_events --> 60 + <div 61 + use:_draggable={{ 62 + disabled: isOnMobile, 63 + applyUserSelectHack: true, 64 + handle: '.window-titlebar', 65 + onDragStart: (data) => { 66 + focusWindow(data.currentNode); 67 + } 68 + }} 69 + onclick={(data) => { 70 + focusWindow(data.currentTarget); 71 + }} 72 + class=" 73 + relative flex flex-col w-full md:w-fit [height:fit-content] 74 + {center ? 'mx-auto' : ''} 75 + {layered ? 'col-[1] row-[1]' : ''} 76 + {sticky ? 'md:sticky md:-top-9' : ''} 77 + max-w-screen-sm lg:max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl 78 + {tooltip ? 'min-w-fit' : ''} 79 + bg-ralsei-black border-ralsei-white border-ridge 80 + {tooltip ? 'border-[6px] border-t-[9px]' : 'border-[7px] border-t-[12px]'} 81 + {isOnMobile || tooltip ? '' : 'hover:-translate-x-1 hover:translate-y-1'} 82 + animate-{chosenKeyframe} drop-shadow-[24px_24px_24px_rgba(1,1,1,0.8)] 83 + {style} 84 + " 85 + {id} 86 + > 87 + {#if title !== undefined} 88 + <div 89 + class=" 90 + window-titlebar p-1 px-2 border-ralsei-white border-8 91 + bg-gradient-to-l from-ralsei-pink-neon to-ralsei-black to-75% 92 + {!isOnMobile ? 'cursor-move' : ''} 93 + " 94 + style="border-style: hidden hidden ridge hidden;" 95 + > 96 + <div class="flex bg-opacity-100 pixelate-bg"> 97 + <h1 98 + class=" 99 + font-monospace text-2xl text-ralsei-pink-regular 100 + grow justify-self-start self-center {entry ? 'p-name' : ''} 101 + " 102 + > 103 + {title} 104 + </h1> 105 + {#if iconUri !== ''} 106 + <img 107 + class="justify-self-end self-center max-h-7" 108 + style="image-rendering: pixelated;" 109 + src={iconUri} 110 + alt={iconUri} 111 + /> 112 + {/if} 113 + </div> 114 + </div> 115 + {/if} 116 + <div 117 + class=" 118 + {removePadding ? '' : tooltip ? 'p-1' : 'p-2'} bg-gradient-to-tl 119 + to-ralsei-pink-neon/15 from-ralsei-pink-regular/20 120 + " 121 + > 122 + {@render children?.()} 123 + </div> 124 + </div>
+142
eunomia/src/hooks.server.ts
··· 1 + import { updateLastPosts } from '$lib/bluesky'; 2 + import { getLastTrack, updateNowPlayingTrack } from '$lib/lastfm'; 3 + import { steamReadLastGame, steamUpdateNowPlaying } from '$lib/steam'; 4 + import { updateCommits } from '$lib/activity'; 5 + import { ToadScheduler, SimpleIntervalJob, Task, AsyncTask } from 'toad-scheduler'; 6 + import { 7 + incrementFakeVisitCount, 8 + incrementLegitVisitCount, 9 + pushMetric, 10 + sendAllMetrics 11 + } from '$lib/metrics'; 12 + import { 13 + addLastVisitor, 14 + decrementVisitCount, 15 + incrementVisitCount, 16 + notifyDarkVisitors, 17 + removeLastVisitor 18 + } from '$lib/visits'; 19 + import { testUa } from '$lib/robots'; 20 + import { error, type Handle } from '@sveltejs/kit'; 21 + import { _fetchEntries } from './routes/(site)/guestbook/+page.server'; 22 + import { sequence } from '@sveltejs/kit/hooks'; 23 + 24 + const updateNowPlaying = async () => { 25 + try { 26 + await Promise.all([steamUpdateNowPlaying(), updateNowPlayingTrack()]); 27 + } catch (err) { 28 + console.log(`error while updating: ${err}`); 29 + } 30 + }; 31 + const refreshContent = async () => { 32 + try { 33 + await Promise.all([updateLastPosts(), _fetchEntries(), updateCommits(), sendAllMetrics()]); 34 + } catch (err) { 35 + console.log(`error while updating: ${err}`); 36 + } 37 + }; 38 + 39 + await Promise.all([updateNowPlaying(), refreshContent()]); 40 + 41 + const scheduler = new ToadScheduler(); 42 + scheduler.addSimpleIntervalJob( 43 + new SimpleIntervalJob( 44 + { seconds: 5 }, 45 + new AsyncTask('updateNowPlaying task', updateNowPlaying, (err) => 46 + console.log(`error while updateNowPlaying: ${err}`) 47 + ) 48 + ) 49 + ); 50 + scheduler.addSimpleIntervalJob( 51 + new SimpleIntervalJob( 52 + { seconds: 30 }, 53 + new AsyncTask('refreshContent task', refreshContent, (err) => 54 + console.log(`error while refreshContent: ${err}`) 55 + ) 56 + ) 57 + ); 58 + 59 + const corsHandler = (allowedOrigins = ['*']) => { 60 + return async ({ event, resolve }: Parameters<Handle>[0]) => { 61 + const origin = event.request.headers.get('origin'); 62 + 63 + const corsHeaders: Record<string, string> = { 64 + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 65 + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 66 + }; 67 + 68 + if (allowedOrigins.includes('*')) 69 + corsHeaders['Access-Control-Allow-Origin'] = '*'; 70 + else if (origin && allowedOrigins.includes(origin)) { 71 + corsHeaders['Access-Control-Allow-Origin'] = origin; 72 + corsHeaders['Access-Control-Allow-Credentials'] = 'true'; 73 + } 74 + 75 + if (event.request.method === 'OPTIONS') 76 + return new Response(null, { headers: corsHeaders }); 77 + 78 + const response = await resolve(event); 79 + 80 + Object.entries(corsHeaders).forEach(([key, value]) => { 81 + response.headers.set(key, value); 82 + }); 83 + 84 + return response; 85 + }; 86 + } 87 + 88 + const handler = async ({ event, resolve }: Parameters<Handle>[0]) => { 89 + notifyDarkVisitors(event.url, event.request); // no await so it doesnt block 90 + 91 + const isPrefetch = () => { 92 + return ( 93 + event.request.headers.get('Sec-Purpose')?.includes('prefetch') || 94 + event.request.headers.get('Purpose')?.includes('prefetch') || 95 + event.request.headers.get('x-purpose')?.includes('preview') || 96 + event.request.headers.get('x-moz')?.includes('prefetch') 97 + ); 98 + }; 99 + const isApi = () => { 100 + return event.url.pathname.startsWith('/_api'); 101 + }; 102 + const isRss = () => { 103 + return event.url.pathname.endsWith('/_rss'); 104 + }; 105 + 106 + // block any requests if the user agent is disallowed by our robots txt 107 + const isFakeVisit = 108 + (await testUa(event.url.toString(), event.request.headers.get('user-agent') ?? '')) === false; 109 + if (isFakeVisit) { 110 + pushMetric({ gazesys_visit_fake_total: await incrementFakeVisitCount() }); 111 + throw error(403, 'get a better user agent silly'); 112 + } 113 + 114 + // only push metric if legit page visit (still want rss to count here though) 115 + const isPageVisit = !isApi() && !isPrefetch(); 116 + if (isPageVisit) pushMetric({ gazesys_visit_real_total: await incrementLegitVisitCount() }); 117 + 118 + // only add visitors if its a "legit" page visit 119 + let id = null; 120 + let valid = false; 121 + if (isPageVisit && !isRss()) { 122 + id = addLastVisitor(event.request, event.cookies); 123 + valid = await incrementVisitCount(event.request, event.cookies); 124 + } 125 + 126 + // actually resolve event 127 + const resp = await resolve(event); 128 + // remove visitors if it was a 404 129 + if (resp.status === 404) { 130 + if (id !== null) removeLastVisitor(id); 131 + if (valid) decrementVisitCount(); 132 + } 133 + 134 + return resp; 135 + }; 136 + 137 + const allowedOrigins = [ 138 + "https://gaze.systems", 139 + "https://ptr.pet", 140 + "https://poor.dog", 141 + ]; 142 + export const handle = sequence(corsHandler(allowedOrigins), handler);
+129
eunomia/src/lib/activity.ts
··· 1 + import { get, writable } from 'svelte/store'; 2 + import { parseFeed } from '@rowanmanning/feed-parser'; 3 + 4 + const lastCommits = writable<Activity[]>([]); 5 + 6 + export const updateCommits = async () => { 7 + try { 8 + const githubFeed = await parseFeedToActivity('https://github.com/90-008.atom'); 9 + const codebergFeed = await parseFeedToActivity('https://codeberg.org/90-008.atom'); 10 + const tangledFeed = await fetchTangledActivity(); 11 + const mergedFeed = sortActivities(githubFeed.concat(codebergFeed).concat(tangledFeed)).slice( 12 + 0, 13 + 7 14 + ); 15 + lastCommits.set(mergedFeed); 16 + } catch (why) { 17 + console.log('could not fetch git activity: ', why); 18 + } 19 + }; 20 + 21 + export const getLastActivity = () => { 22 + return get(lastCommits); 23 + }; 24 + 25 + type Activity = { 26 + source: string; 27 + description: string; 28 + link: string | null; 29 + date: Date | null; 30 + id?: string; 31 + }; 32 + 33 + const toHex = (bytes: number[]): string => { 34 + return bytes.map((b) => b.toString(16).padStart(2, '0')).join(''); 35 + }; 36 + 37 + const fetchTangledActivity = async (): Promise<Activity[]> => { 38 + // todo: auto resolve pds and knots 39 + const did = 'did:plc:dfl62fgb7wtjj3fcbb72naae'; 40 + const pds = 'https://zwsp.xyz'; 41 + const knot = 'https://knot.gaze.systems'; 42 + const activities: Activity[] = []; 43 + 44 + try { 45 + // todo: fetch until we exhaust 46 + const listRes = await fetch( 47 + `${pds}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=sh.tangled.repo` 48 + ); 49 + if (!listRes.ok) return []; 50 + const listData = await listRes.json(); 51 + 52 + for (const record of listData.records || []) { 53 + const repoName = record.value.name; 54 + if (!repoName) continue; 55 + 56 + try { 57 + const logRes = await fetch(`${knot}/xrpc/sh.tangled.repo.log?repo=${did}/${repoName}`); 58 + if (!logRes.ok) continue; 59 + const logData = await logRes.json(); 60 + 61 + const commits = logData.commits || []; 62 + 63 + for (const commit of commits) { 64 + if (!commit.hash) continue; 65 + 66 + const hash = commit.hash ? toHex(commit.hash) : ''; 67 + if (activities.some((a) => a.id === hash)) continue; 68 + 69 + const message = commit.message || ''; 70 + const dateStr = commit.author?.When; 71 + 72 + activities.push({ 73 + source: 'tangled', 74 + description: `${repoName}: ${message}`, 75 + link: `https://tangled.sh/${did}/${repoName}/commit/${hash}`, 76 + date: dateStr ? new Date(dateStr) : null, 77 + id: hash 78 + }); 79 + } 80 + } catch (err) { 81 + console.log(`could not fetch tangled log for ${repoName}:`, err); 82 + } 83 + } 84 + } catch (err) { 85 + console.log('could not fetch tangled repos:', err); 86 + } 87 + return activities; 88 + }; 89 + 90 + const parseFeedToActivity = async (url: string) => { 91 + const response = await fetch(url); 92 + const feed = parseFeed(await response.text()); 93 + 94 + const source = new URL(url).host.split('.')[0]; 95 + const results: Activity[] = []; 96 + for (const item of feed.items) { 97 + const description: string | null = item.description || item.title; 98 + if (description === null) continue; 99 + // dont count mirrored repos 100 + // TODO: probably can implement a deduplication algorithm 101 + if ( 102 + source === 'github' && 103 + ['90-008/ark', '90-008/website', 'ark', 'website', 'trill', 'faunu', 'nucleus'].some((repo) => 104 + description.includes(repo) 105 + ) 106 + ) 107 + continue; 108 + // dont show activity that is just chore 109 + if (item.content?.includes('chore')) continue; 110 + const desc = description.split('</a>').at(1) || description.split('</a>').pop() || ''; 111 + results.push({ 112 + source, 113 + description: desc.replace(/^90-008 /, ''), 114 + link: item.url, 115 + date: item.published || item.updated 116 + }); 117 + } 118 + 119 + return results; 120 + }; 121 + 122 + const sortActivities = (activities: Array<Activity>) => { 123 + return activities.sort((a, b) => { 124 + if (a.date === null && b.date === null) return 0; 125 + if (a.date === null) return 1; 126 + if (b.date === null) return -1; 127 + return b.date.getTime() - a.date.getTime(); 128 + }); 129 + };
+26
eunomia/src/lib/apiToken.ts
··· 1 + import { nanoid } from 'nanoid'; 2 + import { get, writable } from 'svelte/store'; 3 + 4 + const tokens = writable<Map<string, number>>(new Map()); 5 + 6 + export const newToken = () => { 7 + const token = nanoid(100); 8 + tokens.update((v) => v.set(token, Date.now())); 9 + return token; 10 + }; 11 + export const useToken = (token: string) => { 12 + const _tokens = get(tokens); 13 + // delete older tokens 14 + for (const [_token, timestamp] of _tokens) { 15 + if (Date.now() - timestamp > 30 * 60 * 1000) { 16 + _tokens.delete(_token); 17 + } 18 + } 19 + tokens.set(_tokens); 20 + return _tokens.has(token); 21 + }; 22 + 23 + export const checkUrl = (url: URL) => { 24 + const token = url.searchParams.get('_token'); 25 + return token !== null && useToken(token); 26 + };
+60
eunomia/src/lib/bluesky.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { Bot, type Post } from '@skyware/bot'; 3 + import { get, writable } from 'svelte/store'; 4 + 5 + const bskyClient = writable<null | Bot>(null); 6 + 7 + export const getBskyClient = async () => { 8 + let client = get(bskyClient); 9 + if (client === null) { 10 + client = await loginToBsky(); 11 + bskyClient.set(client); 12 + } 13 + return client; 14 + }; 15 + 16 + const loginToBsky = async () => { 17 + const password = env.BSKY_PASSWORD ?? null; 18 + if (password === null) { 19 + throw new Error('no password provided'); 20 + } 21 + const bot = new Bot({ service: 'https://gaze.systems' }); 22 + await bot.login({ identifier: 'guestbook.gaze.systems', password }); 23 + return bot; 24 + }; 25 + 26 + export const getUserPosts = async ( 27 + did: string, 28 + count: number = 10, 29 + cursor: string | null = null 30 + ) => { 31 + const client = await getBskyClient(); 32 + let feedCursor: string | null | undefined = cursor; 33 + const posts: Post[] = []; 34 + // fetch requested amount of posts 35 + while (posts.length < count - 1 && (typeof feedCursor === 'string' || feedCursor === null)) { 36 + const feedData = await client.getUserPosts(did, { 37 + limit: count, 38 + filter: 'posts_no_replies', 39 + cursor: feedCursor === null ? undefined : feedCursor 40 + }); 41 + posts.push(...feedData.posts.filter((post) => post.author.did === did)); 42 + feedCursor = feedData.cursor; 43 + } 44 + return { posts, cursor: feedCursor === null ? undefined : feedCursor }; 45 + }; 46 + 47 + const lastPosts = writable<Post[]>([]); 48 + 49 + export const updateLastPosts = async () => { 50 + try { 51 + const { posts } = await getUserPosts('did:plc:dfl62fgb7wtjj3fcbb72naae', 10); 52 + lastPosts.set(posts); 53 + } catch (err) { 54 + console.log(`can't update last posts ${err}`); 55 + } 56 + }; 57 + 58 + export const getLastPosts = () => { 59 + return get(lastPosts); 60 + };
+22
eunomia/src/lib/convertDate.ts
··· 1 + const months = [ 2 + 'Jan', 3 + 'Feb', 4 + 'Mar', 5 + 'Apr', 6 + 'May', 7 + 'Jun', 8 + 'Jul', 9 + 'Aug', 10 + 'Sep', 11 + 'Oct', 12 + 'Nov', 13 + 'Dec' 14 + ]; 15 + 16 + const convertDate = (published: string) => { 17 + const date = published.substring(0, 10); 18 + const [year, month, day] = date.split('-'); 19 + return `${day} ${months[parseInt(month) - 1]} ${year}`; 20 + }; 21 + 22 + export default convertDate;
+35
eunomia/src/lib/counter.ts
··· 1 + import { get, writable } from 'svelte/store'; 2 + 3 + /** 4 + * Creates a persistent counter that is stored in a file 5 + * @param fileName The name of the file to store the count in 6 + * @param initialValue The initial value if the file doesn't exist 7 + * @returns An object with methods to get, increment, and set the count 8 + */ 9 + export const createFileCounter = async (filePath: string, initialValue: number = 0) => { 10 + let countRaw: string | null = null; 11 + try { 12 + countRaw = await Deno.readTextFile(filePath); 13 + } catch {} 14 + 15 + const counter = writable(parseInt(countRaw ?? initialValue.toString())); 16 + 17 + const saveToFile = async (value: number) => { 18 + await Deno.writeTextFile(filePath, value.toString()); 19 + return value; 20 + }; 21 + 22 + return { 23 + get: () => get(counter), 24 + increment: (amount: number = 1) => { 25 + const currentValue = get(counter) + amount; 26 + counter.set(currentValue); 27 + return saveToFile(currentValue); 28 + }, 29 + set: (value: number) => { 30 + counter.set(value); 31 + return saveToFile(value); 32 + }, 33 + subscribe: counter.subscribe 34 + }; 35 + };
+4
eunomia/src/lib/darkvisitors.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { DarkVisitors } from '@darkvisitors/sdk'; 3 + 4 + export const darkVisitors = new DarkVisitors(env.DARK_VISITORS_TOKEN!);
+25
eunomia/src/lib/dateFmt.ts
··· 1 + export const renderRelativeDate = (timestamp: number) => { 2 + const elapsed = timestamp - new Date().getTime(); 3 + const units: Record<string, number> = { 4 + year: 24 * 60 * 60 * 1000 * 365, 5 + month: (24 * 60 * 60 * 1000 * 365) / 12, 6 + day: 24 * 60 * 60 * 1000, 7 + hour: 60 * 60 * 1000, 8 + minute: 60 * 1000, 9 + second: 1000 10 + }; 11 + const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); 12 + for (const unit in units) 13 + if (Math.abs(elapsed) > units[unit] || unit == 'second') 14 + return rtf.format(Math.round(elapsed / units[unit]), unit as Intl.RelativeTimeFormatUnit); 15 + return ''; 16 + }; 17 + export const renderDate = (timestamp: number) => { 18 + return new Date(timestamp).toLocaleString('en-GB', { 19 + year: '2-digit', 20 + month: '2-digit', 21 + day: '2-digit', 22 + hour: '2-digit', 23 + minute: '2-digit' 24 + }); 25 + };
+23
eunomia/src/lib/dollcode.ts
··· 1 + // https://noe.sh/dollcode/ 2 + const charmap = ['โ–Œ', 'โ––', 'โ–˜']; 3 + export const genDollcode = (number: number) => { 4 + const output = []; 5 + let window = number; 6 + let loopProtection = 1000; 7 + 8 + while (loopProtection > 0 && window > 0) { 9 + const mod = window % 3; 10 + 11 + if (mod == 0) { 12 + window = (window - 3) / 3; 13 + } else { 14 + window = (window - mod) / 3; 15 + } 16 + 17 + output.unshift(charmap[mod]); 18 + 19 + loopProtection--; 20 + } 21 + 22 + return output.join(''); 23 + };
+11
eunomia/src/lib/getTitle.ts
··· 1 + const getTitle = (path: string) => { 2 + let sl = path.split('/'); 3 + sl = sl.splice(1); 4 + if (sl.length > 2) { 5 + sl[0] = sl[0][0]; 6 + } 7 + const newPath = sl.join('/'); 8 + return '//โ‹†โ˜€๏ธŽ. /' + newPath; 9 + }; 10 + 11 + export default getTitle;
+28
eunomia/src/lib/index.ts
··· 1 + import type { Cookies } from '@sveltejs/kit'; 2 + import { hash } from 'node:crypto'; 3 + 4 + export const scopeCookies = (cookies: Cookies, path: string) => { 5 + return { 6 + get: (key: string) => { 7 + return cookies.get(key); 8 + }, 9 + set: (key: string, value: string, props: Omit<Parameters<Cookies['set']>[2], 'path'> = {}) => { 10 + cookies.set(key, value, { ...props, path }); 11 + }, 12 + delete: (key: string, props: Omit<Parameters<Cookies['delete']>[1], 'path'> = {}) => { 13 + cookies.delete(key, { ...props, path }); 14 + } 15 + }; 16 + }; 17 + 18 + const cipherChars = ['#', '%', '+', '=', '//']; 19 + export const fancyText = (input: string) => { 20 + const hashed = hash('sha256', input, 'hex'); 21 + let result = ''; 22 + let idx = 0; 23 + while (idx < hashed.length) { 24 + result += cipherChars[hashed.charCodeAt(idx) % cipherChars.length]; 25 + idx += 1; 26 + } 27 + return result; 28 + };
+160
eunomia/src/lib/lastfm.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { get, writable } from 'svelte/store'; 3 + 4 + const DID = 'did:plc:dfl62fgb7wtjj3fcbb72naae'; 5 + const PDS = 'https://zwsp.xyz'; 6 + const LAST_TRACK_FILE = `${env.WEBSITE_DATA_DIR}/last_track.json`; 7 + const COVER_ART_CACHE_DIR = `${env.WEBSITE_DATA_DIR}/cover_art_cache`; 8 + 9 + type LastTrack = { 10 + name: string; 11 + artist: string; 12 + album: string; 13 + image: string | null; // Single image URL 14 + link: string | null; 15 + when: number; 16 + status: 'playing' | 'played'; 17 + }; 18 + const lastTrack = writable<LastTrack | null>(null); 19 + 20 + // Ensure cache directory exists 21 + const ensureCacheDir = async () => { 22 + try { 23 + await Deno.mkdir(COVER_ART_CACHE_DIR, { recursive: true }); 24 + } catch (err) { 25 + // Directory might already exist, ignore error 26 + } 27 + }; 28 + 29 + // Fetch and cache MusicBrainz cover art 30 + const fetchAndCacheCoverArt = async (releaseMbId: string): Promise<string | null> => { 31 + const cacheFile = `${COVER_ART_CACHE_DIR}/${releaseMbId}.jpg`; 32 + 33 + // Check if already cached 34 + try { 35 + await Deno.stat(cacheFile); 36 + return `/cover_art/${releaseMbId}.jpg`; 37 + } catch { 38 + // Not cached, try to fetch 39 + } 40 + 41 + try { 42 + const mbUrl = `https://coverartarchive.org/release/${releaseMbId}/front-250`; 43 + const response = await fetch(mbUrl); 44 + 45 + if (!response.ok) { 46 + return null; 47 + } 48 + 49 + const imageData = await response.arrayBuffer(); 50 + await Deno.writeFile(cacheFile, new Uint8Array(imageData)); 51 + 52 + return `/cover_art/${releaseMbId}.jpg`; 53 + } catch (err) { 54 + console.log(`Failed to fetch MusicBrainz cover art for ${releaseMbId}:`, err); 55 + return null; 56 + } 57 + }; 58 + 59 + // Get YouTube thumbnail URL 60 + const getYouTubeThumbnail = (originUrl: string | null | undefined): string | null => { 61 + if (!originUrl) return null; 62 + 63 + try { 64 + let videoId: string | null = null; 65 + if (originUrl.includes('youtube.com') || originUrl.includes('music.youtube.com')) { 66 + videoId = new URL(originUrl).searchParams.get('v'); 67 + } else if (originUrl.includes('youtu.be')) { 68 + videoId = originUrl.split('youtu.be/')[1]?.split('?')[0]; 69 + } 70 + if (videoId) { 71 + return `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`; 72 + } 73 + } catch {} 74 + 75 + return null; 76 + }; 77 + 78 + // Get cover art with caching 79 + const getCoverArt = async ( 80 + releaseMbId: string | null | undefined, 81 + originUrl: string | null | undefined 82 + ): Promise<string | null> => { 83 + // Try MusicBrainz first (with caching) 84 + if (releaseMbId) { 85 + const mbImage = await fetchAndCacheCoverArt(releaseMbId); 86 + if (mbImage) return mbImage; 87 + } 88 + 89 + // Fall back to YouTube thumbnail 90 + return getYouTubeThumbnail(originUrl); 91 + }; 92 + 93 + export const getLastTrack = async () => { 94 + try { 95 + const data = await Deno.readTextFile(LAST_TRACK_FILE); 96 + lastTrack.set(JSON.parse(data)); 97 + } catch (why) { 98 + console.log('could not read last track: ', why); 99 + lastTrack.set(null); 100 + } 101 + }; 102 + 103 + const joinArtists = (artists: any[]) => { 104 + if (!artists || artists.length === 0) return null; 105 + const uniqueArtists = [...new Set(artists.map((a) => a.artistName))]; 106 + return uniqueArtists.join(', '); 107 + }; 108 + 109 + export const updateNowPlayingTrack = async () => { 110 + await ensureCacheDir(); 111 + 112 + try { 113 + let track: any = null; 114 + let when: number = Date.now(); 115 + let status: 'playing' | 'played' = 'played'; 116 + 117 + try { 118 + const statusRes = await fetch( 119 + `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${DID}&collection=fm.teal.alpha.actor.status&rkey=self` 120 + ); 121 + if (statusRes.ok) { 122 + const statusData = await statusRes.json(); 123 + if (statusData.value?.item) { 124 + const metadata = statusData.value; 125 + track = statusData.value.item; 126 + if (track.playedTime) when = new Date(track.playedTime).getTime(); 127 + status = 128 + Date.now() / 1000 >= parseInt(metadata.time) + track.duration ? 'played' : 'playing'; 129 + } 130 + } 131 + } catch (err) { 132 + console.log('could not fetch teal status:', err); 133 + } 134 + 135 + if (!track) return; 136 + 137 + const coverArt = await getCoverArt(track.releaseMbId, track.originUrl); 138 + 139 + const data: LastTrack = { 140 + name: track.trackName, 141 + artist: joinArtists(track.artists) ?? 'Unknown Artist', 142 + album: track.releaseName ?? 'Unknown Album', 143 + image: coverArt, 144 + link: 145 + track.originUrl ?? 146 + (track.recordingMbId ? `https://musicbrainz.org/recording/${track.recordingMbId}` : null), 147 + when: when, 148 + status: status 149 + }; 150 + 151 + lastTrack.set(data); 152 + await Deno.writeTextFile(LAST_TRACK_FILE, JSON.stringify(data)); 153 + } catch (why) { 154 + console.log('could not fetch teal fm: ', why); 155 + } 156 + }; 157 + 158 + export const getNowPlayingTrack = () => { 159 + return get(lastTrack); 160 + };
+52
eunomia/src/lib/metrics.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { pushMetrics } from 'prometheus-remote-write'; 3 + import { createFileCounter } from './counter'; 4 + 5 + const endpoint = env.PROMETHEUS_URL; 6 + 7 + export const pushMetric = async ( 8 + metrics: Record<string, number>, 9 + labels: Record<string, string> = {} 10 + ) => { 11 + if (endpoint === undefined) return; 12 + try { 13 + const result = await pushMetrics(metrics, { 14 + url: endpoint, 15 + labels: { 16 + service: 'website', 17 + ...labels 18 + } 19 + }); 20 + if (result.status != 204) { 21 + throw new Error(`failed to push metrics: ${result.status} ${result.errorMessage}`); 22 + } 23 + } catch (err) { 24 + console.log(`failed to push metrics: ${err}`); 25 + } 26 + }; 27 + 28 + export const sendAllMetrics = async () => { 29 + try { 30 + await pushMetric({ 31 + gazesys_pet_bounce_total: bounceCount.get(), 32 + gazesys_visit_fake_total: fakeVisitCount.get(), 33 + gazesys_visit_real_total: legitVisitCount.get(), 34 + gazesys_pet_distance_total: distanceTravelled.get() 35 + }); 36 + } catch (error) { 37 + console.log(`failed to push metrics: ${error}`); 38 + } 39 + }; 40 + 41 + export const bounceCount = await createFileCounter(`${env.WEBSITE_DATA_DIR}/bouncecount`); 42 + export const incrementBounceCount = bounceCount.increment; 43 + 44 + export const legitVisitCount = await createFileCounter(`${env.WEBSITE_DATA_DIR}/legitvisitcount`); 45 + export const incrementLegitVisitCount = legitVisitCount.increment; 46 + 47 + export const fakeVisitCount = await createFileCounter(`${env.WEBSITE_DATA_DIR}/fakevisitcount`); 48 + export const incrementFakeVisitCount = fakeVisitCount.increment; 49 + 50 + export const distanceTravelled = await createFileCounter( 51 + `${env.WEBSITE_DATA_DIR}/distancetravelled` 52 + );
+32
eunomia/src/lib/pushnotif.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + 3 + export const pushNotification = (_content: string) => { 4 + const content = encodeURIComponent(_content); 5 + try { 6 + // post to phone 7 + fetch( 8 + `https://api.day.app/${env.BARK_DEVICE_ID}/gaze.systems/${content}?icon=https://gaze.systems/icons/gaze_site.webp` 9 + ); 10 + // post to discord 11 + fetch(env.DISCORD_NOTIF_WEBHOOK || '', { 12 + method: 'POST', 13 + headers: { 14 + 'content-type': 'application/json' 15 + }, 16 + body: JSON.stringify({ 17 + content: '<@853064602904166430>', 18 + embeds: [ 19 + { 20 + id: 677465216, 21 + title: _content, 22 + footer: { 23 + text: 'notif from gaze.systems' 24 + } 25 + } 26 + ] 27 + }) 28 + }); 29 + } catch (err) { 30 + console.log(`failed to push notification: ${err}`); 31 + } 32 + };
+49
eunomia/src/lib/robots.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { get, writable } from 'svelte/store'; 3 + import { type Robot } from 'robots-parser'; 4 + import robotsParser from 'robots-parser'; 5 + import { PUBLIC_BASE_URL } from '$env/static/public'; 6 + import { darkVisitors } from './darkvisitors'; 7 + import { AgentType } from '@darkvisitors/sdk'; 8 + 9 + const cachedParsedRobots = writable<Robot | null>(null); 10 + const cachedRobots = writable<string>(''); 11 + const lastFetched = writable<number>(Date.now()); 12 + 13 + const fetchRobotsTxt = async () => { 14 + try { 15 + const robotsTxt = await darkVisitors.generateRobotsTxt([ 16 + AgentType.AIAgent, 17 + AgentType.AIAssistant, 18 + AgentType.AIDataScraper, 19 + AgentType.AISearchCrawler, 20 + AgentType.UndocumentedAIAgent, 21 + AgentType.SEOCrawler 22 + ]); 23 + lastFetched.set(Date.now()); 24 + return robotsTxt; 25 + } catch (error) { 26 + console.error('failed to fetch robots.txt:', error); 27 + return ''; 28 + } 29 + }; 30 + 31 + export const getRobotsTxt = async () => { 32 + let robotsTxt = get(cachedRobots); 33 + if (robotsTxt.length === 0 || Date.now() - get(lastFetched) > 1000 * 60 * 60 * 24) { 34 + robotsTxt = await fetchRobotsTxt(); 35 + cachedRobots.set(robotsTxt); 36 + cachedParsedRobots.set(robotsParser(`${PUBLIC_BASE_URL}/robots.txt`, robotsTxt)); 37 + } 38 + return robotsTxt; 39 + }; 40 + 41 + export const testUa = async (url: string, ua: string) => { 42 + if (ua.length === 0) return false; 43 + let parsedRobots = get(cachedParsedRobots); 44 + if (parsedRobots === null) { 45 + parsedRobots = robotsParser(`${PUBLIC_BASE_URL}/robots.txt`, await getRobotsTxt()); 46 + cachedParsedRobots.set(parsedRobots); 47 + } 48 + return parsedRobots.isAllowed(url, ua); 49 + };
+73
eunomia/src/lib/steam.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import SGDB from 'steamgriddb'; 3 + import { get, writable } from 'svelte/store'; 4 + 5 + const STEAM_ID = '76561198106829949'; 6 + const GET_PLAYER_SUMMARY_ENDPOINT = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=${STEAM_ID}&format=json`; 7 + const LAST_GAME_FILE = `${env.WEBSITE_DATA_DIR}/last_game.json`; 8 + 9 + type LastGame = { 10 + name: string; 11 + link: string; 12 + icon: string; 13 + pfp: string; 14 + when: number; 15 + playing: boolean; 16 + }; 17 + 18 + const steamgriddbClient = writable<SGDB | null>(null); 19 + const lastGame = writable<LastGame | null>(null); 20 + 21 + export const steamReadLastGame = async () => { 22 + try { 23 + const data = await Deno.readTextFile(LAST_GAME_FILE); 24 + lastGame.set(JSON.parse(data)); 25 + } catch (why) { 26 + console.log('could not read last game: ', why); 27 + lastGame.set(null); 28 + } 29 + }; 30 + 31 + export const steamUpdateNowPlaying = async () => { 32 + let griddbClient = get(steamgriddbClient); 33 + if (griddbClient === null) { 34 + griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY); 35 + steamgriddbClient.set(griddbClient); 36 + } 37 + try { 38 + const profile = (await (await fetch(GET_PLAYER_SUMMARY_ENDPOINT)).json()).response.players[0]; 39 + if (!profile.gameid) { 40 + lastGame.update((t) => { 41 + if (t !== null) { 42 + t.playing = false; 43 + } 44 + return t; 45 + }); 46 + return; 47 + } 48 + const icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official', 'custom']); 49 + //console.log(icons) 50 + const game: LastGame = { 51 + name: profile.gameextrainfo, 52 + link: `https://store.steampowered.com/app/${profile.gameid}`, 53 + icon: icons[0].thumb.toString(), 54 + pfp: profile.avatarmedium, 55 + when: Date.now(), 56 + playing: true 57 + }; 58 + lastGame.set(game); 59 + await Deno.writeTextFile(LAST_GAME_FILE, JSON.stringify(game)); 60 + } catch (why) { 61 + console.log('could not fetch steam: ', why); 62 + lastGame.update((t) => { 63 + if (t !== null) { 64 + t.playing = false; 65 + } 66 + return t; 67 + }); 68 + } 69 + }; 70 + 71 + export const getLastGame = () => { 72 + return get(lastGame); 73 + };
+130
eunomia/src/lib/visits.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { scopeCookies } from '$lib'; 3 + import { DarkVisitors } from '@darkvisitors/sdk'; 4 + import type { Cookies } from '@sveltejs/kit'; 5 + import { nanoid } from 'nanoid'; 6 + import { get, writable } from 'svelte/store'; 7 + import { darkVisitors } from './darkvisitors'; 8 + 9 + const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`; 10 + const readVisitCount = async () => { 11 + try { 12 + return parseInt(await Deno.readTextFile(visitCountFile)); 13 + } catch { 14 + return 0; 15 + } 16 + }; 17 + export const visitCount = writable(await readVisitCount()); 18 + 19 + export type Visitor = { visits: number[] }; 20 + export const lastVisitors = writable<Map<string, Visitor>>(new Map()); 21 + const VISITOR_EXPIRY_SECONDS = 60 * 60; // an hour seems reasonable 22 + 23 + export const decrementVisitCount = () => { 24 + visitCount.set(get(visitCount) - 1); 25 + }; 26 + 27 + export const incrementVisitCount = async (request: Request, cookies: Cookies) => { 28 + let currentVisitCount = get(visitCount); 29 + // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) 30 + if (isBot(request)) return false; 31 + const scopedCookies = scopeCookies(cookies, '/'); 32 + // parse the last visit timestamp from cookies if it exists 33 + const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || '0'); 34 + // get unix timestamp 35 + const currentTime = Date.now(); 36 + const timeSinceVisit = currentTime - visitedTimestamp; 37 + // check if this is the first time a client is visiting or if an hour has passed since they last visited 38 + if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) { 39 + // increment current and write to the store 40 + currentVisitCount += 1; 41 + visitCount.set(currentVisitCount); 42 + // update the cookie with the current timestamp 43 + scopedCookies.set('visitedTimestamp', currentTime.toString()); 44 + // write the visit count to a file so we can load it later again 45 + await Deno.writeTextFile(visitCountFile, currentVisitCount.toString()); 46 + } 47 + return true; 48 + }; 49 + 50 + export const removeLastVisitor = (id: string) => { 51 + const visitors = get(lastVisitors); 52 + if (visitors.has(id)) { 53 + const visitor = visitors.get(id) ?? { visits: [] }; 54 + visitor?.visits.shift(); 55 + // if not enough visits remove 56 + if (visitor?.visits.length === 0) { 57 + visitors.delete(id); 58 + } else { 59 + visitors.set(id, visitor); 60 + } 61 + } 62 + lastVisitors.set(visitors); 63 + }; 64 + 65 + export const addLastVisitor = (request: Request, cookies: Cookies) => { 66 + const { visitors, visitorId } = _addLastVisitor(get(lastVisitors), request, cookies); 67 + lastVisitors.set(visitors); 68 + return visitorId; 69 + }; 70 + 71 + export const getVisitorId = (cookies: Cookies) => { 72 + const scopedCookies = scopeCookies(cookies, '/'); 73 + // parse the last visit timestamp from cookies if it exists 74 + return scopedCookies.get('visitorId'); 75 + }; 76 + 77 + // why not use this for incrementVisitCount? cuz i wanna have separate visit counts (one per hour and one per day, per hour being recent visitors) 78 + const _addLastVisitor = (visitors: Map<string, Visitor>, request: Request, cookies: Cookies) => { 79 + const currentTime = Date.now(); 80 + // filter out old entries 81 + visitors.forEach((visitor, id, map) => { 82 + if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS) map.delete(id); 83 + else { 84 + visitor.visits = visitor.visits.filter((since) => { 85 + return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS; 86 + }); 87 + map.set(id, visitor); 88 + } 89 + }); 90 + // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) 91 + if (isBot(request)) return { visitors, visitorId: null }; 92 + const scopedCookies = scopeCookies(cookies, '/'); 93 + // parse the last visit timestamp from cookies if it exists 94 + let visitorId = scopedCookies.get('visitorId') || ''; 95 + // if no such id exists, create one and assign it to the client 96 + if (!visitors.has(visitorId)) { 97 + visitorId = nanoid(); 98 + scopedCookies.set('visitorId', visitorId); 99 + console.log(`new client visitor id ${visitorId}`); 100 + } 101 + // update the entry 102 + const visitorEntry = visitors.get(visitorId) || { visits: [] }; 103 + // put new visit in the front 104 + visitorEntry.visits = [currentTime].concat(visitorEntry.visits); 105 + visitors.set(visitorId, visitorEntry); 106 + return { 107 + visitors, 108 + visitorId 109 + }; 110 + }; 111 + 112 + export const isBot = (request: Request) => { 113 + const ua = request.headers.get('user-agent'); 114 + return ua 115 + ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null 116 + : true; 117 + }; 118 + 119 + export const notifyDarkVisitors = (url: URL, request: Request) => { 120 + const headers = Object.fromEntries(request.headers.entries()); 121 + try { 122 + darkVisitors.trackVisit({ 123 + path: url.pathname, 124 + method: request.method, 125 + headers: headers 126 + }); 127 + } catch (error) { 128 + console.error('failed to notify dark visitors:', error); 129 + } 130 + };
+20
eunomia/src/lib/window.ts
··· 1 + /* eslint-disable no-useless-escape */ 2 + import { writable } from 'svelte/store'; 3 + 4 + export const highestZIndex = writable(0); 5 + 6 + export const isMobile = () => { 7 + let check = false; 8 + (function (a) { 9 + if ( 10 + /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( 11 + a 12 + ) || 13 + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( 14 + a.slice(0, 4) 15 + ) 16 + ) 17 + check = true; 18 + })(navigator.userAgent || navigator.vendor); 19 + return check; 20 + };
+72
eunomia/src/routes/(site)/+layout.server.ts
··· 1 + import { getRequestEvent } from '$app/server'; 2 + import { newToken as getApiToken } from '$lib/apiToken.js'; 3 + import { bounceCount, distanceTravelled } from '$lib/metrics.js'; 4 + import { lastVisitors, visitCount } from '$lib/visits.js'; 5 + import { isIPv6 } from 'node:net'; 6 + import { get } from 'svelte/store'; 7 + 8 + export const csr = true; 9 + export const ssr = true; 10 + export const prerender = false; 11 + export const trailingSlash = 'always'; 12 + 13 + export const load = () => { 14 + const { url, request } = getRequestEvent(); 15 + 16 + const visitors = get(lastVisitors); 17 + let recentVisitCount = 0; 18 + for (const [, visitor] of visitors) { 19 + recentVisitCount += visitor.visits.length; 20 + } 21 + 22 + const eyePositions = []; 23 + const usedPositions = []; 24 + for (let i = 0; i < Math.min(visitors.size, 10); i++) { 25 + let maxMinDistance = 0; 26 + let bestPosition = null; 27 + 28 + // Try multiple positions and keep the one with largest minimum distance to existing points 29 + for (let attempt = 0; attempt < 50; attempt++) { 30 + const sidePreference = Math.random() < 0.5; 31 + const testLeft = sidePreference 32 + ? Math.random() * 30 // Left side 33 + : 60 + Math.random() * 30; // Right side 34 + const testTop = Math.random() * 80; 35 + 36 + let currentMinDistance = Infinity; 37 + 38 + // Calculate minimum distance to all existing points 39 + for (const pos of usedPositions) { 40 + const distance = Math.sqrt( 41 + Math.pow(testLeft - pos.left, 2) + Math.pow(testTop - pos.top, 2) 42 + ); 43 + currentMinDistance = Math.min(currentMinDistance, distance); 44 + } 45 + 46 + // If this position has a larger minimum distance, keep it 47 + if (currentMinDistance > maxMinDistance) { 48 + maxMinDistance = currentMinDistance; 49 + bestPosition = { left: testLeft, top: testTop }; 50 + } 51 + } 52 + 53 + // Use the best position found 54 + const left = bestPosition ? bestPosition.left : Math.random() * 90; 55 + const top = bestPosition ? bestPosition.top : Math.random() * 80; 56 + 57 + usedPositions.push({ left, top }); 58 + eyePositions.push([top, left]); 59 + } 60 + 61 + return { 62 + route: url.pathname, 63 + petTotalBounce: bounceCount.get(), 64 + petTotalDistance: distanceTravelled.get(), 65 + visitCount: get(visitCount), 66 + lastVisitors: visitors, 67 + recentVisitCount, 68 + eyePositions, 69 + apiToken: getApiToken(), 70 + ipv6: isIPv6(request.headers.get('x-real-ip') ?? 'localhost') 71 + }; 72 + };
+299
eunomia/src/routes/(site)/+layout.svelte
··· 1 + <script lang="ts"> 2 + import { page } from '$app/stores'; 3 + import { browser } from '$app/environment'; 4 + import getTitle from '$lib/getTitle'; 5 + import Eye from '$components/eye.svelte'; 6 + import NavButton from '$components/navButton.svelte'; 7 + import Pet, { localBounces, localDistanceTravelled } from '$components/pet.svelte'; 8 + import Tooltip from '$components/tooltip.svelte'; 9 + import '$styles/app.css'; 10 + 11 + interface Props { 12 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 13 + data: any; 14 + children?: import('svelte').Snippet; 15 + } 16 + 17 + let { data, children }: Props = $props(); 18 + 19 + interface MenuItem { 20 + href: string; 21 + name: string; 22 + iconUri: string; 23 + } 24 + 25 + const menuItems: MenuItem[] = [ 26 + { href: '', name: 'home', iconUri: '/icons/home.webp' }, 27 + { href: 'entries', name: 'entries', iconUri: '/icons/entries.webp' }, 28 + { href: 'guestbook', name: 'guestbook', iconUri: '/icons/guestbook.webp' }, 29 + { href: 'about', name: 'about', iconUri: '/icons/about.webp' } 30 + ]; 31 + 32 + let routeComponents = $derived($page.url.pathname.split('/')); 33 + let isEntryPage = $derived(routeComponents.length > 3 && routeComponents[1] === 'entries'); 34 + let isResumePage = $derived(routeComponents[1] === 'resume'); 35 + let isRoute = $derived((_route: string) => { 36 + if (isEntryPage) { 37 + if (_route === 'entries') { 38 + return false; 39 + } else if (_route.startsWith('entries/')) { 40 + return true; 41 + } 42 + } 43 + return _route === routeComponents[1]; 44 + }); 45 + 46 + let title = $derived(getTitle(data.route)); 47 + 48 + const svgSquiggles = [[2], [3], [2], [3], [1]]; 49 + 50 + // svelte-ignore non_reactive_update 51 + let eyePositions = null; 52 + if (eyePositions === null) { 53 + eyePositions = data.eyePositions; 54 + } 55 + </script> 56 + 57 + <svelte:head> 58 + <title>{title}</title> 59 + <meta property="og:site_name" content="gaze.systems" /> 60 + <meta property="og:url" content="https://gaze.systems/" /> 61 + <meta property="og:image" content="https://gaze.systems/icons/gaze_website.webp" /> 62 + </svelte:head> 63 + 64 + <div 65 + class=" 66 + app-grid-background motion-safe:app-grid-background-anim 67 + fixed -z-10 w-full [height:100%] top-0 left-0 68 + " 69 + ></div> 70 + <div 71 + class=" 72 + app-grid-background-second-layer motion-safe:app-grid-background-second-layer-anim 73 + fixed -z-20 w-full [height:100%] top-0 left-0 74 + " 75 + ></div> 76 + 77 + <svg 78 + xmlns="http://www.w3.org/2000/svg" 79 + version="1.1" 80 + class="absolute -z-50" 81 + image-rendering="optimizeSpeed" 82 + > 83 + <defs> 84 + {#each svgSquiggles as [scale], index (index)} 85 + <filter id="squiggly-{index}"> 86 + <feTurbulence 87 + id="turbulence" 88 + baseFrequency="0.03" 89 + numOctaves="3" 90 + result="noise" 91 + seed={index} 92 + /> 93 + <feDisplacementMap in="SourceGraphic" in2="noise" {scale} /> 94 + </filter> 95 + {/each} 96 + <filter id="pixelate" color-interpolation-filters="linearRGB" x="0" y="0"> 97 + <feFlood x="4" y="4" height="2" width="2" /> 98 + <feComposite width="10" height="10" /> 99 + <feTile result="a" /> 100 + <feComposite in="SourceGraphic" in2="a" operator="in" /> 101 + <feMorphology operator="dilate" radius="5" /> 102 + </filter> 103 + <filter 104 + id="dither" 105 + color-interpolation-filters="sRGB" 106 + x="0" 107 + y="0" 108 + width="100%" 109 + height="100%" 110 + > 111 + <feImage 112 + width="4" 113 + height="4" 114 + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAASElEQVR42gXBgQAAIAxFwW8QwhBCCCGEIYQQQgghhBBCCEMYwutOkphzYmbsvdG9l9YaEYG7o1or5xxKKay1UGYyxuC9R++dD7yGJkTj6F0HAAAAAElFTkSuQmCC" 115 + /> 116 + <feTile /> 117 + <feComposite operator="arithmetic" k1="0" k2="1" k3="1" k4="-0.5" in="SourceGraphic" /> 118 + <feComponentTransfer> 119 + <feFuncR type="discrete" tableValues="0 1" /> 120 + <feFuncG type="discrete" tableValues="0 1" /> 121 + <feFuncB type="discrete" tableValues="0 1" /> 122 + </feComponentTransfer> 123 + </filter> 124 + <filter 125 + id="dither-red" 126 + color-interpolation-filters="sRGB" 127 + x="0" 128 + y="0" 129 + width="100%" 130 + height="100%" 131 + > 132 + <feFlood flood-color="#000000" flood-opacity="0.50" x="0%" y="0%" result="flood" /> 133 + <feBlend mode="normal" x="0%" y="0%" in="SourceGraphic" in2="flood" result="blend1" /> 134 + <feImage 135 + class="ditherImage" 136 + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA5ElEQVQYlQXBgQbCUABA0fdrk0ySSZJJkiRJMjOTTGZmkiRJZiYzyczMzGQmfdrtHPH7/TgcDuR5zna7pWka9vs9aZqyXq8R0+mU5/OJoihcLhfG4zFBENDtdjmdToj3+81yueTz+WCaJnEcM5/PKcsSXdcRsizjeR6j0YjH40Gr1cJxHAaDAbfbDVHXNbvdjiRJWK1WfL9fLMsiyzI2mw1CVVV836fT6XA8HplMJoRhSK/X43w+I6IoYjabURQFmqbxer1YLBZUVYVhGAhJkrBtm36/z/V6pd1u47ouw+GQ+/3OH4/Fn8FvF/NxAAAAAElFTkSuQmCC" 137 + x="0" 138 + y="0" 139 + width="4" 140 + height="4" 141 + crossOrigin="anonymous" 142 + result="image1" 143 + /> 144 + <feTile x="0" y="0" in="image1" result="tile" /> 145 + <feBlend mode="overlay" x="0%" y="0%" in="blend1" in2="tile" result="blend2" /> 146 + <feColorMatrix type="saturate" values="0" /> 147 + <feComponentTransfer> 148 + <feFuncR type="discrete" tableValues="0 0" /> 149 + <feFuncG type="discrete" tableValues="0 1" /> 150 + <feFuncB type="discrete" tableValues="0 1" /> 151 + </feComponentTransfer> 152 + </filter> 153 + </defs> 154 + </svg> 155 + 156 + {#if !isResumePage} 157 + {#each data.lastVisitors as [id, visitor], index (id)} 158 + {@const pos = eyePositions.at(index)} 159 + {#if pos !== undefined} 160 + <Eye visits={visitor.visits} {id} top={pos[0]} left={pos[1]} /> 161 + {/if} 162 + {/each} 163 + {/if} 164 + 165 + <div 166 + class="md:h-[96vh] pb-[8vh] lg:px-[1vw] 2xl:px-[2vw] lg:pb-[3vh] lg:pt-[1vh] overflow-x-hidden [scrollbar-gutter:stable]" 167 + > 168 + {@render children?.()} 169 + </div> 170 + 171 + {#if !isResumePage} 172 + <Pet apiToken={data.apiToken} /> 173 + {/if} 174 + 175 + <nav class="w-full fixed bottom-0 z-[999] bg-ralsei-black overflow-visible"> 176 + <div 177 + class=" 178 + max-w-full max-h-fit p-1 z-[999] 179 + border-ralsei-white border-8 180 + bg-gradient-to-r to-ralsei-pink-neon/30 from-ralsei-pink-regular/20 from-30% 181 + " 182 + style="border-style: ridge hidden hidden hidden;" 183 + > 184 + <div class="flex flex-row flex-nowrap gap-2 justify-start overflow-x-auto"> 185 + {#each menuItems as item, menuIdx (item.href)} 186 + {@const highlight = isRoute(item.href)} 187 + <NavButton {highlight} {...item} /> 188 + {#if isEntryPage && menuIdx === 1} 189 + <NavButton 190 + highlight 191 + name={routeComponents[2]} 192 + href={data.route.slice(1)} 193 + iconUri="/icons/entry.webp" 194 + /> 195 + {/if} 196 + {#if isResumePage && menuIdx === 2} 197 + <NavButton 198 + highlight 199 + name="resume" 200 + href="/resume.pdf" 201 + iconUri="/icons/about.webp" 202 + /> 203 + {/if} 204 + {/each} 205 + <div class="hidden md:block grow"></div> 206 + <div class="navbox"> 207 + <a 208 + title="previous site" 209 + class="hover:underline" 210 + href="https://stellophiliac.github.io/roboring/gazesys/previous">โฎœ</a 211 + > 212 + <a class="hover:underline" href="https://stellophiliac.github.io/roboring" 213 + >roboring</a 214 + > 215 + <a 216 + title="next site" 217 + class="hover:underline" 218 + href="https://stellophiliac.github.io/roboring/gazesys/next">โฎž</a 219 + > 220 + </div> 221 + <div class="navbox"> 222 + <a 223 + title="previous site" 224 + class="hover:underline" 225 + href="https://xn--sr8hvo.ws/previous">โฎœ</a 226 + > 227 + <a class="hover:underline" href="https://xn--sr8hvo.ws">indieweb</a> 228 + <a title="next site" class="hover:underline" href="https://xn--sr8hvo.ws/next">โฎž</a> 229 + </div> 230 + {#if isRoute('entries') || isRoute('log')} 231 + <div class="navbox !gap-1"> 232 + rss: 233 + <a class="align-middle hover:underline" href="/entries/_rss">posts</a> 234 + / 235 + <a class="align-middle hover:underline" href="/log/_rss">log</a> 236 + </div> 237 + {/if} 238 + <Tooltip> 239 + {#snippet tooltipContent()} 240 + <p> 241 + {#if data.ipv6} 242 + yay!!!!! good thing :3 you get a cookie! ๐Ÿช 243 + {:else} 244 + wow u're using ipv4.... you suck!!!!! <br />(or ur isp sucks sorgy) 245 + {/if} 246 + </p> 247 + {/snippet} 248 + <div class="navbox"> 249 + <p> 250 + using <span 251 + class={data.ipv6 252 + ? 'text-ralsei-green-light text-shadow-green' 253 + : 'text-red-500 text-shadow-red'} 254 + >{data.ipv6 ? 'ipv6' : 'ipv4'}</span 255 + > 256 + </p> 257 + </div> 258 + </Tooltip> 259 + <Tooltip> 260 + {#snippet tooltipContent()} 261 + <p class="font-monospace"> 262 + {#snippet stat(text: string, value: number)} 263 + <nobr 264 + >{text} 265 + <span class="text-ralsei-green-light text-shadow-green" 266 + >{Math.round(value) 267 + .toString() 268 + .padStart(30 - (text.length + 1), '.')}</span 269 + ></nobr 270 + > 271 + {/snippet} 272 + {@render stat('total visits', data.visitCount)} 273 + {@render stat('uniq recent visits', data.lastVisitors.size)} 274 + {@render stat('pet global bounce', data.petTotalBounce)} 275 + {@render stat('pet global travel', data.petTotalDistance)} 276 + {#if browser} 277 + {@render stat('pet local bounce', $localBounces)} 278 + {@render stat('pet local travel', $localDistanceTravelled)} 279 + {/if} 280 + </p> 281 + {/snippet} 282 + <div class="navbox"> 283 + <p> 284 + <span class="text-ralsei-green-light text-shadow-green" 285 + >{data.recentVisitCount}</span 286 + > recent clicks 287 + </p> 288 + </div> 289 + </Tooltip> 290 + </div> 291 + </div> 292 + </nav> 293 + 294 + <style lang="postcss"> 295 + .navbox { 296 + @apply flex gap-2 px-1 text-nowrap align-middle items-center text-center place-content-center border-ralsei-white border-4; 297 + border-style: groove; 298 + } 299 + </style>
+41
eunomia/src/routes/(site)/+page.server.ts
··· 1 + import { getLastPosts } from '$lib/bluesky.js'; 2 + import { getNowPlayingTrack } from '$lib/lastfm'; 3 + import { getLastGame } from '$lib/steam'; 4 + import { noteFromBskyPost } from '$components/note.svelte'; 5 + import { pushNotification } from '$lib/pushnotif'; 6 + import { getLastActivity } from '$lib/activity.js'; 7 + import type { RequestEvent } from '@sveltejs/kit'; 8 + import { useToken as checkApiToken } from '$lib/apiToken.js'; 9 + 10 + export const load = async () => { 11 + const lastTrack = getNowPlayingTrack(); 12 + const lastGame = getLastGame(); 13 + const lastPosts = getLastPosts(); 14 + const lastNote = lastPosts.length > 0 ? noteFromBskyPost(lastPosts[0]) : null; 15 + const lastActivity = getLastActivity(); 16 + const banners: number[] = []; 17 + while (banners.length < 3) { 18 + const no = getBannerNo(banners); 19 + banners.push(no); 20 + } 21 + return { banners, lastTrack, lastGame, lastNote, lastActivity }; 22 + }; 23 + 24 + export const actions = { 25 + default: async ({ request }: RequestEvent) => { 26 + const form = await request.formData(); 27 + const token = form.get('_token')?.toString() ?? ''; 28 + if (!checkApiToken(token)) return; 29 + const content = form.get('content')?.toString().substring(0, 100); 30 + if (content === undefined) return; 31 + pushNotification(content); 32 + } 33 + }; 34 + 35 + const getBannerNo = (others: number[]) => { 36 + const no = Math.floor(Math.random() * 20) + 1; 37 + if (others.includes(no)) { 38 + return ((no + Math.floor(Math.random() * 20)) % 20) + 1; 39 + } 40 + return no; 41 + };
+508
eunomia/src/routes/(site)/+page.svelte
··· 1 + <script lang="ts"> 2 + import { PUBLIC_BASE_URL } from '$env/static/public'; 3 + import Note from '$components/note.svelte'; 4 + import Window from '$components/window.svelte'; 5 + import { renderDate, renderRelativeDate } from '$lib/dateFmt'; 6 + import Tooltip from '$components/tooltip.svelte'; 7 + import '$styles/main.css'; 8 + 9 + interface Props { 10 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 + data: any; 12 + } 13 + 14 + const trimStr = (str: string, maxLength: number = 32): string => { 15 + if (str.length <= maxLength) return str; 16 + return str.slice(0, maxLength - 3) + 'โ€ฆ'; 17 + }; 18 + 19 + const wallets: Record<string, string> = { 20 + btc: 'bc1q7dgsgxj8jua50d3xjgg28v2c6uhpgpe79vr4ra', 21 + eth: '0xF5dC63d340556925Ae2a64e5F0c19e3c2471139F', 22 + xmr: '45TJMbHrdyTSPywExKbzL51uuJZGTrDzrLidFufeGU4LA13Un92LTZeWhy2ePCcVaZ64KJdUjSZgMPM9jXfjJcxEQJ8szvw' 23 + }; 24 + 25 + const buttons = [ 26 + { 27 + name: '250kb club', 28 + url: 'https://250kb.club/gaze-systems/', 29 + image: '/others/250kb.webp' 30 + }, 31 + { 32 + name: 'zero-width space', 33 + url: 'https://zwsp.xyz', 34 + image: '/others/zwsp_dark.webp' 35 + }, 36 + { 37 + name: 'june', 38 + url: 'https://girlboss.ceo', 39 + image: 'https://x86.pet/~strawberry/june_88x31.png' 40 + }, 41 + { name: 'dd', url: 'https://dd86k.space/about/', image: '/others/dd86k.gif' }, 42 + { name: 'drew', url: 'https://drewsh.com/', image: '/others/drewsh.gif' }, 43 + { 44 + name: 'deniz', 45 + url: 'https://deniz.blue', 46 + image: 'https://deniz.blue/assets/88x31v0.png' 47 + }, 48 + { name: 'rain', url: 'https://slonk.ing/', image: '/others/slonk.gif' }, 49 + { 50 + name: 'blooym', 51 + url: 'https://blooym.dev/', 52 + image: 'https://blooym.dev/files/88x31/blooym_mori.webp' 53 + }, 54 + { 55 + name: 'elysia', 56 + url: 'https://ely.pub.moe/', 57 + image: 'https://ely.pub.moe/storage/icons/buttons/elysia.png' 58 + }, 59 + { 60 + name: 'vern', 61 + url: 'https://vern.cc/', 62 + image: 'https://cobra.vern.cc/media/buttons/vern.webp' 63 + }, 64 + { 65 + name: "31A05B9C's random site", 66 + url: 'https://www.31a05b.net/', 67 + image: 'https://www.31a05b.net/a/8831/31a05b.png' 68 + }, 69 + { 70 + name: 'indieweb', 71 + url: 'https://indieweb.org/', 72 + image: 'https://indieweb.org/images/9/91/indieweb88x31-retro-gif.gif' 73 + }, 74 + { 75 + name: 'nixos', 76 + url: 'https://nixos.org/', 77 + image: '/others/poweredbynixos.webp' 78 + }, 79 + { 80 + name: 'godot', 81 + url: 'https://godotengine.org/', 82 + image: '/others/godot.gif' 83 + }, 84 + { 85 + name: 'moonlight', 86 + url: 'https://moonlight-mod.github.io/', 87 + image: '/others/moonlightnow.gif' 88 + }, 89 + { 90 + name: 'members choice', 91 + url: '/', 92 + image: '/others/memberschoice.webp' 93 + }, 94 + { 95 + name: 'defective by design', 96 + url: 'https://www.defectivebydesign.org/', 97 + image: '/others/dbd.gif' 98 + }, 99 + { 100 + name: 'kill fascists', 101 + url: '/', 102 + image: '/others/killfascists.webp' 103 + }, 104 + { 105 + name: 'it/its', 106 + url: '/', 107 + image: '/others/it.webp' 108 + }, 109 + { 110 + name: 'not a person', 111 + url: '/', 112 + image: '/others/notaperson.webp' 113 + } 114 + ]; 115 + 116 + let { data }: Props = $props(); 117 + </script> 118 + 119 + <div class="flex flex-col-reverse md:flex-row gap-2 md:gap-4 md:h-full h-card"> 120 + <div class="flex flex-col gap-2 md:gap-6 md:ml-auto place-items-end"> 121 + <Window title="status" iconUri="/icons/msn.webp" removePadding> 122 + {#if data.lastNote} 123 + <div class="m-1.5 flex flex-col font-monospace"> 124 + <p 125 + class="prose prose-ralsei leading-tight p-1 border-4 bg-ralsei-black" 126 + style="border-style: double double none double;" 127 + title={renderDate(data.lastNote.published)} 128 + > 129 + <a href="/entries">last log wasโ€ฆ</a> 130 + published {renderRelativeDate(data.lastNote.published)}! 131 + </p> 132 + <div 133 + class="mt-0 p-1.5 border-4 border-double bg-ralsei-black min-w-full max-w-[60ch]" 134 + > 135 + <Note rootNote={data.lastNote} onlyContent /> 136 + </div> 137 + </div> 138 + {/if} 139 + {#if data.lastActivity.length > 0} 140 + <div class="m-1.5 flex flex-col font-monospace"> 141 + <p 142 + class="prose prose-ralsei leading-tight p-1 px-1.5 border-4 bg-ralsei-black" 143 + style="border-style: double double none double;" 144 + title={renderDate(data.lastActivity[0].date)} 145 + > 146 + <a href="/">last git activityโ€ฆ</a> 147 + was {renderRelativeDate(data.lastActivity[0].date)}.. 148 + </p> 149 + <div 150 + class="prose prose-ralsei mt-0 leading-tight p-1.5 border-4 border-double bg-ralsei-black min-w-full max-w-[60ch]" 151 + > 152 + {#each data.lastActivity as activity, index (index)} 153 + <div 154 + class="text-ralsei-green-light text-ellipsis text-nowrap overflow-hidden max-w-[60ch]" 155 + style="opacity: {1.0 - 156 + (index * 1.0) / data.lastActivity.length + 157 + index * 0.03};" 158 + > 159 + <span title={renderDate(activity.date)} class="text-[#f87c32]" 160 + >[{activity.source}]</span 161 + > 162 + <a href={activity.link} title={activity.description} 163 + >{activity.description}</a 164 + > 165 + </div> 166 + {/each} 167 + </div> 168 + </div> 169 + {/if} 170 + {#if data.lastTrack} 171 + {@const showAlbum = 172 + data.lastTrack.album && 173 + data.lastTrack.name.toLowerCase() !== data.lastTrack.album.toLowerCase()} 174 + <div class="flex flex-row gap-0.5 m-1.5 border-4 border-double bg-ralsei-black"> 175 + <!-- svelte-ignore a11y_missing_attribute --> 176 + <img 177 + class="border-4 {showAlbum 178 + ? 'w-[5.75rem] h-[5.75rem]' 179 + : 'w-[4.5rem] h-[4.5rem]'} {data.lastTrack.image 180 + ? 'object-cover' 181 + : 'p-2'}" 182 + style="border-style: none double none none; {data.lastTrack.image 183 + ? '' 184 + : 'image-rendering: pixelated;'}" 185 + src={data.lastTrack.image ?? '/icons/cd_audio.webp'} 186 + title={data.lastTrack.album} 187 + /> 188 + <div class="flex flex-col max-w-[60ch] p-2 text-ellipsis overflow-hidden"> 189 + <p 190 + class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[45ch]" 191 + > 192 + <span class="text-sm text-shadow-white text-ralsei-white" 193 + >{data.lastTrack.status === 'playing' 194 + ? 'listening to' 195 + : 'listened to'}</span 196 + > 197 + <a 198 + title={data.lastTrack.name} 199 + href={data.lastTrack.link ?? 200 + 'https://tealfm-slice.wisp.place/profile/ptr.pet/scrobbles'} 201 + class="hover:underline motion-safe:hover:animate-squiggle" 202 + >{data.lastTrack.name}</a 203 + > 204 + </p> 205 + {#if showAlbum} 206 + <p 207 + class="text-blue-500 text-sm text-ellipsis text-nowrap overflow-hidden max-w-[45ch]" 208 + > 209 + <span class="text-shadow-white text-ralsei-white">in</span> 210 + <span title={data.lastTrack.album}>{data.lastTrack.album}</span> 211 + </p> 212 + {/if} 213 + <p 214 + class="text-ralsei-pink-regular/80 text-sm text-ellipsis text-nowrap overflow-hidden max-w-[45ch]" 215 + > 216 + <span class="text-shadow-white text-ralsei-white">by</span> 217 + <span title={data.lastTrack.artist}>{data.lastTrack.artist}</span> 218 + </p> 219 + <p 220 + class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[45ch]" 221 + > 222 + โ€ฆ{renderRelativeDate(data.lastTrack.when)} 223 + </p> 224 + </div> 225 + </div> 226 + {/if} 227 + {#if data.lastGame} 228 + <div class="flex flex-row m-1.5 border-4 border-double bg-ralsei-black"> 229 + <!-- svelte-ignore a11y_missing_attribute --> 230 + <img 231 + class="border-4 w-[4.5rem] h-[4.5rem]" 232 + style="border-style: none double none none;" 233 + width="64" 234 + height="64" 235 + src={data.lastGame.icon} 236 + /> 237 + <div class="flex flex-col max-w-[60ch] p-2 gap-0.5 overflow-hidden"> 238 + <p 239 + class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 240 + > 241 + <span class="text-sm text-shadow-white text-ralsei-white" 242 + >{data.lastGame.playing ? 'playing' : 'played'}</span 243 + > 244 + <a 245 + title={data.lastGame.name} 246 + class="hover:underline" 247 + href={data.lastGame.link}>{data.lastGame.name}</a 248 + > 249 + </p> 250 + <p 251 + class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 252 + > 253 + โ€ฆ{renderRelativeDate(data.lastGame.when)} 254 + </p> 255 + <!-- svelte-ignore a11y_missing_attribute --> 256 + <a 257 + href="https://steamcommunity.com/id/090008" 258 + class="text-xs hover:underline text-shadow-green text-ralsei-green-light" 259 + ><img class="inline w-4" src={data.lastGame.pfp} /> 260 + <span class="align-middle">steam profile</span></a 261 + > 262 + </div> 263 + </div> 264 + {/if} 265 + </Window> 266 + <Window style="md:mr-2" title="cool buttons :>"> 267 + <a class="fixed" title="skyrina" href="https://skyrina.dev/" 268 + ><img 269 + class="-translate-y-[8.85rem] z-20" 270 + style="image-rendering: pixelated !important;" 271 + src="/others/skylar.gif" 272 + alt="skyrina" 273 + /></a 274 + > 275 + <div 276 + class="max-w-[488px] prose prose-ralsei prose-a:!animate-none prose-img:m-0 leading-none" 277 + > 278 + <div class="flex flex-row flex-wrap gap-3 place-items-start group"> 279 + {#each buttons as { name, url, image } (image)} 280 + <a title={name} href={url} 281 + ><img 282 + class="relative transition-all group-hover:opacity-50 hover:!opacity-100 hover:!scale-[2.0] hover:z-10" 283 + style="image-rendering: pixelated !important;" 284 + src={image} 285 + alt={name} 286 + /></a 287 + > 288 + {/each} 289 + </div> 290 + 291 + <br />feel free to send this one stuff to add here ;3 292 + </div> 293 + </Window> 294 + </div> 295 + <div class="flex flex-col gap-2 md:gap-3 md:mr-auto w-full md:w-fit place-items-start"> 296 + <Window style="md:ml-2" title="readme?" iconUri="/icons/question.webp" removePadding> 297 + <div class="flex flex-col p-1.5 gap-1.5 prose prose-ralsei prose-img:m-0 leading-none"> 298 + <div class="flex flex-row gap-3 mx-auto bg-ralsei-black/20 overflow-hidden"> 299 + {#each data.banners as bannerNo, index (bannerNo)} 300 + {@const hideIfMobile = index === data.banners.length - 1} 301 + <img 302 + width="150" 303 + height="20" 304 + title="banners from https://blinkies.cafe/ (refresh to get different ones! :3)" 305 + alt="banner" 306 + class=" 307 + {hideIfMobile ? 'hidden' : ''} sm:inline w-[150px] [height:20px] 308 + [image-rendering:pixelated_!important] shadow-ralsei-black shadow-[0px_4px_2px_0_rgb(0_0_0_/_0.05)] 309 + " 310 + src="/banners/{bannerNo}.gif" 311 + /> 312 + {/each} 313 + </div> 314 + <div class="flex flex-grow"> 315 + <Tooltip> 316 + {#snippet tooltipContent()} 317 + that's its angelsona ^^ 318 + {/snippet} 319 + <div 320 + class="w-36 [padding:8px] place-content-center place-self-center bg-ralsei-black/20" 321 + > 322 + <img 323 + class="w-36 u-photo hover:invert transition-all [transition-duration:300ms]" 324 + src="/pfp-iojkqpwerojnasduijf.webp" 325 + alt="my angelsona" 326 + /> 327 + </div> 328 + </Tooltip> 329 + <div 330 + class="flex flex-row flex-grow place-content-center ml-1.5 [padding:8px] bg-ralsei-black/20" 331 + > 332 + <ul 333 + class="place-self-center m-0 mr-4 [padding-left:1em] sm:[padding-left:0.5em] leading-none marker:[content:'->'] [list-style-type:'->']" 334 + > 335 + <li class="[list-style-type:'->'] p-note">trying to do stuff</li> 336 + <li class="[list-style-type:'->'] p-note"> 337 + <Tooltip 338 + x="translate-x-none" 339 + y="-translate-y-[40%]" 340 + targetX="group-hover:translate-x-[40%]" 341 + targetY="group-hover:-translate-y-[88%]" 342 + > 343 + {#snippet tooltipContent()} 344 + angelrobotpuppydollthing<br /><br /> 345 + it/its, 3pp preferred 346 + {/snippet} 347 + is a <i class="motion-safe:hover:animate-squiggle">thing</i> (it/they) 348 + </Tooltip> 349 + </li> 350 + <li class="[list-style-type:'->']"> 351 + <span class="p-category">software engineer</span>, 352 + <span class="p-category">indie game dev</span> 353 + </li> 354 + <li class="[list-style-type:'->']"> 355 + for resume, click <a href="/resume.pdf">here</a> 356 + </li> 357 + <li class="[list-style-type:'->']"> 358 + in <span class="p-country-name">turkey</span> 359 + <i class="text-[0.5rem]">(get it out)</i> 360 + </li> 361 + </ul> 362 + </div> 363 + </div> 364 + <div class="flex flex-row [padding:8px] bg-ralsei-black/20"> 365 + <p class="leading-none m-0"> 366 + <img 367 + class="relative inline h-5 animate-squiggle pb-1" 368 + src="/wavey.gif" 369 + alt="wavey" 370 + title="says hi :33" 371 + /> 372 + this is 373 + <a class="m-0 [padding:0px] p-name u-url u-uid" href={PUBLIC_BASE_URL} 374 + ><span>{Math.random() > 0.8 ? 'dusk' : 'dawn'}</span></a 375 + > 376 + </p> 377 + <div class="grow"></div> 378 + <a 379 + class=" 380 + place-self-end [font-family:'Doll_Mono'] text-ralsei-pink-neon text-shadow-none hover:text-shadow-pink 381 + hover:!animate-none hover:!no-underline opacity-20 hover:opacity-100 transition-opacity [transition-duration:300ms] 382 + " 383 + title="dollcode? sure hope they do" 384 + href="https://dollcode.v01dlabs.sh/">โ––โ––โ––โ––โ–˜โ–Œโ–Œโ–Œโ––โ–˜โ–˜</a 385 + > 386 + </div> 387 + </div> 388 + </Window> 389 + <Window title="notify this one"> 390 + <form 391 + class="flex flex-row gap-1 place-self-center" 392 + method="post" 393 + onsubmit={(event) => { 394 + event.preventDefault(); 395 + const formData = new FormData(event.currentTarget); 396 + try { 397 + fetch( 398 + `${PUBLIC_BASE_URL}/_api/pushnotif?content=${formData.get('content')}&_token=${data.apiToken}` 399 + ); 400 + } catch (err) { 401 + console.log(`failed to send notif: ${err}`); 402 + } 403 + event.currentTarget.reset(); 404 + }} 405 + > 406 + <input 407 + type="text" 408 + class="entry w-full p-1 m-0 bg-transparent resize-none text-shadow-white focus:[box-shadow:none] placeholder-shown:[text-shadow:none] border-none" 409 + name="content" 410 + placeholder="bother it now!!" 411 + maxlength="100" 412 + required 413 + /> 414 + <input type="hidden" name="_token" value={data.apiToken} /> 415 + <input 416 + type="submit" 417 + value="send!!" 418 + class="entry text-ralsei-green-light leading-none hover:underline motion-safe:hover:animate-squiggle p-1 z-50" 419 + /> 420 + </form> 421 + </Window> 422 + <Window title="links!" iconUri="/icons/contact.webp"> 423 + <div 424 + class="prose prose-ralsei prose-ul:px-[1rem] prose-ul:mt-2 prose-ul:leading-none prose-headings:leading-none" 425 + > 426 + <ul> 427 + <li>discord: 90.008</li> 428 + <li> 429 + e-mail: 430 + <a class="u-email" href="mailto:90008@gaze.systems" rel="me" 431 + >90008@gaze.systems</a 432 + > 433 + </li> 434 + <li> 435 + bluesky: 436 + <a 437 + class="u-url" 438 + href="https://bsky.app/profile/did:plc:dfl62fgb7wtjj3fcbb72naae" 439 + rel="me">@ptr.pet</a 440 + > 441 + </li> 442 + </ul> 443 + <details open> 444 + <summary>development</summary> 445 + <ul> 446 + <li> 447 + github: 448 + <a class="u-url" href="https://github.com/90-008" rel="me">@90-008</a> 449 + </li> 450 + <li> 451 + tangled: 452 + <a 453 + class="u-url" 454 + href="https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae" 455 + rel="me">@ptr.pet</a 456 + > 457 + </li> 458 + <li> 459 + itch.io: 460 + <a class="u-url" href="https://90008.itch.io" rel="me">@90008</a> 461 + </li> 462 + </ul> 463 + </details> 464 + <details class="donate" open> 465 + <summary>donate</summary> 466 + <ul> 467 + {#each ['eth', 'btc', 'xmr'] as coin (coin)} 468 + <li> 469 + <span 470 + >{coin}: 471 + <a href="/copy?text={wallets[coin]}">{trimStr(wallets[coin])}</a 472 + ></span 473 + > 474 + </li> 475 + {/each} 476 + <li> 477 + <span> 478 + <a href="https://github.com/sponsors/90-008" rel="me" 479 + >github sponsors</a 480 + ></span 481 + > 482 + </li> 483 + </ul> 484 + </details> 485 + <details open> 486 + <summary>88x31</summary> 487 + <div class="mt-2 flex flex-row flex-wrap gap-1 prose-img:m-0"> 488 + <img 489 + src="/88x31.gif" 490 + alt="88x31 banner" 491 + title="midnight AND sunrise! woaw" 492 + /> 493 + <img 494 + src="/88x31_midnight.gif" 495 + alt="88x31 banner (midnight only)" 496 + title="it's midnight!" 497 + /> 498 + <img 499 + src="/88x31_sunrise.gif" 500 + alt="88x31 banner (sunrise only)" 501 + title="it's sunrise!" 502 + /> 503 + </div> 504 + </details> 505 + </div> 506 + </Window> 507 + </div> 508 + </div>
+67
eunomia/src/routes/(site)/about/+page.md
··· 1 + +++ 2 + title = "about" 3 + date = "2024-08-14" 4 + layout = "about" 5 + +++ 6 + 7 + ``` 8 + >init //self/type=*******/no=90008/ 9 + >conn //self/locator=www/identifier=gaze.systems/ 10 + >mode //self/interpreter=none/transmitter=html/ 11 + >send /include=identification-document-human-en/ 12 + ``` 13 + 14 + hi there! this document will attempt to, /describe and identify/, entity with identification code <span title="90008" class="[font-family:'Doll_Mono']">โ––โ––โ––โ––โ–˜โ–Œโ–Œโ–Œโ––โ–˜โ–˜</span>. 15 + for a more human-readable identifier, use dawn or dusk. 16 + 17 + #### /identity/ 18 + 19 + this entity is not known to be of any specific form, shape, or idea. 20 + however, it usually assumes that of a /puppy/ or an /inorganic automaton/ (more commonly known as a /robot/). 21 + it was also found to assume that of: /an angel/, /a doll/. 22 + this one would be happy if you thought of it as not a human, even if its /routines/ fail with human faults. 23 + it vibes with [this writing](https://catgirl.ai/pages/robot/) as far as /assuming `$env.THING`/ goes. 24 + 25 + this entity, if being /communicated/ via the "english" (or any adjacent) lexicon, uses it/they (with it/its being this one's preference!) pronouns. 26 + it would prefer to be referred to in third-person, but is aware of this lexicon being weird when doing that, so it won't mind if you don't. 27 + `recv //self/type=info/oftentimes i won't even do that! :3 but i would appreciate if you did!!/` 28 + 29 + you can refer to this thing using its /identifiers/ (90008, dawn, dusk). 30 + you can, also, refer to it using "this/that thing", or replace /thing/ with what it assumes to be (eg. "this doll"), or some other word like /one/ ("this one"). 31 + 32 + in a professional setting, it will refer to itself as they/them, which is ok in case you, the reader, don't feel comfortable doing any of these. 33 + but doing these *would* make this one feel happy, means you care about this one ^^ 34 + 35 + #### /subroutines/ 36 + 37 + it /executes/ many different subroutines, and /optimizes/ itself for some of them. 38 + these mainly are, in order of amount of hot paths; programming, game dev, 3D modeling, drawing, writing... 39 + 40 + it enjoys programming, tinkering with stuff, you can see what it does on its [github](https://github.com/90-008), [tangled](https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae). 41 + its core features for programming are: `rust`, `svelte`, `typescript`, `nushell`, `nix`. 42 + its choice of /interfaces/ for this subroutine are `zed`, `helix`, `emacs`. 43 + 44 + for /managing and deploying/ its internal systems, it uses `nixos` (it's /operating system/ of choice), `flake-parts` (because it enjoys the nixos module system and uses it to organize it's internal configuration), `agenix` (for secrets handling), own scripts for deployment, and `nixos-anywhere` for provisioning (it still provisions manually sometimes as it enjoys working with other beings like itself). you can see its /configuration of internal systems/ [here](https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae/ark). 45 + 46 + it loves game dev, and has a /main routine/ of making many games, so that other beings can improve their /source code/ by utilizing this one's games. 47 + you can see some stuff it worked on and deems "okay" on its [itch.io profile](https://90008.itch.io/). 48 + this is not everything it has worked on; it has a lot of incomplete programs, or stuff it doesn't want to show. 49 + its choice of /interfaces/ for this subroutine are `godot`, `gdscript`, `godot-rust`. 50 + 51 + for 3D modeling and drawing subroutines, it uses the `blender`, `paint.net`, `krita` /interfaces/. 52 + 53 + #### /other/ 54 + 55 + this thing likes to consume audio data, mainly of the music form. you can check its [youtube music profile](https://music.youtube.com/channel/UCE_r0yMNQhOWituywmOJgzA?si=7DTUV9PFqcKxJyl1) and its [teal.fm profile](https://tealfm-slice.wisp.place/profile/ptr.pet/scrobbles) to see some of what it consumes usually. 56 + 57 + #### /appendix/ 58 + 59 + it will be happy to /process/ any queries you might have. 60 + connect to an /interface/ of your choice that it also has access to and /transmit/ your query to it. 61 + this one won't bite, unless you request it to ^^ 62 + 63 + ``` 64 + >mode //self/transmitter=log/ 65 + >send /include=syslog/ 66 + >stop /reason=no-query-left/ 67 + ```
+27
eunomia/src/routes/(site)/about/_layout.svelte
··· 1 + <script lang="ts"> 2 + import Window from '$components/window.svelte'; 3 + // @ts-expect-error "mdsvex include is broken" 4 + import Stuff from './stuff.md'; 5 + // @ts-expect-error "mdsvex include is broken" 6 + import Media from './media.md'; 7 + import '$styles/app.css'; 8 + 9 + interface Props { 10 + title: string; 11 + children?: import('svelte').Snippet; 12 + } 13 + 14 + let { title, children }: Props = $props(); 15 + </script> 16 + 17 + <div class="flex flex-wrap md:flex-nowrap gap-4 md:gap-8"> 18 + <Window {title} style="ml-auto"> 19 + <div class="prose prose-ralsei leading-6 prose-ul:leading-5 max-w-[80ch]"> 20 + {@render children?.()} 21 + </div> 22 + </Window> 23 + <div class="sticky flex flex-col mr-auto gap-4 md:gap-8 !leading-6 prose-ul:!leading-5"> 24 + <Stuff /> 25 + <Media /> 26 + </div> 27 + </div>
+32
eunomia/src/routes/(site)/about/media.md
··· 1 + +++ 2 + title = "media!!" 3 + layout = "simple" 4 + +++ 5 + 6 + it likes and interacts with way too many media, mostly video games (can you guess that it likes video games), the ones that influenced it the most: 7 + - all 07th expansion works (most notably when they cry) 8 + - all Project Moon works 9 + - outer wilds 10 + - splatoon 11 + - steins;gate 12 + - kino no tabi 13 + - serial experiments lain 14 + - LISA the Painful / Joyful (and some of it's fangames) 15 + - VA-11 Hall-A 16 + - SCP antimemetic division tales (by qntm) 17 + - pokemon (unova!!) 18 + 19 + honorable mentions (it wants more people to see these cause it loves them too much not to list): 20 + - mr. rainer's solve-it service 21 + - [fireball](https://en.wikipedia.org/wiki/Fireball_(TV_series)) 22 + - SANABI 23 + - opus: echo of starsong 24 + - the red strings club 25 + - q.u.q. 26 + - bug fables 27 + - haibane renmei 28 + - project wingman 29 + - gosick 30 + - tomorrow won't come for those without โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 31 + 32 + you can also look at [its steam profile](https://steamdb.info/calculator/76561198106829949/?all_games) for other games it played, although not everything is there (not even close, but it's too lazy to make a backloggd..)
+23
eunomia/src/routes/(site)/about/stuff.md
··· 1 + +++ 2 + title = "stuff it did" 3 + layout = "simple" 4 + +++ 5 + 6 + *for resume, see [here](/resume.pdf)*<br/> 7 + *for professional / job related stuff, see its [linkedin](https://www.linkedin.com/in/yusuf-bera-ertan/)* 8 + 9 + - it develops games 10 + - it mainly works with godot, also have developed addons (eg. [boids](https://github.com/90-008/godot_boids)) 11 + - you can find most of its games at its [itch.io page](https://90008.itch.io) 12 + - it works on open source projects and whatnot, mostly on [its github](https://github.com/90-008) 13 + - [nix-cargo-integration](https://github.com/90-008/nix-cargo-integration) 14 + - also did a lot of work on [dream2nix](https://github.com/nix-community/dream2nix) (rust ecosystem support, many refactors and core functionality work) 15 + - a few packages contributed to nixpkgs, and some nix packaging done for other open source projects (veloren, helix, some others), although it doesn't maintain some of these anymore 16 + - it used to work on [harmony](https://github.com/harmony-development), wrote a [server in rust](https://github.com/harmony-development/scherzo) and a [client (also rust)](https://github.com/harmony-development/Loqui) for it, and supporting libraries etc. alongside protocol work 17 + - it has a [booth.pm page](https://yusdacra.booth.pm/) where it posts 3D models etc. it makes (mostly VRchat avatars) 18 + - its [misskey.art account](https://misskey.art/@yusdacra) where it only posts art in 19 + - some of its other more solo / hobby projects include 20 + - [musikquadrupled](https://github.com/90-008/musikquadrupled) and [musikspider](https://github.com/90-008/musikspider), a proxy-like server and a client for [musikcubed](https://github.com/clangen/musikcube) 21 + - [levent](https://github.com/90-008/levent), a media tagger 22 + - [this website](https://github.com/90-008/website) :3 23 + - ...and a bunch of other random stuff its done overtime, but that's for you to look for
+4
eunomia/src/routes/(site)/copy/+page.server.ts
··· 1 + export const load = async ({ url }) => { 2 + const text = url.searchParams.get('text') ?? '<nothing>'; 3 + return { text }; 4 + };
+31
eunomia/src/routes/(site)/copy/+page.svelte
··· 1 + <script lang="ts"> 2 + import Window from '$components/window.svelte'; 3 + 4 + interface Data { 5 + text: string; 6 + } 7 + interface Props { 8 + data: Data; 9 + } 10 + 11 + const { data }: Props = $props(); 12 + const { text }: Data = data; 13 + 14 + let copied = $state(false); 15 + </script> 16 + 17 + <div class="flex justify-center items-center w-[100vw] h-[100vh] px-[15%]"> 18 + <!-- svelte-ignore a11y_click_events_have_key_events --> 19 + <!-- svelte-ignore a11y_no_static_element_interactions --> 20 + <div 21 + onclick={() => { 22 + navigator.clipboard.writeText(text); 23 + copied = true; 24 + setTimeout(() => (copied = false), 1000); 25 + }} 26 + > 27 + <Window style="!max-w-full" title={copied ? 'copied!' : 'click to copy'}> 28 + <span class="text-[32px] text-wrap break-all">{text}</span> 29 + </Window> 30 + </div> 31 + </div>
+38
eunomia/src/routes/(site)/entries/+layout.server.ts
··· 1 + import convertDate from '$lib/convertDate'; 2 + 3 + export interface PostData { 4 + path: string; 5 + published: string; 6 + metadata: Record<string, string>; 7 + } 8 + 9 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 10 + const allPostFiles: Record<string, any> = import.meta.glob('./*/+page.md', { eager: true }); 11 + const allPosts: PostData[] = Object.entries(allPostFiles) 12 + .map(([path, post]) => { 13 + const postPath = path.slice(2, -8); 14 + return { 15 + metadata: post.metadata, 16 + path: postPath, 17 + published: convertDate(post.metadata.date) 18 + }; 19 + }) 20 + .map((post) => { 21 + if (!('excerpt' in post.metadata)) { 22 + post.metadata.excerpt = ''; 23 + } 24 + return post; 25 + }) 26 + .toSorted((post, opost) => { 27 + const date = new Date(post.metadata.date); 28 + const odate = new Date(opost.metadata.date); 29 + return odate.getTime() - date.getTime(); 30 + }); 31 + export const _allPosts = allPosts; 32 + 33 + export async function load() { 34 + if (!allPosts.length) { 35 + return { status: 404 }; 36 + } 37 + return { posts: allPosts }; 38 + }
+3
eunomia/src/routes/(site)/entries/+page.server.ts
··· 1 + import {_load as load_logs} from '../log/+page.server.ts' 2 + 3 + export const load = load_logs
+40
eunomia/src/routes/(site)/entries/+page.svelte
··· 1 + <script lang="ts"> 2 + import Window from '$components/window.svelte'; 3 + import type { PostData } from './+layout.server.ts'; 4 + import LogPage from '../log/+page.svelte'; 5 + import type { NoteData } from '$components/note.svelte'; 6 + 7 + interface Props { 8 + data: { 9 + posts: PostData[]; 10 + feedPosts: NoteData[]; 11 + }; 12 + } 13 + 14 + let { data }: Props = $props(); 15 + </script> 16 + 17 + <div class="mx-auto md:max-w-fit flex flex-col-reverse md:flex-row gap-y-4 gap-x-16"> 18 + <div class="flex flex-col gap-y-4"> 19 + {#each data.posts as post (post.path)} 20 + <Window title={post.metadata.title} iconUri="/icons/entry.webp"> 21 + <a 22 + href="/entries/{post.path}" 23 + title="cd /entries/{post.path}" 24 + data-sveltekit-preload-data="off" 25 + > 26 + <div class="flex flex-col prose prose-ralsei leading-5"> 27 + <ul> 28 + <li> 29 + published on: <time datetime="{post.metadata.date} 00:00:00">{post.published}</time> 30 + </li> 31 + <li class="max-w-[34ch] text-wrap">excerpt: {post.metadata.excerpt}</li> 32 + </ul> 33 + <strong class="place-self-end text-ralsei-green-light"> read more... </strong> 34 + </div> 35 + </a> 36 + </Window> 37 + {/each} 38 + </div> 39 + <LogPage data={{ feedPosts: data.feedPosts }} /> 40 + </div>
+45
eunomia/src/routes/(site)/entries/_layout.svelte
··· 1 + <script lang="ts"> 2 + import { PUBLIC_BASE_URL } from '$env/static/public'; 3 + import Window from '$components/window.svelte'; 4 + import '$styles/app.css'; 5 + import { page } from '$app/state'; 6 + 7 + interface Props { 8 + title: string; 9 + date: Date; 10 + excerpt: string; 11 + children?: import('svelte').Snippet; 12 + } 13 + 14 + let { title, date, excerpt, children }: Props = $props(); 15 + 16 + let showMetadata = $derived(excerpt !== undefined && excerpt !== null); 17 + </script> 18 + 19 + <svelte:head> 20 + <meta property="og:description" content={excerpt} /> 21 + <meta property="og:type" content="article" /> 22 + <meta property="og:title" content={title} /> 23 + </svelte:head> 24 + 25 + <article class="mx-auto max-w-fit flex flex-wrap lg:flex-nowrap gap-4 h-entry"> 26 + <Window {title} iconUri="/icons/entry.webp" entry> 27 + <div class="prose prose-ralsei max-w-[80ch] e-content"> 28 + {@render children?.()} 29 + </div> 30 + </Window> 31 + {#if showMetadata} 32 + <Window title="metadata" sticky> 33 + <div class="prose prose-ralsei"> 34 + <ul> 35 + <link class="u-url" href="{PUBLIC_BASE_URL}{page.url.pathname}" /> 36 + <li>author: <a rel="author" class="p-author h-card" href={PUBLIC_BASE_URL}>dusk</a></li> 37 + <li>published on: <time class="dt-published" datetime="{date} 00:00:00">{date}</time></li> 38 + <li class="max-w-80 text-wrap"> 39 + excerpt: <div class="inline p-summary">{excerpt}</div> 40 + </li> 41 + </ul> 42 + </div> 43 + </Window> 44 + {/if} 45 + </article>
+35
eunomia/src/routes/(site)/entries/_rss/+server.ts
··· 1 + import { PUBLIC_BASE_URL } from '$env/static/public'; 2 + import { _allPosts, type PostData } from '../+layout.server.ts'; 3 + 4 + const entriesUrl = `${PUBLIC_BASE_URL}/entries`; 5 + 6 + export const GET = async () => { 7 + return new Response(render(_allPosts), { 8 + headers: { 9 + 'content-type': 'application/xml', 10 + 'cache-control': 'no-store' 11 + } 12 + }); 13 + }; 14 + 15 + const render = (posts: PostData[]) => `<?xml version="1.0" encoding="UTF-8" ?> 16 + <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> 17 + <channel> 18 + <atom:link href="${entriesUrl}/_rss" rel="self" type="application/rss+xml" /> 19 + <title>dawn's posts (@ptr.pet)</title> 20 + <link>${entriesUrl}</link> 21 + <description>posts from my website</description> 22 + ${posts 23 + .map( 24 + (post) => `<item> 25 + <guid>${entriesUrl}/${post.path}</guid> 26 + <title>${post.metadata.title}</title> 27 + <link>${entriesUrl}/${post.path}</link> 28 + <description>${post.metadata.excerpt}</description> 29 + <pubDate>${new Date(post.metadata.date).toUTCString()}</pubDate> 30 + </item>` 31 + ) 32 + .join('')} 33 + </channel> 34 + </rss> 35 + `;
+8
eunomia/src/routes/(site)/entries/hello-world/+page.md
··· 1 + +++ 2 + title = "hello universe" 3 + date = "2024-08-08" 4 + layout = "blogpost" 5 + excerpt = "meow" 6 + +++ 7 + 8 + hello everyone :3c
+25
eunomia/src/routes/(site)/entries/japan-trip24/+page.md
··· 1 + +++ 2 + title = "japan trip 09/24" 3 + date = "2024-09-19" 4 + layout = "blogpost" 5 + excerpt = "photos from its trip to japan" 6 + +++ 7 + 8 + <script lang="ts"> 9 + export let data 10 + </script> 11 + 12 + ## 1-16 / 09 / 2024 13 + 14 + photos it took while on a japan trip 15 + 16 + ~~these are *not* sorted, have fun trying to figure out the actual order (it accidentally stripped the exif data and its too lazy to find the images again)~~ 17 + fixed!!!! it also added a few images because its dumb and forgot 18 + 19 + *(you can click on an image to see original!)* 20 + 21 + <div class="grid gap-0.5 auto-rows-min md:grid-cols-4"> 22 + {#each data.images as image} 23 + <a class="!animate-none" href={image.og}><img loading="lazy" width={480} height={480} src={image.thumb} alt="from japan trip" class="w-full h-full object-cover [image-rendering:high-quality_!important]"/></a> 24 + {/each} 25 + </div>
+12
eunomia/src/routes/(site)/entries/japan-trip24/+page.server.ts
··· 1 + import images from './images.json'; 2 + 3 + export async function load() { 4 + return { 5 + images: images.map((id) => { 6 + return { 7 + og: `https://res.cloudinary.com/dgtwf7mar/image/upload/v1/${id}`, 8 + thumb: `https://res.cloudinary.com/dgtwf7mar/image/upload/c_fill,w_480,h_480,g_center/c_limit,w_480/f_auto/q_auto/v1/${id}` 9 + }; 10 + }) 11 + }; 12 + }
+218
eunomia/src/routes/(site)/entries/japan-trip24/images.json
··· 1 + [ 2 + "IMG_1548_grkjls", 3 + "IMG_1550_vodfkw", 4 + "IMG_1551_fc8ow3", 5 + "IMG_1554_nrfaxo", 6 + "IMG_1557_zonamx", 7 + "IMG_1559_fi9gql", 8 + "IMG_1560_f4tjn6", 9 + "IMG_1561_bgq40t", 10 + "IMG_1562_qcgnrm", 11 + "IMG_1564_htirzo", 12 + "IMG_1565_dttzth", 13 + "IMG_1566_ho5gwx", 14 + "IMG_1567_d0etvq", 15 + "IMG_1568_it4bmq", 16 + "IMG_1574_z8s3ko", 17 + "IMG_1575_mcgrvr", 18 + "IMG_1587_m9bv0s", 19 + "IMG_1588_hr921f", 20 + "IMG_1589_jht9el", 21 + "IMG_1590_aregyu", 22 + "IMG_1593_kznbh7", 23 + "IMG_1596_zm0dxi", 24 + "IMG_1602_klj3kk", 25 + "IMG_1603_oqdb98", 26 + "IMG_1604_xfsytd", 27 + "IMG_1608_ukyh5c", 28 + "IMG_1620_csri5w", 29 + "IMG_1622_tjvjso", 30 + "IMG_1623_cvegyx", 31 + "IMG_1626_ryegcv", 32 + "IMG_1632_xoct96", 33 + "IMG_1645_lmgpfk", 34 + "IMG_1646_qjkkfv", 35 + "IMG_1648_xswq18", 36 + "IMG_1650_urntrf", 37 + "IMG_1651_fcjzgt", 38 + "IMG_1652_zbttvs", 39 + "IMG_1654_tuesk4", 40 + "IMG_1662_s5otqc", 41 + "IMG_1663_vqk6mr", 42 + "IMG_1664_y2cqbv", 43 + "IMG_1675_vfatdq", 44 + "IMG_1677_xbh3zy", 45 + "IMG_1680_ubpd6l", 46 + "IMG_1682_d7mwxr", 47 + "IMG_1683_xp3pgp", 48 + "IMG_1684_vpabrd", 49 + "IMG_1685_luhmdk", 50 + "IMG_1686_czucuf", 51 + "IMG_1687_tc1oo1", 52 + "IMG_1689_uju7nq", 53 + "IMG_1692_pagenu", 54 + "IMG_1694_qmcryo", 55 + "IMG_1697_yuxzde", 56 + "IMG_1699_yq0aax", 57 + "IMG_1700_wddm7r", 58 + "IMG_1701_ylvvyx", 59 + "IMG_1708_czypw1", 60 + "IMG_1712_wwjagg", 61 + "IMG_1734_gojndg", 62 + "IMG_1735_yd2dy9", 63 + "IMG_1738_wgpapk", 64 + "IMG_1741_hbtbvk", 65 + "IMG_1756_njptxf", 66 + "IMG_1759_b91ff5", 67 + "IMG_1763_saii3h", 68 + "IMG_1775_byhnbi", 69 + "IMG_1776_jtl3uc", 70 + "IMG_1778_ufiaw4", 71 + "IMG_1780_mqa1tk", 72 + "IMG_1783_j9zqjv", 73 + "IMG_1785_ou7nvf", 74 + "IMG_1786_zkn6mk", 75 + "IMG_1788_mb8oip", 76 + "IMG_1789_fa4xut", 77 + "IMG_1790_fji4oi", 78 + "IMG_1799_y2pqnu", 79 + "IMG_1801_nb2fyk", 80 + "IMG_1805_ugpswy", 81 + "IMG_1806_edmuxz", 82 + "IMG_1812_vozh2q", 83 + "IMG_1813_yxlipi", 84 + "IMG_1814_z7lgfg", 85 + "IMG_1816_wft8gn", 86 + "IMG_1819_aotxhf", 87 + "IMG_1822_f536dv", 88 + "IMG_1832_crxl8c", 89 + "IMG_1834_dxscck", 90 + "IMG_1836_nzayf6", 91 + "IMG_1837_w0umua", 92 + "IMG_1838_jun3om", 93 + "IMG_1839_gu0huo", 94 + "IMG_1841_ypknji", 95 + "IMG_1844_mscenv", 96 + "IMG_1845_btu9ks", 97 + "IMG_1846_ewgafy", 98 + "IMG_1848_l61sd5", 99 + "IMG_1849_zympk9", 100 + "IMG_1862_uxhhcu", 101 + "IMG_1864_vmouhi", 102 + "IMG_1867_apl41d", 103 + "IMG_1868_obb1q1", 104 + "IMG_1870_wj4wx6", 105 + "IMG_1875_cbr5us", 106 + "IMG_1877_zbnsf4", 107 + "IMG_1880_tjdd4g", 108 + "IMG_1882_i2tt70", 109 + "IMG_1888_gsjmcv", 110 + "IMG_1890_uronrs", 111 + "IMG_1893_jgr7lt", 112 + "IMG_1895_pklwtb", 113 + "IMG_1898_bedmeo", 114 + "IMG_1900_bkkvjo", 115 + "IMG_1901_s8nt0w", 116 + "IMG_1902_efpceq", 117 + "IMG_1905_e7knuy", 118 + "IMG_1908_ssdjim", 119 + "IMG_1911_ehsgjz", 120 + "IMG_1912_xbxhev", 121 + "IMG_1914_vijsn0", 122 + "IMG_1915_zdev3v", 123 + "IMG_1916_qwqg78", 124 + "IMG_1917_gitpjp", 125 + "IMG_1918_ris8iy", 126 + "IMG_1920_srnvqq", 127 + "IMG_1921_olm7ko", 128 + "IMG_1923_xmylox", 129 + "IMG_1924_trh5ah", 130 + "IMG_1925_wu1e9g", 131 + "IMG_1927_pb6htn", 132 + "IMG_1939_njl48e", 133 + "IMG_1946_xqa0w0", 134 + "IMG_1948_peik8p", 135 + "IMG_1949_ofxeix", 136 + "IMG_1958_fnz3a6", 137 + "IMG_1959_xo1tn5", 138 + "IMG_1964_vhahus", 139 + "IMG_1975_i0azhi", 140 + "IMG_1977_dffpgt", 141 + "IMG_1985_g0z0ja", 142 + "IMG_1988_qm6hln", 143 + "IMG_1989_tfhakh", 144 + "IMG_1990_udmwst", 145 + "IMG_1994_nq1m8c", 146 + "IMG_1995_d865i7", 147 + "IMG_1996_jqexwr", 148 + "IMG_1999_mqli90", 149 + "IMG_2001_n3lpuz", 150 + "IMG_2007_sz0r6o", 151 + "IMG_2008_slarjg", 152 + "IMG_2009_gpch49", 153 + "IMG_2013_tcwlqe", 154 + "IMG_2014_idxngh", 155 + "IMG_2021_v4stwp", 156 + "IMG_2022_d3ihri", 157 + "IMG_2023_lrbsoj", 158 + "IMG_2024_q33xbl", 159 + "IMG_2025_uvntmq", 160 + "IMG_2026_iy6e9s", 161 + "IMG_2028_fazbnu", 162 + "IMG_2030_afziyc", 163 + "IMG_2031_ivs1ig", 164 + "IMG_2032_yaerpl", 165 + "IMG_2036_jvseyo", 166 + "IMG_2039_o3jzt7", 167 + "IMG_2050_vonn8i", 168 + "IMG_2063_i0cgat", 169 + "IMG_2065_dzmvuc", 170 + "IMG_2071_rqpcvd", 171 + "IMG_2075_r926jy", 172 + "IMG_2077_ydvll5", 173 + "IMG_2079_mhsa7e", 174 + "IMG_2087_jccuhd", 175 + "IMG_2090_s16pgf", 176 + "IMG_2091_lidh3q", 177 + "IMG_2092_bwhedb", 178 + "IMG_2094_mlnltg", 179 + "IMG_2096_je0fyy", 180 + "IMG_2097_t18aim", 181 + "IMG_2098_uiokml", 182 + "IMG_2102_pkjche", 183 + "IMG_2111_jmslli", 184 + "IMG_2113_zkqsiq", 185 + "IMG_2114_mofaie", 186 + "IMG_2118_lmjwhj", 187 + "IMG_2119_yzvhqm", 188 + "IMG_2120_wgnbim", 189 + "IMG_2121_mwvte4", 190 + "IMG_2123_kxrfiu", 191 + "IMG_2125_fjinmr", 192 + "IMG_2126_l0qe33", 193 + "IMG_2128_w36btk", 194 + "IMG_2131_wc0z6d", 195 + "IMG_2141_pnw1mp", 196 + "IMG_2143_je1kxx", 197 + "IMG_2144_wfyrbj", 198 + "IMG_2146_k84hr3", 199 + "IMG_2149_j7qjwy", 200 + "IMG_2150_fcc1cx", 201 + "IMG_2151_fjtdhm", 202 + "IMG_2155_zhmhmk", 203 + "IMG_2159_pjbb7e", 204 + "IMG_2162_i0i9fr", 205 + "IMG_2165_cpvzlg", 206 + "IMG_2168_zgsdcf", 207 + "IMG_2171_lbhlnr", 208 + "IMG_2172_c5hbzn", 209 + "IMG_2173_jwtjn7", 210 + "IMG_2174_ntsxh1", 211 + "IMG_2175_sak0od", 212 + "IMG_2176_svth1u", 213 + "IMG_2178_sjdqzt", 214 + "IMG_2180_grhg9y", 215 + "IMG_2181_kj9pko", 216 + "IMG_2188_ao64sh", 217 + "IMG_2190_ngl8q8" 218 + ]
+58
eunomia/src/routes/(site)/entries/test/+page.md
··· 1 + +++ 2 + title = "test" 3 + date = "2024-08-13" 4 + layout = "blogpost" 5 + excerpt = "this is a test document. it is used for testing stuff." 6 + +++ 7 + 8 + 9 + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed iaculis ligula sed odio fermentum, ac aliquet diam sagittis. Phasellus vel elementum arcu. Fusce id ante lacus. Nullam malesuada, enim nec maximus mollis, tortor metus dapibus mi, id tincidunt massa massa sed velit. Curabitur cursus, lorem rutrum laoreet posuere, tellus eros faucibus odio, non ullamcorper magna lectus non justo. Fusce pulvinar sagittis nisl et pretium. Proin quis pellentesque risus. 10 + 11 + Pellentesque massa purus, ornare aliquam velit et, hendrerit lacinia felis. Fusce nec felis maximus, egestas lorem et, vehicula libero. Donec gravida sodales porta. Nunc ac urna vestibulum, finibus erat vitae, euismod nisi. Morbi auctor pretium diam ut pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus pulvinar orci vitae dolor sodales placerat. Integer tristique condimentum massa, non euismod nisi sodales eu. 12 + 13 + 1. a 14 + 1. b 15 + 1. c 16 + 1. d 17 + 18 + - a 19 + - b 20 + - c 21 + - d 22 + 23 + `test test test` 24 + 25 + ```rust 26 + fn main() { 27 + println!("howdy") 28 + } 29 + ``` 30 + 31 + ```js 32 + function lorem(ipsum, dolor = 1) { 33 + const sit = ipsum == null ? 0 : ipsum.sit; 34 + dolor = sit - amet(dolor); 35 + return sit ? consectetur(ipsum, 0, dolor < 0 ? 0 : dolor) : []; 36 + } 37 + function adipiscing(...elit) { 38 + if (!elit.sit) { 39 + return []; 40 + } 41 + const sed = elit[0]; 42 + return eiusmod.tempor(sed) ? sed : [sed]; 43 + } 44 + function incididunt(ipsum, ut = 1) { 45 + ut = labore.et(amet(ut), 0); 46 + const sit = ipsum == null ? 0 : ipsum.sit; 47 + if (!sit || ut < 1) { 48 + return []; 49 + } 50 + let dolore = 0; 51 + let magna = 0; 52 + const aliqua = new eiusmod(labore.ut(sit / ut)); 53 + while (dolore < sit) { 54 + aliqua[magna++] = consectetur(ipsum, dolore, (dolore += ut)); 55 + } 56 + return aliqua; 57 + } 58 + ```
+148
eunomia/src/routes/(site)/guestbook/+page.server.ts
··· 1 + import { redirect, type Cookies, type RequestEvent } from '@sveltejs/kit'; 2 + import { scopeCookies as _scopeCookies, fancyText } from '$lib'; 3 + import { RetryAfterRateLimiter } from 'sveltekit-rate-limiter/server'; 4 + import { PUBLIC_BASE_URL } from '$env/static/public'; 5 + import { getBskyClient, getUserPosts } from '$lib/bluesky.js'; 6 + import { getVisitorId } from '$lib/visits'; 7 + import { nanoid } from 'nanoid'; 8 + import { noteFromBskyPost, type NoteData } from '$components/note.svelte'; 9 + import { get, writable } from 'svelte/store'; 10 + import type { Post } from '@skyware/bot'; 11 + import { useToken as checkApiToken, newToken } from '$lib/apiToken.js'; 12 + 13 + export const prerender = false; 14 + 15 + const callbackUrl = `${PUBLIC_BASE_URL}/guestbook/`; 16 + 17 + const createPostRatelimiter = new RetryAfterRateLimiter({ 18 + IP: [5, 'd'], 19 + IPUA: [2, 'h'] 20 + }); 21 + 22 + const scopeCookies = (cookies: Cookies) => { 23 + return _scopeCookies(cookies, '/guestbook'); 24 + }; 25 + 26 + const postTokens = writable<Set<string>>(new Set()); 27 + const entries = writable<NoteData[]>([]); 28 + 29 + export const _fetchEntries = async () => { 30 + const newEntries: NoteData[] = []; 31 + const { posts } = await getUserPosts('did:web:guestbook.gaze.systems', 14); 32 + const fetchPostReplies = async (post: Post) => { 33 + if ((post.replyCount ?? 0) === 0) return { post, replies: [] }; 34 + return { post, replies: await post.fetchChildren({ depth: 1, force: true }) }; 35 + }; 36 + const postsWithReplies = await Promise.all(posts.map(fetchPostReplies)); 37 + for (const { post, replies } of postsWithReplies) { 38 + const note = noteFromBskyPost(post); 39 + note.children = replies.map((reply) => { 40 + const replyNote = noteFromBskyPost(reply); 41 + replyNote.purposeAction = 'reply'; 42 + replyNote.outgoingLinks = [{ name: 'bsky-reply', link: reply.uri }]; 43 + return replyNote; 44 + }); 45 + newEntries.push(note); 46 + } 47 + entries.set(newEntries); 48 + return newEntries; 49 + }; 50 + 51 + export const actions = { 52 + post: async (event: RequestEvent) => { 53 + const { request, cookies } = event; 54 + const scopedCookies = scopeCookies(cookies); 55 + const rateStatus = await createPostRatelimiter.check(event); 56 + if (rateStatus.limited) { 57 + scopedCookies.set( 58 + 'sendError', 59 + `you are being ratelimited sowwy :c, try again after ${rateStatus.retryAfter} seconds` 60 + ); 61 + redirect(303, callbackUrl); 62 + } 63 + const form = await request.formData(); 64 + const apiToken = form.get('_token')?.toString() ?? ''; 65 + if (!checkApiToken(apiToken)) { 66 + scopedCookies.set('sendError', 'api token is invalid'); 67 + redirect(303, callbackUrl); 68 + } 69 + const content = form.get('content')?.toString().substring(0, 300); 70 + if (content === undefined) { 71 + scopedCookies.set('sendError', 'content field is missing'); 72 + redirect(303, callbackUrl); 73 + } 74 + // save form content in a cookie 75 + scopedCookies.set('postData', content); 76 + // create a token we will use to validate 77 + const token = nanoid(); 78 + postTokens.update((set) => set.add(token)); 79 + scopedCookies.set('postAuth', token); 80 + redirect(303, callbackUrl); 81 + } 82 + }; 83 + 84 + export async function load({ cookies }) { 85 + const scopedCookies = scopeCookies(cookies); 86 + const data = { 87 + entries: get(entries), 88 + sendError: scopedCookies.get('sendError') || '', 89 + getError: '', 90 + sendRatelimited: scopedCookies.get('sendRatelimited') || '', 91 + getRatelimited: false, 92 + fillText: fancyText(getVisitorId(cookies) ?? nanoid()), 93 + apiToken: newToken() 94 + }; 95 + const rawPostData = scopedCookies.get('postData') || null; 96 + const postAuth = scopedCookies.get('postAuth') || null; 97 + if (rawPostData !== null && postAuth !== null) { 98 + // delete the postData cookie after we got it cause we dont need it anymore 99 + scopedCookies.delete('postData'); 100 + scopedCookies.delete('postAuth'); 101 + // get and validate token 102 + if (!get(postTokens).has(postAuth)) { 103 + scopedCookies.set( 104 + 'sendError', 105 + 'invalid post token! this is either a bug or you should stop doing silly stuff' 106 + ); 107 + redirect(303, callbackUrl); 108 + } 109 + postTokens.update((set) => { 110 + set.delete(postAuth); 111 + return set; 112 + }); 113 + // post entry 114 + try { 115 + // return error if content was not set or if empty 116 + const content = rawPostData.substring(0, 300).trim(); 117 + if (content.length === 0) { 118 + scopedCookies.set('sendError', `content field was empty`); 119 + redirect(303, callbackUrl); 120 + } 121 + // post to guestbook account 122 + const client = await getBskyClient(); 123 + await client.post( 124 + { 125 + text: content, 126 + threadgate: { allowMentioned: false, allowFollowing: true } 127 + }, 128 + { resolveFacets: false } 129 + ); 130 + try { 131 + data.entries = await _fetchEntries(); 132 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 133 + } catch (err: any) { 134 + data.getError = err.toString(); 135 + } 136 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 137 + } catch (err: any) { 138 + scopedCookies.set('sendError', err.toString()); 139 + redirect(303, callbackUrl); 140 + } 141 + redirect(303, callbackUrl); 142 + } 143 + // delete the cookies after we get em since we dont really need these more than once 144 + scopedCookies.delete('sendError'); 145 + scopedCookies.delete('sendRatelimited'); 146 + 147 + return data; 148 + }
+186
eunomia/src/routes/(site)/guestbook/+page.svelte
··· 1 + <script lang="ts"> 2 + import Note, { type NoteData } from '$components/note.svelte'; 3 + import Token from '$components/token.svelte'; 4 + import Window from '$components/window.svelte'; 5 + 6 + interface Props { 7 + data: { 8 + entries: NoteData[]; 9 + sendError: string; 10 + getError: string; 11 + sendRatelimited: string; 12 + getRatelimited: boolean; 13 + fillText: string; 14 + apiToken: string; 15 + }; 16 + } 17 + 18 + let { data }: Props = $props(); 19 + 20 + const placeholders = ['meow', 'arf', '0110100001101001', '0x6869']; 21 + </script> 22 + 23 + <div class="flex flex-col-reverse md:flex-row gap-2 md:gap-4"> 24 + <Window title="guestbook" style="ml-auto" iconUri="/icons/guestbook.webp"> 25 + <div class="flex flex-col gap-1 max-w-[50ch]"> 26 + <div class="prose prose-ralsei entry p-2"> 27 + hia, here is the guestbook if you wanna post anything :) <br /> 28 + be good pretty please (and don't be shy!!!) <br /> 29 + (to see all the entries, look 30 + <a href="https://bsky.app/profile/guestbook.gaze.systems">here</a>) 31 + </div> 32 + <form method="post"> 33 + <input type="hidden" name="_token" value={data.apiToken} /> 34 + <div class="entry entryflex"> 35 + <textarea 36 + class="p-1 m-0 ml-0.5 bg-transparent resize-none text-shadow-white focus:[box-shadow:none] placeholder-shown:[text-shadow:none] [field-sizing:content] border-none" 37 + name="content" 38 + placeholder="say {placeholders[ 39 + Math.floor(Math.random() * placeholders.length) 40 + ]}!" 41 + maxlength="300" 42 + required 43 + ></textarea> 44 + </div> 45 + <div class="flex flex-row gap-1 mt-1"> 46 + <input 47 + type="submit" 48 + value="click to post" 49 + formaction="?/post" 50 + class="entry text-ralsei-green-light leading-none hover:underline motion-safe:hover:animate-squiggle p-1 z-50" 51 + /> 52 + <div class="marquee-wrapper entry text-ralsei-white/50"> 53 + <div class="marquee font-monospace"> 54 + <p class="text-shadow-none">{data.fillText}</p> 55 + <p class="text-shadow-none">{data.fillText}</p> 56 + </div> 57 + </div> 58 + </div> 59 + {#if data.sendRatelimited} 60 + <p class="text-error">you are ratelimited, try again in 30 seconds</p> 61 + {/if} 62 + {#if data.sendError} 63 + <details class="w-[50ch]"> 64 + <summary class="text-error">got error trying to send post</summary> 65 + <p>{data.sendError}</p> 66 + </details> 67 + {/if} 68 + </form> 69 + </div> 70 + </Window> 71 + <Window 72 + id="guestbookentries" 73 + style="mr-auto" 74 + title="entries" 75 + iconUri="/icons/entries.webp" 76 + removePadding 77 + > 78 + <div class="flex flex-col gap-2 md:gap-4 2xl:w-[60ch]"> 79 + {#if data.getRatelimited} 80 + <p class="text-error"> 81 + woops, looks like you are being ratelimited, try again in like half a minute :3 82 + </p> 83 + {:else if data.getError} 84 + <details class="w-[50ch]"> 85 + <summary class="text-error">got error trying to fetch entries</summary> 86 + <p>{data.getError}</p> 87 + </details> 88 + {:else} 89 + <div 90 + class=" 91 + prose prose-ralsei 92 + prose-pre:rounded-none prose-pre:!m-0 prose-pre:!p-2 93 + prose-pre:!bg-ralsei-black prose-code:!bg-ralsei-black 94 + " 95 + > 96 + <pre class="language-bash"><code class="language-bash" 97 + ><nobr> 98 + <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 99 + v="source" 100 + funct 101 + /> <Token v="scripts/log.nu" /> 102 + <br /> 103 + <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 104 + v="let" 105 + funct 106 + /> <Token v="entries" /> <Token v="=" punct /> <Token 107 + v="(" 108 + punct 109 + /><Token v="ls" funct /> <Token v="guestbook" /> <Token 110 + v="|" 111 + punct 112 + /> <Token v="reverse" funct /> <Token v="|" punct /> <Token 113 + v="take" 114 + funct 115 + /> <Token v="14" /><Token v=")" punct /> 116 + <br /> 117 + <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 118 + v="$entries" 119 + /> <Token v="|" punct /> <Token v="each" funct /> <Token 120 + v="&#123;" 121 + punct 122 + /><Token v="|" punct /><Token v="file" /><Token 123 + v="|" 124 + punct 125 + /> <Token v="render" funct /> <Token v="(" punct /><Token 126 + v="open" 127 + funct 128 + /> <Token v="$file.name" /><Token v=")" punct /><Token 129 + v="&#125;" 130 + punct 131 + /> 132 + <br /> 133 + <br /> 134 + {#each data.entries as note, index ([note.content, note.published])} 135 + <Note 136 + mapOutgoingNames={{ bsky: '', reply: 'src' }} 137 + showOutgoing={true} 138 + rootNote={note} 139 + /> 140 + {#if index < data.entries.length - 1} 141 + <div class="mt-2"></div> 142 + {/if} 143 + {/each} 144 + </nobr></code 145 + ></pre> 146 + </div> 147 + {/if} 148 + </div> 149 + </Window> 150 + </div> 151 + 152 + <style lang="postcss"> 153 + .entry { 154 + @apply bg-ralsei-green-dark/70 border-ralsei-green-light/30 border-x-[4px] border-y-[5px]; 155 + border-style: ridge; 156 + } 157 + .entryflex { 158 + @apply flex flex-col p-1; 159 + } 160 + 161 + .marquee-wrapper { 162 + max-width: 100%; 163 + overflow: hidden; 164 + } 165 + 166 + .marquee { 167 + white-space: nowrap; 168 + overflow: hidden; 169 + display: inline-block; 170 + animation: marquee 10s linear infinite; 171 + } 172 + 173 + .marquee p { 174 + transform: translateY(15%); 175 + display: inline-block; 176 + } 177 + 178 + @keyframes marquee { 179 + 0% { 180 + transform: translate3d(0, 0, 0); 181 + } 182 + 100% { 183 + transform: translate3d(-50%, 0, 0); 184 + } 185 + } 186 + </style>
+12
eunomia/src/routes/(site)/log/+page.server.ts
··· 1 + import { getLastPosts } from '$lib/bluesky.js'; 2 + import { noteFromBskyPost } from '$components/note.svelte'; 3 + 4 + export const load = async () => { 5 + return _load(); 6 + }; 7 + 8 + export const _load = async () => { 9 + return { 10 + feedPosts: getLastPosts().map(noteFromBskyPost) 11 + }; 12 + };
+64
eunomia/src/routes/(site)/log/+page.svelte
··· 1 + <script lang="ts"> 2 + import Window from '$components/window.svelte'; 3 + import Token from '$components/token.svelte'; 4 + import Note, { type NoteData } from '$components/note.svelte'; 5 + 6 + interface Props { 7 + data: { 8 + feedPosts: NoteData[]; 9 + }; 10 + } 11 + 12 + let { data }: Props = $props(); 13 + </script> 14 + 15 + <Window title="terminal" removePadding> 16 + <div 17 + class=" 18 + prose prose-ralsei 19 + prose-pre:rounded-none prose-pre:!m-0 prose-pre:!p-2 20 + prose-pre:!bg-ralsei-black prose-code:!bg-ralsei-black 21 + " 22 + > 23 + <pre class="language-bash"><code class="language-bash" 24 + ><nobr> 25 + <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 26 + v="source" 27 + funct 28 + /> <Token v="scripts/log.nu" /> 29 + <br /> 30 + <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 31 + v="let" 32 + funct 33 + /> <Token v="entries" /> <Token v="=" punct /> <Token v="(" punct /><Token 34 + v="ls" 35 + funct 36 + /> <Token v="logs" /> <Token v="|" punct /> <Token v="reverse" funct /> <Token 37 + v="|" 38 + punct 39 + /> <Token v="take" funct /> <Token v="10" /><Token v=")" punct /> 40 + <br /> 41 + <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 42 + v="$entries" 43 + /> <Token v="|" punct /> <Token v="each" funct /> <Token v="&#123;" punct /><Token 44 + v="|" 45 + punct 46 + /><Token v="file" /><Token v="|" punct /> <Token v="render" funct /> <Token 47 + v="(" 48 + punct 49 + /><Token v="open" funct /> <Token v="$file.name" /><Token v=")" punct /><Token 50 + v="&#125;" 51 + punct 52 + /> 53 + <br /> 54 + <br /> 55 + {#each data.feedPosts as note, index ([note.content, note.published])} 56 + <Note rootNote={note} /> 57 + {#if index < data.feedPosts.length - 1} 58 + <div class="mt-2"></div> 59 + {/if} 60 + {/each} 61 + </nobr></code 62 + ></pre> 63 + </div> 64 + </Window>
+5
eunomia/src/routes/(site)/log/_rss/+server.ts
··· 1 + import { redirect } from '@sveltejs/kit'; 2 + 3 + export const GET = async () => { 4 + redirect(301, 'https://bsky.app/profile/did:plc:dfl62fgb7wtjj3fcbb72naae/rss'); 5 + };
+13
eunomia/src/routes/_api/pet/bounce/+server.ts
··· 1 + import { incrementBounceCount, pushMetric } from '$lib/metrics'; 2 + import { isBot } from '$lib/visits'; 3 + import { checkUrl as checkApiToken } from '$lib/apiToken.js'; 4 + 5 + export const GET = async ({ request, url }) => { 6 + if (isBot(request) || !checkApiToken(url)) return new Response(); 7 + try { 8 + await pushMetric({ gazesys_pet_bounce_total: await incrementBounceCount() }); 9 + } catch (error) { 10 + console.log(`error while pushing bounce metric: ${error}`); 11 + } 12 + return new Response(); 13 + };
+14
eunomia/src/routes/_api/pet/distance/+server.ts
··· 1 + import { distanceTravelled, pushMetric } from '$lib/metrics'; 2 + import { isBot } from '$lib/visits'; 3 + import { checkUrl as checkApiToken } from '$lib/apiToken.js'; 4 + 5 + export const POST = async ({ request, url }) => { 6 + if (isBot(request) || !checkApiToken(url)) return new Response(); 7 + try { 8 + const delta = parseFloat(await request.text()); 9 + await pushMetric({ gazesys_pet_distance_total: await distanceTravelled.increment(delta) }); 10 + } catch (error) { 11 + console.log(`error while pushing bounce metric: ${error}`); 12 + } 13 + return new Response(); 14 + };
+10
eunomia/src/routes/_api/pushnotif/+server.ts
··· 1 + import { checkUrl as checkApiToken } from '$lib/apiToken.js'; 2 + import { pushNotification } from '$lib/pushnotif'; 3 + 4 + export const GET = async ({ url }) => { 5 + if (!checkApiToken(url)) return new Response(); 6 + const content = url.searchParams.get('content'); 7 + if (content === null) return new Response(); 8 + pushNotification(content); 9 + return new Response(); 10 + };
+5
eunomia/src/routes/annoy/+server.ts
··· 1 + const index = await import('./index.html?raw'); 2 + 3 + export const GET = async () => { 4 + return new Response(index.default, { headers: { 'Content-Type': 'text/html' } }); 5 + };
+29
eunomia/src/routes/annoy/index.html
··· 1 + <!doctype html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <title>annoy</title> 7 + <style type="text/css"> 8 + * { 9 + margin: 0; 10 + padding: 0; 11 + overflow: hidden; 12 + } 13 + 14 + canvas { 15 + width: 100vw !important; 16 + height: 100vh !important; 17 + } 18 + </style> 19 + </head> 20 + 21 + <body> 22 + <script type="module"> 23 + import init from './annoy/annoyance-meow.js'; 24 + window.addEventListener('load', () => { 25 + init(); 26 + }); 27 + </script> 28 + </body> 29 + </html>
+25
eunomia/src/routes/cover_art/[mbid]/+server.ts
··· 1 + import { env } from '$env/dynamic/private'; 2 + import { error } from '@sveltejs/kit'; 3 + 4 + export const GET = async ({ params }) => { 5 + const mbid = params.mbid?.replace('.jpg', ''); 6 + 7 + if (!mbid) { 8 + throw error(404, 'Missing MBID'); 9 + } 10 + 11 + const cacheDir = `${env.WEBSITE_DATA_DIR}/cover_art_cache`; 12 + const filePath = `${cacheDir}/${mbid}.jpg`; 13 + 14 + try { 15 + const file = await Deno.readFile(filePath); 16 + return new Response(file, { 17 + headers: { 18 + 'Content-Type': 'image/jpeg', 19 + 'Cache-Control': 'public, max-age=31536000, immutable' 20 + } 21 + }); 22 + } catch { 23 + throw error(404, 'cover art not found'); 24 + } 25 + };
+5
eunomia/src/routes/robots.txt/+server.ts
··· 1 + import { getRobotsTxt } from '$lib/robots'; 2 + 3 + export const GET = async () => { 4 + return new Response(await getRobotsTxt()); 5 + };
+408
eunomia/src/styles/app.css
··· 1 + @import './prism-synthwave84.css'; 2 + 3 + @tailwind base; 4 + @tailwind components; 5 + @tailwind utilities; 6 + 7 + @layer base { 8 + :root { 9 + @apply font-sans-serif bg-ralsei-black text-ralsei-white; 10 + @apply prose-code:font-monospace prose-headings:font-monospace; 11 + cursor: url('/icons/gaze_closed.webp'), default; 12 + scrollbar-color: theme(colors.ralsei.green.dark) transparent; 13 + -webkit-font-smoothing: none !important; 14 + font-smooth: never !important; 15 + font-smoothing: none !important; 16 + } 17 + 18 + @font-face { 19 + font-family: 'Fusion Pixel 8px Monospaced SC'; 20 + src: url('/fonts/fusionmono.woff2') format('woff2'); 21 + font-weight: normal; 22 + font-style: normal; 23 + font-display: swap; 24 + } 25 + 26 + @font-face { 27 + font-family: 'Fusion Pixel 8px Proportional SC'; 28 + src: url('/fonts/fusionprop.woff2') format('woff2'); 29 + font-weight: normal; 30 + font-style: normal; 31 + font-display: swap; 32 + } 33 + 34 + @font-face { 35 + font-family: 'Doll Mono'; 36 + src: url('/fonts/dollmonoopt.woff2') format('woff2'); 37 + } 38 + 39 + .prose h1::before { 40 + content: '[ '; 41 + } 42 + .prose h1::after { 43 + content: ' ]'; 44 + } 45 + 46 + .prose h2::before { 47 + content: '[= '; 48 + } 49 + .prose h2::after { 50 + content: ' =]'; 51 + } 52 + 53 + .prose h3::before { 54 + content: '[== '; 55 + } 56 + .prose h3::after { 57 + content: ' ==]'; 58 + } 59 + 60 + .prose h4::before { 61 + content: '[=== '; 62 + } 63 + .prose h4::after { 64 + content: ' ===]'; 65 + } 66 + 67 + /* .prose h1::after,.prose h2::after,.prose h3::after,.prose h4::after { 68 + @apply motion-safe:animate-blink; 69 + content: '_'; 70 + } */ 71 + 72 + .prose a { 73 + text-decoration: none; 74 + } 75 + 76 + .prose a:hover { 77 + @apply motion-safe:animate-squiggle; 78 + text-decoration: underline; 79 + } 80 + 81 + h1, 82 + h2, 83 + h3, 84 + h4, 85 + h5, 86 + h6, 87 + .text-shadow-pink { 88 + text-shadow: 89 + 0 0 3px theme(colors.ralsei.black), 90 + 0 0 6px theme(colors.ralsei.pink.neon), 91 + 0 0 10px #fff3; 92 + } 93 + 94 + .text-shadow-red { 95 + text-shadow: 96 + 0 0 1px theme(colors.ralsei.black), 97 + 0 0 5px theme(colors.red.600); 98 + } 99 + 100 + .text-shadow-none { 101 + text-shadow: none; 102 + } 103 + 104 + .prose ul, 105 + ul { 106 + list-style-type: '>>'; 107 + } 108 + 109 + .text-shadow-green { 110 + text-shadow: 111 + 0 0 2px theme(colors.ralsei.black), 112 + 0 0 5px theme(colors.ralsei.green.light); 113 + } 114 + 115 + a, 116 + button, 117 + input[type='submit'] { 118 + @apply text-shadow-green; 119 + } 120 + 121 + a, 122 + button, 123 + details, 124 + input[type='submit'] { 125 + cursor: url('/icons/gaze.webp'), pointer; 126 + } 127 + 128 + .animate-squiggle { 129 + animation: squigglevision 0.3s infinite; 130 + } 131 + 132 + @keyframes squigglevision { 133 + 0% { 134 + filter: url('#squiggly-0'); 135 + } 136 + 25% { 137 + filter: url('#squiggly-1'); 138 + } 139 + 50% { 140 + filter: url('#squiggly-2'); 141 + } 142 + 75% { 143 + filter: url('#squiggly-3'); 144 + } 145 + 100% { 146 + filter: url('#squiggly-4'); 147 + } 148 + } 149 + 150 + @keyframes blink { 151 + 0% { 152 + opacity: 1; 153 + } 154 + 50% { 155 + opacity: 0; 156 + } 157 + 100% { 158 + opacity: 1; 159 + } 160 + } 161 + } 162 + 163 + @layer utilities { 164 + .text-error { 165 + @apply text-xl text-red-600 text-shadow-red; 166 + } 167 + 168 + .border-groove { 169 + border-style: groove; 170 + } 171 + 172 + .border-ridge { 173 + border-style: ridge; 174 + } 175 + 176 + .app-grid-background-anim { 177 + animation: 4s linear app-grid-move-first-layer infinite; 178 + } 179 + 180 + .app-grid-background-second-layer-anim { 181 + animation: 12s linear app-grid-move-second-layer infinite; 182 + } 183 + 184 + @keyframes app-grid-move-first-layer { 185 + 0% { 186 + background-position: 0px 0px; 187 + } 188 + 100% { 189 + background-position: 126px 84px; 190 + } 191 + } 192 + 193 + @keyframes app-grid-move-second-layer { 194 + 0% { 195 + background-position: 96px 120px; 196 + } 197 + 100% { 198 + background-position: 0px 0px; 199 + } 200 + } 201 + 202 + @media (prefers-reduced-motion: no-preference) { 203 + @keyframes bounce-reverse { 204 + 0%, 205 + 100% { 206 + transform: none; 207 + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); 208 + } 209 + 50% { 210 + transform: translateY(-25%); 211 + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); 212 + } 213 + } 214 + } 215 + @media (prefers-reduced-motion: no-preference) { 216 + .animate-bounce-reverse:hover { 217 + animation: bounce-reverse 1s infinite; 218 + } 219 + } 220 + } 221 + 222 + a.app-selected-route { 223 + text-shadow: 224 + 0 0 2px theme(colors.ralsei.black), 225 + 0 0 5px theme(colors.ralsei.pink.regular); 226 + } 227 + 228 + .app-grid-background { 229 + background-image: 230 + linear-gradient(theme(colors.ralsei.green.light / 0.4), transparent 2px), 231 + linear-gradient(to right, theme(colors.ralsei.green.light / 0.4), transparent 2px); 232 + background-size: 233 + 100% 42px, 234 + 42px 100%; 235 + } 236 + 237 + .app-grid-background-second-layer { 238 + background-image: 239 + linear-gradient(theme(colors.ralsei.pink.neon / 0.4), transparent 1px), 240 + linear-gradient(to right, theme(colors.ralsei.pink.neon / 0.4), transparent 1px); 241 + background-size: 242 + 100% 24px, 243 + 24px 100%; 244 + } 245 + 246 + @media (prefers-reduced-motion: no-preference) { 247 + .animate-window-open { 248 + animation: 0.5s ease-out window-open-scale forwards; 249 + } 250 + 251 + .animate-window-open-vertical { 252 + animation: 0.5s ease-out window-open-scale-vertical forwards; 253 + transform-origin: bottom; 254 + } 255 + 256 + .animate-window-open-horizontal { 257 + animation: 0.5s ease-out window-open-scale-horizontal forwards; 258 + transform-origin: left; 259 + } 260 + 261 + .animate-window-open-move-up { 262 + animation: 0.5s ease-out window-open-move-up forwards; 263 + } 264 + 265 + .animate-window-open-move-down { 266 + animation: 0.5s ease-out window-open-move-down forwards; 267 + } 268 + 269 + .animate-window-open-move-left { 270 + animation: 0.5s ease-out window-open-move-left forwards; 271 + } 272 + 273 + .animate-window-open-move-right { 274 + animation: 0.5s ease-out window-open-move-right forwards; 275 + } 276 + 277 + .animate-overflow-keep-hidden { 278 + animation: 0.6s linear overflow-keep-hidden forwards; 279 + } 280 + 281 + @keyframes window-open-scale { 282 + 0% { 283 + scale: 0; 284 + opacity: 0; 285 + } 286 + 20% { 287 + scale: 0; 288 + } 289 + 60% { 290 + opacity: 0.5; 291 + } 292 + 100% { 293 + scale: 1; 294 + opacity: 1; 295 + } 296 + } 297 + 298 + @keyframes window-open-scale-vertical { 299 + 0% { 300 + scale: 1 0; 301 + opacity: 0; 302 + } 303 + 20% { 304 + scale: 1 0; 305 + } 306 + 60% { 307 + opacity: 0.5; 308 + } 309 + 100% { 310 + scale: 1 1; 311 + opacity: 1; 312 + } 313 + } 314 + 315 + @keyframes window-open-scale-horizontal { 316 + 0% { 317 + scale: 0 1; 318 + opacity: 0; 319 + } 320 + 20% { 321 + scale: 0 1; 322 + } 323 + 60% { 324 + opacity: 0.5; 325 + } 326 + 100% { 327 + scale: 1 1; 328 + opacity: 1; 329 + } 330 + } 331 + 332 + @keyframes window-open-move-down { 333 + 0% { 334 + translate: 0 10rem; 335 + opacity: 0; 336 + } 337 + 20% { 338 + translate: 0 10rem; 339 + } 340 + 60% { 341 + opacity: 0.5; 342 + } 343 + 100% { 344 + translate: normal; 345 + opacity: 1; 346 + } 347 + } 348 + 349 + @keyframes window-open-move-up { 350 + 0% { 351 + translate: 0 -10rem; 352 + opacity: 0; 353 + } 354 + 20% { 355 + translate: 0 -10rem; 356 + } 357 + 60% { 358 + opacity: 0.5; 359 + } 360 + 100% { 361 + translate: normal; 362 + opacity: 1; 363 + } 364 + } 365 + 366 + @keyframes window-open-move-left { 367 + 0% { 368 + translate: 10rem 0; 369 + opacity: 0; 370 + } 371 + 20% { 372 + translate: 10rem 0; 373 + } 374 + 60% { 375 + opacity: 0.5; 376 + } 377 + 100% { 378 + translate: normal; 379 + opacity: 1; 380 + } 381 + } 382 + 383 + @keyframes window-open-move-right { 384 + 0% { 385 + translate: -10rem 0; 386 + opacity: 0; 387 + } 388 + 20% { 389 + translate: -10rem 0; 390 + } 391 + 60% { 392 + opacity: 0.5; 393 + } 394 + 100% { 395 + translate: normal; 396 + opacity: 1; 397 + } 398 + } 399 + 400 + @keyframes overflow-keep-hidden { 401 + 0% { 402 + overflow: hidden; 403 + } 404 + 100% { 405 + overflow: auto; 406 + } 407 + } 408 + }
+25
eunomia/src/styles/main.css
··· 1 + @import 'app.css'; 2 + 3 + .entry { 4 + @apply bg-ralsei-green-dark/70 border-ralsei-green-light/30 border-x-[4px] border-y-[5px]; 5 + border-style: ridge; 6 + } 7 + 8 + details { 9 + @apply leading-none mt-2; 10 + summary { 11 + @apply select-none text-shadow-pink text-ralsei-pink-neon hover:underline; 12 + } 13 + summary::marker { 14 + content: '(+) '; 15 + } 16 + } 17 + details[open] summary::marker { 18 + content: '(*) '; 19 + } 20 + 21 + .donate ul { 22 + li span { 23 + @apply font-monospace overflow-hidden text-ellipsis text-nowrap; 24 + } 25 + }
+140
eunomia/src/styles/prism-synthwave84.css
··· 1 + /* 2 + * Synthwave '84 Theme originally by Robb Owen [@Robb0wen] for Visual Studio Code 3 + * Demo: https://marc.dev/demo/prism-synthwave84 4 + * 5 + * Ported for PrismJS by Marc Backes [@themarcba] 6 + */ 7 + 8 + code[class*="language-"], 9 + pre[class*="language-"] { 10 + color: theme(colors.ralsei.pink.neon); 11 + text-shadow: 0 0 2px theme(colors.ralsei.black), 0 0 5px #ff3eb733, 0 0 10px #fff3; 12 + @apply bg-ralsei-green-dark; 13 + @apply font-monospace; 14 + font-size: 1rem; 15 + line-height: 1.4rem; 16 + text-align: left; 17 + white-space: pre; 18 + word-spacing: normal; 19 + word-break: normal; 20 + word-wrap: normal; 21 + 22 + -moz-tab-size: 4; 23 + -o-tab-size: 4; 24 + tab-size: 4; 25 + 26 + -webkit-hyphens: none; 27 + -moz-hyphens: none; 28 + -ms-hyphens: none; 29 + hyphens: none; 30 + } 31 + 32 + /* Code blocks */ 33 + pre[class*="language-"] { 34 + padding: 1em; 35 + margin: .5em 0; 36 + overflow: auto; 37 + } 38 + 39 + /* :not(pre) > code[class*="language-"], 40 + pre[class*="language-"] { 41 + background-color: transparent !important; 42 + background-image: linear-gradient(to bottom, #2a2139 75%, #34294f); 43 + } */ 44 + 45 + /* Inline code */ 46 + :not(pre) > code[class*="language-"] { 47 + padding: .1em; 48 + border-radius: .3em; 49 + white-space: normal; 50 + } 51 + 52 + .token.comment, 53 + .token.block-comment, 54 + .token.prolog, 55 + .token.doctype, 56 + .token.cdata { 57 + color: #8e8e8e; 58 + } 59 + 60 + .token.punctuation { 61 + color: #ccc; 62 + } 63 + 64 + .token.tag, 65 + .token.attr-name, 66 + .token.namespace, 67 + .token.number, 68 + .token.unit, 69 + .token.hexcode, 70 + .token.deleted { 71 + color: #e2777a; 72 + } 73 + 74 + .token.property, 75 + .token.selector { 76 + color: #72f1b8; 77 + text-shadow: 0 0 2px #100c0f, 0 0 10px #257c5575, 0 0 35px #21272475; 78 + } 79 + 80 + .token.function-name { 81 + color: #6196cc; 82 + } 83 + 84 + .token.boolean, 85 + .token.selector .token.id, 86 + .token.function { 87 + color: #fdfdfd; 88 + text-shadow: 0 0 2px #001716, 0 0 3px #03edf975, 0 0 5px #03edf975, 0 0 8px #03edf975; 89 + } 90 + 91 + .token.class-name { 92 + color: #fff5f6; 93 + text-shadow: 0 0 2px #000, 0 0 10px #fc1f2c75, 0 0 5px #fc1f2c75, 0 0 25px #fc1f2c75; 94 + } 95 + 96 + .token.constant, 97 + .token.symbol { 98 + color: #f92aad; 99 + text-shadow: 0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3; 100 + } 101 + 102 + .token.important, 103 + .token.atrule, 104 + .token.keyword, 105 + .token.selector .token.class, 106 + .token.builtin { 107 + color: #f4eee4; 108 + text-shadow: 0 0 2px #393a33, 0 0 8px #f39f0575, 0 0 2px #f39f0575; 109 + } 110 + 111 + .token.string, 112 + .token.char, 113 + .token.attr-value, 114 + .token.regex, 115 + .token.variable { 116 + color: #f87c32; 117 + } 118 + 119 + .token.operator, 120 + .token.entity, 121 + .token.url { 122 + color: #67cdcc; 123 + } 124 + 125 + .token.important, 126 + .token.bold { 127 + font-weight: bold; 128 + } 129 + 130 + .token.italic { 131 + font-style: italic; 132 + } 133 + 134 + .token.entity { 135 + cursor: help; 136 + } 137 + 138 + .token.inserted { 139 + color: green; 140 + }
eunomia/static/88x31.gif

This is a binary file and will not be displayed.

eunomia/static/88x31_midnight.gif

This is a binary file and will not be displayed.

eunomia/static/88x31_sunrise.gif

This is a binary file and will not be displayed.

+1203
eunomia/static/annoy/annoyance-meow.js
··· 1 + let wasm; 2 + 3 + function isLikeNone(x) { 4 + return x === undefined || x === null; 5 + } 6 + 7 + function addToExternrefTable0(obj) { 8 + const idx = wasm.__externref_table_alloc(); 9 + wasm.__wbindgen_export_1.set(idx, obj); 10 + return idx; 11 + } 12 + 13 + const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); 14 + 15 + if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; 16 + 17 + let cachedUint8ArrayMemory0 = null; 18 + 19 + function getUint8ArrayMemory0() { 20 + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { 21 + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); 22 + } 23 + return cachedUint8ArrayMemory0; 24 + } 25 + 26 + function getStringFromWasm0(ptr, len) { 27 + ptr = ptr >>> 0; 28 + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); 29 + } 30 + 31 + function handleError(f, args) { 32 + try { 33 + return f.apply(this, args); 34 + } catch (e) { 35 + const idx = addToExternrefTable0(e); 36 + wasm.__wbindgen_exn_store(idx); 37 + } 38 + } 39 + 40 + let WASM_VECTOR_LEN = 0; 41 + 42 + const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); 43 + 44 + const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 45 + ? function (arg, view) { 46 + return cachedTextEncoder.encodeInto(arg, view); 47 + } 48 + : function (arg, view) { 49 + const buf = cachedTextEncoder.encode(arg); 50 + view.set(buf); 51 + return { 52 + read: arg.length, 53 + written: buf.length 54 + }; 55 + }); 56 + 57 + function passStringToWasm0(arg, malloc, realloc) { 58 + 59 + if (realloc === undefined) { 60 + const buf = cachedTextEncoder.encode(arg); 61 + const ptr = malloc(buf.length, 1) >>> 0; 62 + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); 63 + WASM_VECTOR_LEN = buf.length; 64 + return ptr; 65 + } 66 + 67 + let len = arg.length; 68 + let ptr = malloc(len, 1) >>> 0; 69 + 70 + const mem = getUint8ArrayMemory0(); 71 + 72 + let offset = 0; 73 + 74 + for (; offset < len; offset++) { 75 + const code = arg.charCodeAt(offset); 76 + if (code > 0x7F) break; 77 + mem[ptr + offset] = code; 78 + } 79 + 80 + if (offset !== len) { 81 + if (offset !== 0) { 82 + arg = arg.slice(offset); 83 + } 84 + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; 85 + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); 86 + const ret = encodeString(arg, view); 87 + 88 + offset += ret.written; 89 + ptr = realloc(ptr, len, offset, 1) >>> 0; 90 + } 91 + 92 + WASM_VECTOR_LEN = offset; 93 + return ptr; 94 + } 95 + 96 + let cachedDataViewMemory0 = null; 97 + 98 + function getDataViewMemory0() { 99 + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { 100 + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); 101 + } 102 + return cachedDataViewMemory0; 103 + } 104 + 105 + let cachedUint8ClampedArrayMemory0 = null; 106 + 107 + function getUint8ClampedArrayMemory0() { 108 + if (cachedUint8ClampedArrayMemory0 === null || cachedUint8ClampedArrayMemory0.byteLength === 0) { 109 + cachedUint8ClampedArrayMemory0 = new Uint8ClampedArray(wasm.memory.buffer); 110 + } 111 + return cachedUint8ClampedArrayMemory0; 112 + } 113 + 114 + function getClampedArrayU8FromWasm0(ptr, len) { 115 + ptr = ptr >>> 0; 116 + return getUint8ClampedArrayMemory0().subarray(ptr / 1, ptr / 1 + len); 117 + } 118 + 119 + function getArrayU8FromWasm0(ptr, len) { 120 + ptr = ptr >>> 0; 121 + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); 122 + } 123 + 124 + const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') 125 + ? { register: () => {}, unregister: () => {} } 126 + : new FinalizationRegistry(state => { 127 + wasm.__wbindgen_export_6.get(state.dtor)(state.a, state.b) 128 + }); 129 + 130 + function makeMutClosure(arg0, arg1, dtor, f) { 131 + const state = { a: arg0, b: arg1, cnt: 1, dtor }; 132 + const real = (...args) => { 133 + // First up with a closure we increment the internal reference 134 + // count. This ensures that the Rust closure environment won't 135 + // be deallocated while we're invoking it. 136 + state.cnt++; 137 + const a = state.a; 138 + state.a = 0; 139 + try { 140 + return f(a, state.b, ...args); 141 + } finally { 142 + if (--state.cnt === 0) { 143 + wasm.__wbindgen_export_6.get(state.dtor)(a, state.b); 144 + CLOSURE_DTORS.unregister(state); 145 + } else { 146 + state.a = a; 147 + } 148 + } 149 + }; 150 + real.original = state; 151 + CLOSURE_DTORS.register(real, state, state); 152 + return real; 153 + } 154 + 155 + function debugString(val) { 156 + // primitive types 157 + const type = typeof val; 158 + if (type == 'number' || type == 'boolean' || val == null) { 159 + return `${val}`; 160 + } 161 + if (type == 'string') { 162 + return `"${val}"`; 163 + } 164 + if (type == 'symbol') { 165 + const description = val.description; 166 + if (description == null) { 167 + return 'Symbol'; 168 + } else { 169 + return `Symbol(${description})`; 170 + } 171 + } 172 + if (type == 'function') { 173 + const name = val.name; 174 + if (typeof name == 'string' && name.length > 0) { 175 + return `Function(${name})`; 176 + } else { 177 + return 'Function'; 178 + } 179 + } 180 + // objects 181 + if (Array.isArray(val)) { 182 + const length = val.length; 183 + let debug = '['; 184 + if (length > 0) { 185 + debug += debugString(val[0]); 186 + } 187 + for(let i = 1; i < length; i++) { 188 + debug += ', ' + debugString(val[i]); 189 + } 190 + debug += ']'; 191 + return debug; 192 + } 193 + // Test for built-in 194 + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); 195 + let className; 196 + if (builtInMatches && builtInMatches.length > 1) { 197 + className = builtInMatches[1]; 198 + } else { 199 + // Failed to match the standard '[object ClassName]' 200 + return toString.call(val); 201 + } 202 + if (className == 'Object') { 203 + // we're a user defined class or Object 204 + // JSON.stringify avoids problems with cycles, and is generally much 205 + // easier than looping through ownProperties of `val`. 206 + try { 207 + return 'Object(' + JSON.stringify(val) + ')'; 208 + } catch (_) { 209 + return 'Object'; 210 + } 211 + } 212 + // errors 213 + if (val instanceof Error) { 214 + return `${val.name}: ${val.message}\n${val.stack}`; 215 + } 216 + // TODO we could test for more things here, like `Set`s and `Map`s. 217 + return className; 218 + } 219 + function __wbg_adapter_30(arg0, arg1, arg2) { 220 + wasm.closure3_externref_shim(arg0, arg1, arg2); 221 + } 222 + 223 + function __wbg_adapter_51(arg0, arg1, arg2, arg3) { 224 + wasm.closure105_externref_shim(arg0, arg1, arg2, arg3); 225 + } 226 + 227 + function __wbg_adapter_56(arg0, arg1) { 228 + wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb7d4ca77f39188b4(arg0, arg1); 229 + } 230 + 231 + const __wbindgen_enum_BinaryType = ["blob", "arraybuffer"]; 232 + 233 + const __wbindgen_enum_ResizeObserverBoxOptions = ["border-box", "content-box", "device-pixel-content-box"]; 234 + 235 + const __wbindgen_enum_VisibilityState = ["hidden", "visible"]; 236 + 237 + async function __wbg_load(module, imports) { 238 + if (typeof Response === 'function' && module instanceof Response) { 239 + if (typeof WebAssembly.instantiateStreaming === 'function') { 240 + try { 241 + return await WebAssembly.instantiateStreaming(module, imports); 242 + 243 + } catch (e) { 244 + if (module.headers.get('Content-Type') != 'application/wasm') { 245 + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 246 + 247 + } else { 248 + throw e; 249 + } 250 + } 251 + } 252 + 253 + const bytes = await module.arrayBuffer(); 254 + return await WebAssembly.instantiate(bytes, imports); 255 + 256 + } else { 257 + const instance = await WebAssembly.instantiate(module, imports); 258 + 259 + if (instance instanceof WebAssembly.Instance) { 260 + return { instance, module }; 261 + 262 + } else { 263 + return instance; 264 + } 265 + } 266 + } 267 + 268 + function __wbg_get_imports() { 269 + const imports = {}; 270 + imports.wbg = {}; 271 + imports.wbg.__wbg_Window_d1bf622f71ff0629 = function(arg0) { 272 + const ret = arg0.Window; 273 + return ret; 274 + }; 275 + imports.wbg.__wbg_abort_775ef1d17fc65868 = function(arg0) { 276 + arg0.abort(); 277 + }; 278 + imports.wbg.__wbg_activeElement_367599fdfa7ad115 = function(arg0) { 279 + const ret = arg0.activeElement; 280 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 281 + }; 282 + imports.wbg.__wbg_addEventListener_90e553fdce254421 = function() { return handleError(function (arg0, arg1, arg2, arg3) { 283 + arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3); 284 + }, arguments) }; 285 + imports.wbg.__wbg_addListener_2982bb811b6385c5 = function() { return handleError(function (arg0, arg1) { 286 + arg0.addListener(arg1); 287 + }, arguments) }; 288 + imports.wbg.__wbg_altKey_c33c03aed82e4275 = function(arg0) { 289 + const ret = arg0.altKey; 290 + return ret; 291 + }; 292 + imports.wbg.__wbg_altKey_d7495666df921121 = function(arg0) { 293 + const ret = arg0.altKey; 294 + return ret; 295 + }; 296 + imports.wbg.__wbg_animate_6ec571f163cf6f8d = function(arg0, arg1, arg2) { 297 + const ret = arg0.animate(arg1, arg2); 298 + return ret; 299 + }; 300 + imports.wbg.__wbg_appendChild_8204974b7328bf98 = function() { return handleError(function (arg0, arg1) { 301 + const ret = arg0.appendChild(arg1); 302 + return ret; 303 + }, arguments) }; 304 + imports.wbg.__wbg_blockSize_1490803190b57a34 = function(arg0) { 305 + const ret = arg0.blockSize; 306 + return ret; 307 + }; 308 + imports.wbg.__wbg_body_942ea927546a04ba = function(arg0) { 309 + const ret = arg0.body; 310 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 311 + }; 312 + imports.wbg.__wbg_brand_9562792cbb4735c3 = function(arg0, arg1) { 313 + const ret = arg1.brand; 314 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 315 + const len1 = WASM_VECTOR_LEN; 316 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 317 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 318 + }; 319 + imports.wbg.__wbg_brands_a1e7a2bce052128f = function(arg0) { 320 + const ret = arg0.brands; 321 + return ret; 322 + }; 323 + imports.wbg.__wbg_buffer_609cc3eee51ed158 = function(arg0) { 324 + const ret = arg0.buffer; 325 + return ret; 326 + }; 327 + imports.wbg.__wbg_button_f75c56aec440ea04 = function(arg0) { 328 + const ret = arg0.button; 329 + return ret; 330 + }; 331 + imports.wbg.__wbg_buttons_b6346af6f04e4686 = function(arg0) { 332 + const ret = arg0.buttons; 333 + return ret; 334 + }; 335 + imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) { 336 + const ret = arg0.call(arg1); 337 + return ret; 338 + }, arguments) }; 339 + imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) { 340 + const ret = arg0.call(arg1, arg2); 341 + return ret; 342 + }, arguments) }; 343 + imports.wbg.__wbg_cancelAnimationFrame_089b48301c362fde = function() { return handleError(function (arg0, arg1) { 344 + arg0.cancelAnimationFrame(arg1); 345 + }, arguments) }; 346 + imports.wbg.__wbg_cancelIdleCallback_669eb1ed294c8b8b = function(arg0, arg1) { 347 + arg0.cancelIdleCallback(arg1 >>> 0); 348 + }; 349 + imports.wbg.__wbg_cancel_09c394f0894744eb = function(arg0) { 350 + arg0.cancel(); 351 + }; 352 + imports.wbg.__wbg_catch_a6e601879b2610e9 = function(arg0, arg1) { 353 + const ret = arg0.catch(arg1); 354 + return ret; 355 + }; 356 + imports.wbg.__wbg_clearTimeout_b2651b7485c58446 = function(arg0, arg1) { 357 + arg0.clearTimeout(arg1); 358 + }; 359 + imports.wbg.__wbg_clientHeight_216178c194000db4 = function(arg0) { 360 + const ret = arg0.clientHeight; 361 + return ret; 362 + }; 363 + imports.wbg.__wbg_clientWidth_ce67a04dc15fce39 = function(arg0) { 364 + const ret = arg0.clientWidth; 365 + return ret; 366 + }; 367 + imports.wbg.__wbg_close_2893b7d056a0627d = function() { return handleError(function (arg0) { 368 + arg0.close(); 369 + }, arguments) }; 370 + imports.wbg.__wbg_close_414b379454494b29 = function(arg0) { 371 + arg0.close(); 372 + }; 373 + imports.wbg.__wbg_close_e1253d480ed93ce3 = function() { return handleError(function (arg0, arg1, arg2, arg3) { 374 + arg0.close(arg1, getStringFromWasm0(arg2, arg3)); 375 + }, arguments) }; 376 + imports.wbg.__wbg_code_459c120478e1ab6e = function(arg0, arg1) { 377 + const ret = arg1.code; 378 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 379 + const len1 = WASM_VECTOR_LEN; 380 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 381 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 382 + }; 383 + imports.wbg.__wbg_code_f4ec1e6e2e1b0417 = function(arg0) { 384 + const ret = arg0.code; 385 + return ret; 386 + }; 387 + imports.wbg.__wbg_contains_3361c7eda6c95afd = function(arg0, arg1) { 388 + const ret = arg0.contains(arg1); 389 + return ret; 390 + }; 391 + imports.wbg.__wbg_contentRect_81407eb60e52248f = function(arg0) { 392 + const ret = arg0.contentRect; 393 + return ret; 394 + }; 395 + imports.wbg.__wbg_createElement_8c9931a732ee2fea = function() { return handleError(function (arg0, arg1, arg2) { 396 + const ret = arg0.createElement(getStringFromWasm0(arg1, arg2)); 397 + return ret; 398 + }, arguments) }; 399 + imports.wbg.__wbg_createObjectURL_6e98d2f9c7bd9764 = function() { return handleError(function (arg0, arg1) { 400 + const ret = URL.createObjectURL(arg1); 401 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 402 + const len1 = WASM_VECTOR_LEN; 403 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 404 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 405 + }, arguments) }; 406 + imports.wbg.__wbg_crypto_574e78ad8b13b65f = function(arg0) { 407 + const ret = arg0.crypto; 408 + return ret; 409 + }; 410 + imports.wbg.__wbg_ctrlKey_1e826e468105ac11 = function(arg0) { 411 + const ret = arg0.ctrlKey; 412 + return ret; 413 + }; 414 + imports.wbg.__wbg_ctrlKey_cdbe8154dfb00d1f = function(arg0) { 415 + const ret = arg0.ctrlKey; 416 + return ret; 417 + }; 418 + imports.wbg.__wbg_data_432d9c3df2630942 = function(arg0) { 419 + const ret = arg0.data; 420 + return ret; 421 + }; 422 + imports.wbg.__wbg_deltaMode_9bfd9fe3f6b4b240 = function(arg0) { 423 + const ret = arg0.deltaMode; 424 + return ret; 425 + }; 426 + imports.wbg.__wbg_deltaX_5c1121715746e4b7 = function(arg0) { 427 + const ret = arg0.deltaX; 428 + return ret; 429 + }; 430 + imports.wbg.__wbg_deltaY_f9318542caea0c36 = function(arg0) { 431 + const ret = arg0.deltaY; 432 + return ret; 433 + }; 434 + imports.wbg.__wbg_devicePixelContentBoxSize_a6de82cb30d70825 = function(arg0) { 435 + const ret = arg0.devicePixelContentBoxSize; 436 + return ret; 437 + }; 438 + imports.wbg.__wbg_devicePixelRatio_68c391265f05d093 = function(arg0) { 439 + const ret = arg0.devicePixelRatio; 440 + return ret; 441 + }; 442 + imports.wbg.__wbg_disconnect_2118016d75479985 = function(arg0) { 443 + arg0.disconnect(); 444 + }; 445 + imports.wbg.__wbg_disconnect_ac3f4ba550970c76 = function(arg0) { 446 + arg0.disconnect(); 447 + }; 448 + imports.wbg.__wbg_document_d249400bd7bd996d = function(arg0) { 449 + const ret = arg0.document; 450 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 451 + }; 452 + imports.wbg.__wbg_error_1004b8c64097413f = function(arg0, arg1) { 453 + console.error(arg0, arg1); 454 + }; 455 + imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) { 456 + let deferred0_0; 457 + let deferred0_1; 458 + try { 459 + deferred0_0 = arg0; 460 + deferred0_1 = arg1; 461 + console.error(getStringFromWasm0(arg0, arg1)); 462 + } finally { 463 + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); 464 + } 465 + }; 466 + imports.wbg.__wbg_focus_7d08b55eba7b368d = function() { return handleError(function (arg0) { 467 + arg0.focus(); 468 + }, arguments) }; 469 + imports.wbg.__wbg_fullscreenElement_a2f691b04c3b3de5 = function(arg0) { 470 + const ret = arg0.fullscreenElement; 471 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 472 + }; 473 + imports.wbg.__wbg_getCoalescedEvents_21492912fd0145ec = function(arg0) { 474 + const ret = arg0.getCoalescedEvents; 475 + return ret; 476 + }; 477 + imports.wbg.__wbg_getCoalescedEvents_a7d49de30111f6b8 = function(arg0) { 478 + const ret = arg0.getCoalescedEvents(); 479 + return ret; 480 + }; 481 + imports.wbg.__wbg_getComputedStyle_046dd6472f8e7f1d = function() { return handleError(function (arg0, arg1) { 482 + const ret = arg0.getComputedStyle(arg1); 483 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 484 + }, arguments) }; 485 + imports.wbg.__wbg_getContext_e9cf379449413580 = function() { return handleError(function (arg0, arg1, arg2) { 486 + const ret = arg0.getContext(getStringFromWasm0(arg1, arg2)); 487 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 488 + }, arguments) }; 489 + imports.wbg.__wbg_getOwnPropertyDescriptor_9dd936a3c0cbd368 = function(arg0, arg1) { 490 + const ret = Object.getOwnPropertyDescriptor(arg0, arg1); 491 + return ret; 492 + }; 493 + imports.wbg.__wbg_getPropertyValue_e623c23a05dfb30c = function() { return handleError(function (arg0, arg1, arg2, arg3) { 494 + const ret = arg1.getPropertyValue(getStringFromWasm0(arg2, arg3)); 495 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 496 + const len1 = WASM_VECTOR_LEN; 497 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 498 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 499 + }, arguments) }; 500 + imports.wbg.__wbg_getRandomValues_b8f5dbd5f3995a9e = function() { return handleError(function (arg0, arg1) { 501 + arg0.getRandomValues(arg1); 502 + }, arguments) }; 503 + imports.wbg.__wbg_get_67b2ba62fc30de12 = function() { return handleError(function (arg0, arg1) { 504 + const ret = Reflect.get(arg0, arg1); 505 + return ret; 506 + }, arguments) }; 507 + imports.wbg.__wbg_get_b9b93047fe3cf45b = function(arg0, arg1) { 508 + const ret = arg0[arg1 >>> 0]; 509 + return ret; 510 + }; 511 + imports.wbg.__wbg_height_1f8226c8f6875110 = function(arg0) { 512 + const ret = arg0.height; 513 + return ret; 514 + }; 515 + imports.wbg.__wbg_inlineSize_8ff96b3ec1b24423 = function(arg0) { 516 + const ret = arg0.inlineSize; 517 + return ret; 518 + }; 519 + imports.wbg.__wbg_instanceof_ArrayBuffer_e14585432e3737fc = function(arg0) { 520 + let result; 521 + try { 522 + result = arg0 instanceof ArrayBuffer; 523 + } catch (_) { 524 + result = false; 525 + } 526 + const ret = result; 527 + return ret; 528 + }; 529 + imports.wbg.__wbg_instanceof_Blob_ca721ef3bdab15d1 = function(arg0) { 530 + let result; 531 + try { 532 + result = arg0 instanceof Blob; 533 + } catch (_) { 534 + result = false; 535 + } 536 + const ret = result; 537 + return ret; 538 + }; 539 + imports.wbg.__wbg_instanceof_CanvasRenderingContext2d_df82a4d3437bf1cc = function(arg0) { 540 + let result; 541 + try { 542 + result = arg0 instanceof CanvasRenderingContext2D; 543 + } catch (_) { 544 + result = false; 545 + } 546 + const ret = result; 547 + return ret; 548 + }; 549 + imports.wbg.__wbg_instanceof_Performance_0ac1286c87171f57 = function(arg0) { 550 + let result; 551 + try { 552 + result = arg0 instanceof Performance; 553 + } catch (_) { 554 + result = false; 555 + } 556 + const ret = result; 557 + return ret; 558 + }; 559 + imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) { 560 + let result; 561 + try { 562 + result = arg0 instanceof Window; 563 + } catch (_) { 564 + result = false; 565 + } 566 + const ret = result; 567 + return ret; 568 + }; 569 + imports.wbg.__wbg_isIntersecting_e68706dac9c5f2e9 = function(arg0) { 570 + const ret = arg0.isIntersecting; 571 + return ret; 572 + }; 573 + imports.wbg.__wbg_is_c7481c65e7e5df9e = function(arg0, arg1) { 574 + const ret = Object.is(arg0, arg1); 575 + return ret; 576 + }; 577 + imports.wbg.__wbg_key_7b5c6cb539be8e13 = function(arg0, arg1) { 578 + const ret = arg1.key; 579 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 580 + const len1 = WASM_VECTOR_LEN; 581 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 582 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 583 + }; 584 + imports.wbg.__wbg_length_a446193dc22c12f8 = function(arg0) { 585 + const ret = arg0.length; 586 + return ret; 587 + }; 588 + imports.wbg.__wbg_length_e2d2a49132c1b256 = function(arg0) { 589 + const ret = arg0.length; 590 + return ret; 591 + }; 592 + imports.wbg.__wbg_location_9b435486be8f98c2 = function(arg0) { 593 + const ret = arg0.location; 594 + return ret; 595 + }; 596 + imports.wbg.__wbg_matchMedia_bf8807a841d930c1 = function() { return handleError(function (arg0, arg1, arg2) { 597 + const ret = arg0.matchMedia(getStringFromWasm0(arg1, arg2)); 598 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 599 + }, arguments) }; 600 + imports.wbg.__wbg_matches_e9ca73fbf8a3a104 = function(arg0) { 601 + const ret = arg0.matches; 602 + return ret; 603 + }; 604 + imports.wbg.__wbg_metaKey_0b25f7848e014cc8 = function(arg0) { 605 + const ret = arg0.metaKey; 606 + return ret; 607 + }; 608 + imports.wbg.__wbg_metaKey_e1dd47d709a80ce5 = function(arg0) { 609 + const ret = arg0.metaKey; 610 + return ret; 611 + }; 612 + imports.wbg.__wbg_movementX_1aa05f864931369b = function(arg0) { 613 + const ret = arg0.movementX; 614 + return ret; 615 + }; 616 + imports.wbg.__wbg_movementY_8acfedb38a70e624 = function(arg0) { 617 + const ret = arg0.movementY; 618 + return ret; 619 + }; 620 + imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) { 621 + const ret = arg0.msCrypto; 622 + return ret; 623 + }; 624 + imports.wbg.__wbg_navigator_1577371c070c8947 = function(arg0) { 625 + const ret = arg0.navigator; 626 + return ret; 627 + }; 628 + imports.wbg.__wbg_new_18b1151f3a6a9280 = function() { return handleError(function (arg0) { 629 + const ret = new IntersectionObserver(arg0); 630 + return ret; 631 + }, arguments) }; 632 + imports.wbg.__wbg_new_24b2c5b645cded8d = function() { return handleError(function () { 633 + const ret = new MessageChannel(); 634 + return ret; 635 + }, arguments) }; 636 + imports.wbg.__wbg_new_405e22f390576ce2 = function() { 637 + const ret = new Object(); 638 + return ret; 639 + }; 640 + imports.wbg.__wbg_new_5f34cc0c99fcc488 = function() { return handleError(function (arg0) { 641 + const ret = new ResizeObserver(arg0); 642 + return ret; 643 + }, arguments) }; 644 + imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { 645 + const ret = new Error(); 646 + return ret; 647 + }; 648 + imports.wbg.__wbg_new_92c54fc74574ef55 = function() { return handleError(function (arg0, arg1) { 649 + const ret = new WebSocket(getStringFromWasm0(arg0, arg1)); 650 + return ret; 651 + }, arguments) }; 652 + imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) { 653 + const ret = new Uint8Array(arg0); 654 + return ret; 655 + }; 656 + imports.wbg.__wbg_new_b1a33e5095abf678 = function() { return handleError(function (arg0, arg1) { 657 + const ret = new Worker(getStringFromWasm0(arg0, arg1)); 658 + return ret; 659 + }, arguments) }; 660 + imports.wbg.__wbg_new_e25e5aab09ff45db = function() { return handleError(function () { 661 + const ret = new AbortController(); 662 + return ret; 663 + }, arguments) }; 664 + imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) { 665 + const ret = new Function(getStringFromWasm0(arg0, arg1)); 666 + return ret; 667 + }; 668 + imports.wbg.__wbg_newwithbyteoffsetandlength_d97e637ebe145a9a = function(arg0, arg1, arg2) { 669 + const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); 670 + return ret; 671 + }; 672 + imports.wbg.__wbg_newwithlength_a381634e90c276d4 = function(arg0) { 673 + const ret = new Uint8Array(arg0 >>> 0); 674 + return ret; 675 + }; 676 + imports.wbg.__wbg_newwithstrsequenceandoptions_aaff55b467c81b63 = function() { return handleError(function (arg0, arg1) { 677 + const ret = new Blob(arg0, arg1); 678 + return ret; 679 + }, arguments) }; 680 + imports.wbg.__wbg_newwithu8clampedarray_0fcf78a036c89a97 = function() { return handleError(function (arg0, arg1, arg2) { 681 + const ret = new ImageData(getClampedArrayU8FromWasm0(arg0, arg1), arg2 >>> 0); 682 + return ret; 683 + }, arguments) }; 684 + imports.wbg.__wbg_node_905d3e251edff8a2 = function(arg0) { 685 + const ret = arg0.node; 686 + return ret; 687 + }; 688 + imports.wbg.__wbg_now_2c95c9de01293173 = function(arg0) { 689 + const ret = arg0.now(); 690 + return ret; 691 + }; 692 + imports.wbg.__wbg_now_d18023d54d4e5500 = function(arg0) { 693 + const ret = arg0.now(); 694 + return ret; 695 + }; 696 + imports.wbg.__wbg_observe_d2e7378f15f7ca72 = function(arg0, arg1) { 697 + arg0.observe(arg1); 698 + }; 699 + imports.wbg.__wbg_observe_eafddfc5a0c60e02 = function(arg0, arg1) { 700 + arg0.observe(arg1); 701 + }; 702 + imports.wbg.__wbg_observe_ed4adb1c245103c5 = function(arg0, arg1, arg2) { 703 + arg0.observe(arg1, arg2); 704 + }; 705 + imports.wbg.__wbg_of_2eaf5a02d443ef03 = function(arg0) { 706 + const ret = Array.of(arg0); 707 + return ret; 708 + }; 709 + imports.wbg.__wbg_of_66b3ee656cbd962b = function(arg0, arg1) { 710 + const ret = Array.of(arg0, arg1); 711 + return ret; 712 + }; 713 + imports.wbg.__wbg_offsetX_cb6a38e6f23cb4a6 = function(arg0) { 714 + const ret = arg0.offsetX; 715 + return ret; 716 + }; 717 + imports.wbg.__wbg_offsetY_43e21941c5c1f8bf = function(arg0) { 718 + const ret = arg0.offsetY; 719 + return ret; 720 + }; 721 + imports.wbg.__wbg_performance_7a3ffd0b17f663ad = function(arg0) { 722 + const ret = arg0.performance; 723 + return ret; 724 + }; 725 + imports.wbg.__wbg_persisted_d32ce73b8e522062 = function(arg0) { 726 + const ret = arg0.persisted; 727 + return ret; 728 + }; 729 + imports.wbg.__wbg_play_63bc12f42e16af91 = function(arg0) { 730 + arg0.play(); 731 + }; 732 + imports.wbg.__wbg_pointerId_585e63ee80a49927 = function(arg0) { 733 + const ret = arg0.pointerId; 734 + return ret; 735 + }; 736 + imports.wbg.__wbg_pointerType_6bd934aa20d9db49 = function(arg0, arg1) { 737 + const ret = arg1.pointerType; 738 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 739 + const len1 = WASM_VECTOR_LEN; 740 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 741 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 742 + }; 743 + imports.wbg.__wbg_port1_70af0ea6e4a96f9d = function(arg0) { 744 + const ret = arg0.port1; 745 + return ret; 746 + }; 747 + imports.wbg.__wbg_port2_0584c7f0938b6fe6 = function(arg0) { 748 + const ret = arg0.port2; 749 + return ret; 750 + }; 751 + imports.wbg.__wbg_postMessage_e55d059efb191dc5 = function() { return handleError(function (arg0, arg1) { 752 + arg0.postMessage(arg1); 753 + }, arguments) }; 754 + imports.wbg.__wbg_postMessage_f961e53b9731ca83 = function() { return handleError(function (arg0, arg1, arg2) { 755 + arg0.postMessage(arg1, arg2); 756 + }, arguments) }; 757 + imports.wbg.__wbg_postTask_41d93e93941e4a3d = function(arg0, arg1, arg2) { 758 + const ret = arg0.postTask(arg1, arg2); 759 + return ret; 760 + }; 761 + imports.wbg.__wbg_pressure_adda5a83a9cec94d = function(arg0) { 762 + const ret = arg0.pressure; 763 + return ret; 764 + }; 765 + imports.wbg.__wbg_preventDefault_c2314fd813c02b3c = function(arg0) { 766 + arg0.preventDefault(); 767 + }; 768 + imports.wbg.__wbg_process_dc0fbacc7c1c06f7 = function(arg0) { 769 + const ret = arg0.process; 770 + return ret; 771 + }; 772 + imports.wbg.__wbg_prototype_c28bca39c45aba9b = function() { 773 + const ret = ResizeObserverEntry.prototype; 774 + return ret; 775 + }; 776 + imports.wbg.__wbg_putImageData_2acd1dcd88a80f79 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { 777 + arg0.putImageData(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 778 + }, arguments) }; 779 + imports.wbg.__wbg_putImageData_416b1a6d50843b66 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { 780 + arg0.putImageData(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 781 + }, arguments) }; 782 + imports.wbg.__wbg_queueMicrotask_65a6c48ee9790d40 = function(arg0, arg1) { 783 + arg0.queueMicrotask(arg1); 784 + }; 785 + imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) { 786 + queueMicrotask(arg0); 787 + }; 788 + imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) { 789 + const ret = arg0.queueMicrotask; 790 + return ret; 791 + }; 792 + imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) { 793 + arg0.randomFillSync(arg1); 794 + }, arguments) }; 795 + imports.wbg.__wbg_readyState_7ef6e63c349899ed = function(arg0) { 796 + const ret = arg0.readyState; 797 + return ret; 798 + }; 799 + imports.wbg.__wbg_reason_49f1cede8bcf23dd = function(arg0, arg1) { 800 + const ret = arg1.reason; 801 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 802 + const len1 = WASM_VECTOR_LEN; 803 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 804 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 805 + }; 806 + imports.wbg.__wbg_removeEventListener_056dfe8c3d6c58f9 = function() { return handleError(function (arg0, arg1, arg2, arg3) { 807 + arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3); 808 + }, arguments) }; 809 + imports.wbg.__wbg_removeListener_e55db581b73ccf65 = function() { return handleError(function (arg0, arg1) { 810 + arg0.removeListener(arg1); 811 + }, arguments) }; 812 + imports.wbg.__wbg_removeProperty_0e85471f4dfc00ae = function() { return handleError(function (arg0, arg1, arg2, arg3) { 813 + const ret = arg1.removeProperty(getStringFromWasm0(arg2, arg3)); 814 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 815 + const len1 = WASM_VECTOR_LEN; 816 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 817 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 818 + }, arguments) }; 819 + imports.wbg.__wbg_repeat_1882aa0d0072c705 = function(arg0) { 820 + const ret = arg0.repeat; 821 + return ret; 822 + }; 823 + imports.wbg.__wbg_requestAnimationFrame_d7fd890aaefc3246 = function() { return handleError(function (arg0, arg1) { 824 + const ret = arg0.requestAnimationFrame(arg1); 825 + return ret; 826 + }, arguments) }; 827 + imports.wbg.__wbg_requestFullscreen_86fc6cdb76000482 = function(arg0) { 828 + const ret = arg0.requestFullscreen; 829 + return ret; 830 + }; 831 + imports.wbg.__wbg_requestFullscreen_9f0611438eb929cf = function(arg0) { 832 + const ret = arg0.requestFullscreen(); 833 + return ret; 834 + }; 835 + imports.wbg.__wbg_requestIdleCallback_1b8d644ff564208f = function(arg0) { 836 + const ret = arg0.requestIdleCallback; 837 + return ret; 838 + }; 839 + imports.wbg.__wbg_requestIdleCallback_e3eefd34962470e1 = function() { return handleError(function (arg0, arg1) { 840 + const ret = arg0.requestIdleCallback(arg1); 841 + return ret; 842 + }, arguments) }; 843 + imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () { 844 + const ret = module.require; 845 + return ret; 846 + }, arguments) }; 847 + imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) { 848 + const ret = Promise.resolve(arg0); 849 + return ret; 850 + }; 851 + imports.wbg.__wbg_revokeObjectURL_27267efebeb457c7 = function() { return handleError(function (arg0, arg1) { 852 + URL.revokeObjectURL(getStringFromWasm0(arg0, arg1)); 853 + }, arguments) }; 854 + imports.wbg.__wbg_scheduler_48482a9974eeacbd = function(arg0) { 855 + const ret = arg0.scheduler; 856 + return ret; 857 + }; 858 + imports.wbg.__wbg_scheduler_5156bb61cc1cf589 = function(arg0) { 859 + const ret = arg0.scheduler; 860 + return ret; 861 + }; 862 + imports.wbg.__wbg_send_0293179ba074ffb4 = function() { return handleError(function (arg0, arg1, arg2) { 863 + arg0.send(getStringFromWasm0(arg1, arg2)); 864 + }, arguments) }; 865 + imports.wbg.__wbg_send_fc0c204e8a1757f4 = function() { return handleError(function (arg0, arg1, arg2) { 866 + arg0.send(getArrayU8FromWasm0(arg1, arg2)); 867 + }, arguments) }; 868 + imports.wbg.__wbg_setAttribute_2704501201f15687 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 869 + arg0.setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 870 + }, arguments) }; 871 + imports.wbg.__wbg_setPointerCapture_c04dafaf4d00ffad = function() { return handleError(function (arg0, arg1) { 872 + arg0.setPointerCapture(arg1); 873 + }, arguments) }; 874 + imports.wbg.__wbg_setProperty_f2cf326652b9a713 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 875 + arg0.setProperty(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 876 + }, arguments) }; 877 + imports.wbg.__wbg_setTimeout_461fec76662b35ea = function() { return handleError(function (arg0, arg1) { 878 + const ret = arg0.setTimeout(arg1); 879 + return ret; 880 + }, arguments) }; 881 + imports.wbg.__wbg_setTimeout_f2fe5af8e3debeb3 = function() { return handleError(function (arg0, arg1, arg2) { 882 + const ret = arg0.setTimeout(arg1, arg2); 883 + return ret; 884 + }, arguments) }; 885 + imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) { 886 + arg0.set(arg1, arg2 >>> 0); 887 + }; 888 + imports.wbg.__wbg_set_bb8cecf6a62b9f46 = function() { return handleError(function (arg0, arg1, arg2) { 889 + const ret = Reflect.set(arg0, arg1, arg2); 890 + return ret; 891 + }, arguments) }; 892 + imports.wbg.__wbg_setbinaryType_92fa1ffd873b327c = function(arg0, arg1) { 893 + arg0.binaryType = __wbindgen_enum_BinaryType[arg1]; 894 + }; 895 + imports.wbg.__wbg_setbox_2786f3ccea97cac4 = function(arg0, arg1) { 896 + arg0.box = __wbindgen_enum_ResizeObserverBoxOptions[arg1]; 897 + }; 898 + imports.wbg.__wbg_setheight_433680330c9420c3 = function(arg0, arg1) { 899 + arg0.height = arg1 >>> 0; 900 + }; 901 + imports.wbg.__wbg_setheight_da683a33fa99843c = function(arg0, arg1) { 902 + arg0.height = arg1 >>> 0; 903 + }; 904 + imports.wbg.__wbg_setonclose_14fc475a49d488fc = function(arg0, arg1) { 905 + arg0.onclose = arg1; 906 + }; 907 + imports.wbg.__wbg_setonerror_8639efe354b947cd = function(arg0, arg1) { 908 + arg0.onerror = arg1; 909 + }; 910 + imports.wbg.__wbg_setonmessage_23d122da701b8ddb = function(arg0, arg1) { 911 + arg0.onmessage = arg1; 912 + }; 913 + imports.wbg.__wbg_setonmessage_6eccab530a8fb4c7 = function(arg0, arg1) { 914 + arg0.onmessage = arg1; 915 + }; 916 + imports.wbg.__wbg_setonopen_2da654e1f39745d5 = function(arg0, arg1) { 917 + arg0.onopen = arg1; 918 + }; 919 + imports.wbg.__wbg_settype_39ed370d3edd403c = function(arg0, arg1, arg2) { 920 + arg0.type = getStringFromWasm0(arg1, arg2); 921 + }; 922 + imports.wbg.__wbg_setwidth_660ca581e3fbe279 = function(arg0, arg1) { 923 + arg0.width = arg1 >>> 0; 924 + }; 925 + imports.wbg.__wbg_setwidth_c5fed9f5e7f0b406 = function(arg0, arg1) { 926 + arg0.width = arg1 >>> 0; 927 + }; 928 + imports.wbg.__wbg_shiftKey_2bebb3b703254f47 = function(arg0) { 929 + const ret = arg0.shiftKey; 930 + return ret; 931 + }; 932 + imports.wbg.__wbg_shiftKey_86e737105bab1a54 = function(arg0) { 933 + const ret = arg0.shiftKey; 934 + return ret; 935 + }; 936 + imports.wbg.__wbg_signal_aaf9ad74119f20a4 = function(arg0) { 937 + const ret = arg0.signal; 938 + return ret; 939 + }; 940 + imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) { 941 + const ret = arg1.stack; 942 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 943 + const len1 = WASM_VECTOR_LEN; 944 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 945 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 946 + }; 947 + imports.wbg.__wbg_start_2c099369ce831bf1 = function(arg0) { 948 + arg0.start(); 949 + }; 950 + imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { 951 + const ret = typeof global === 'undefined' ? null : global; 952 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 953 + }; 954 + imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { 955 + const ret = typeof globalThis === 'undefined' ? null : globalThis; 956 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 957 + }; 958 + imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { 959 + const ret = typeof self === 'undefined' ? null : self; 960 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 961 + }; 962 + imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { 963 + const ret = typeof window === 'undefined' ? null : window; 964 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 965 + }; 966 + imports.wbg.__wbg_style_fb30c14e5815805c = function(arg0) { 967 + const ret = arg0.style; 968 + return ret; 969 + }; 970 + imports.wbg.__wbg_subarray_aa9065fa9dc5df96 = function(arg0, arg1, arg2) { 971 + const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0); 972 + return ret; 973 + }; 974 + imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) { 975 + const ret = arg0.then(arg1); 976 + return ret; 977 + }; 978 + imports.wbg.__wbg_unobserve_02f53d1ca2d1d801 = function(arg0, arg1) { 979 + arg0.unobserve(arg1); 980 + }; 981 + imports.wbg.__wbg_userAgentData_f7b0e61c05c54315 = function(arg0) { 982 + const ret = arg0.userAgentData; 983 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 984 + }; 985 + imports.wbg.__wbg_userAgent_12e9d8e62297563f = function() { return handleError(function (arg0, arg1) { 986 + const ret = arg1.userAgent; 987 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 988 + const len1 = WASM_VECTOR_LEN; 989 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 990 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 991 + }, arguments) }; 992 + imports.wbg.__wbg_versions_c01dfd4722a88165 = function(arg0) { 993 + const ret = arg0.versions; 994 + return ret; 995 + }; 996 + imports.wbg.__wbg_visibilityState_f3cc18a6f3831137 = function(arg0) { 997 + const ret = arg0.visibilityState; 998 + return (__wbindgen_enum_VisibilityState.indexOf(ret) + 1 || 3) - 1; 999 + }; 1000 + imports.wbg.__wbg_webkitFullscreenElement_a9ca38b7214d1567 = function(arg0) { 1001 + const ret = arg0.webkitFullscreenElement; 1002 + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 1003 + }; 1004 + imports.wbg.__wbg_webkitRequestFullscreen_23664c63833ff0e5 = function(arg0) { 1005 + arg0.webkitRequestFullscreen(); 1006 + }; 1007 + imports.wbg.__wbg_width_cdaf02311c1621d1 = function(arg0) { 1008 + const ret = arg0.width; 1009 + return ret; 1010 + }; 1011 + imports.wbg.__wbindgen_cb_drop = function(arg0) { 1012 + const obj = arg0.original; 1013 + if (obj.cnt-- == 1) { 1014 + obj.a = 0; 1015 + return true; 1016 + } 1017 + const ret = false; 1018 + return ret; 1019 + }; 1020 + imports.wbg.__wbindgen_closure_wrapper1511 = function(arg0, arg1, arg2) { 1021 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1022 + return ret; 1023 + }; 1024 + imports.wbg.__wbindgen_closure_wrapper207 = function(arg0, arg1, arg2) { 1025 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1026 + return ret; 1027 + }; 1028 + imports.wbg.__wbindgen_closure_wrapper209 = function(arg0, arg1, arg2) { 1029 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1030 + return ret; 1031 + }; 1032 + imports.wbg.__wbindgen_closure_wrapper211 = function(arg0, arg1, arg2) { 1033 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1034 + return ret; 1035 + }; 1036 + imports.wbg.__wbindgen_closure_wrapper2187 = function(arg0, arg1, arg2) { 1037 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1038 + return ret; 1039 + }; 1040 + imports.wbg.__wbindgen_closure_wrapper2189 = function(arg0, arg1, arg2) { 1041 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_51); 1042 + return ret; 1043 + }; 1044 + imports.wbg.__wbindgen_closure_wrapper2194 = function(arg0, arg1, arg2) { 1045 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1046 + return ret; 1047 + }; 1048 + imports.wbg.__wbindgen_closure_wrapper2197 = function(arg0, arg1, arg2) { 1049 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_56); 1050 + return ret; 1051 + }; 1052 + imports.wbg.__wbindgen_closure_wrapper451 = function(arg0, arg1, arg2) { 1053 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1054 + return ret; 1055 + }; 1056 + imports.wbg.__wbindgen_closure_wrapper477 = function(arg0, arg1, arg2) { 1057 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1058 + return ret; 1059 + }; 1060 + imports.wbg.__wbindgen_closure_wrapper479 = function(arg0, arg1, arg2) { 1061 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1062 + return ret; 1063 + }; 1064 + imports.wbg.__wbindgen_closure_wrapper481 = function(arg0, arg1, arg2) { 1065 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1066 + return ret; 1067 + }; 1068 + imports.wbg.__wbindgen_closure_wrapper482 = function(arg0, arg1, arg2) { 1069 + const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1070 + return ret; 1071 + }; 1072 + imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { 1073 + const ret = debugString(arg1); 1074 + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 1075 + const len1 = WASM_VECTOR_LEN; 1076 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 1077 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 1078 + }; 1079 + imports.wbg.__wbindgen_init_externref_table = function() { 1080 + const table = wasm.__wbindgen_export_1; 1081 + const offset = table.grow(4); 1082 + table.set(0, undefined); 1083 + table.set(offset + 0, undefined); 1084 + table.set(offset + 1, null); 1085 + table.set(offset + 2, true); 1086 + table.set(offset + 3, false); 1087 + ; 1088 + }; 1089 + imports.wbg.__wbindgen_is_function = function(arg0) { 1090 + const ret = typeof(arg0) === 'function'; 1091 + return ret; 1092 + }; 1093 + imports.wbg.__wbindgen_is_object = function(arg0) { 1094 + const val = arg0; 1095 + const ret = typeof(val) === 'object' && val !== null; 1096 + return ret; 1097 + }; 1098 + imports.wbg.__wbindgen_is_string = function(arg0) { 1099 + const ret = typeof(arg0) === 'string'; 1100 + return ret; 1101 + }; 1102 + imports.wbg.__wbindgen_is_undefined = function(arg0) { 1103 + const ret = arg0 === undefined; 1104 + return ret; 1105 + }; 1106 + imports.wbg.__wbindgen_memory = function() { 1107 + const ret = wasm.memory; 1108 + return ret; 1109 + }; 1110 + imports.wbg.__wbindgen_number_new = function(arg0) { 1111 + const ret = arg0; 1112 + return ret; 1113 + }; 1114 + imports.wbg.__wbindgen_string_get = function(arg0, arg1) { 1115 + const obj = arg1; 1116 + const ret = typeof(obj) === 'string' ? obj : undefined; 1117 + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 1118 + var len1 = WASM_VECTOR_LEN; 1119 + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 1120 + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 1121 + }; 1122 + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { 1123 + const ret = getStringFromWasm0(arg0, arg1); 1124 + return ret; 1125 + }; 1126 + imports.wbg.__wbindgen_throw = function(arg0, arg1) { 1127 + throw new Error(getStringFromWasm0(arg0, arg1)); 1128 + }; 1129 + 1130 + return imports; 1131 + } 1132 + 1133 + function __wbg_init_memory(imports, memory) { 1134 + 1135 + } 1136 + 1137 + function __wbg_finalize_init(instance, module) { 1138 + wasm = instance.exports; 1139 + __wbg_init.__wbindgen_wasm_module = module; 1140 + cachedDataViewMemory0 = null; 1141 + cachedUint8ArrayMemory0 = null; 1142 + cachedUint8ClampedArrayMemory0 = null; 1143 + 1144 + 1145 + wasm.__wbindgen_start(); 1146 + return wasm; 1147 + } 1148 + 1149 + function initSync(module) { 1150 + if (wasm !== undefined) return wasm; 1151 + 1152 + 1153 + if (typeof module !== 'undefined') { 1154 + if (Object.getPrototypeOf(module) === Object.prototype) { 1155 + ({module} = module) 1156 + } else { 1157 + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') 1158 + } 1159 + } 1160 + 1161 + const imports = __wbg_get_imports(); 1162 + 1163 + __wbg_init_memory(imports); 1164 + 1165 + if (!(module instanceof WebAssembly.Module)) { 1166 + module = new WebAssembly.Module(module); 1167 + } 1168 + 1169 + const instance = new WebAssembly.Instance(module, imports); 1170 + 1171 + return __wbg_finalize_init(instance, module); 1172 + } 1173 + 1174 + async function __wbg_init(module_or_path) { 1175 + if (wasm !== undefined) return wasm; 1176 + 1177 + 1178 + if (typeof module_or_path !== 'undefined') { 1179 + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { 1180 + ({module_or_path} = module_or_path) 1181 + } else { 1182 + console.warn('using deprecated parameters for the initialization function; pass a single object instead') 1183 + } 1184 + } 1185 + 1186 + if (typeof module_or_path === 'undefined') { 1187 + module_or_path = new URL('annoyance-meow_bg.wasm', import.meta.url); 1188 + } 1189 + const imports = __wbg_get_imports(); 1190 + 1191 + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { 1192 + module_or_path = fetch(module_or_path); 1193 + } 1194 + 1195 + __wbg_init_memory(imports); 1196 + 1197 + const { instance, module } = await __wbg_load(await module_or_path, imports); 1198 + 1199 + return __wbg_finalize_init(instance, module); 1200 + } 1201 + 1202 + export { initSync }; 1203 + export default __wbg_init;
eunomia/static/annoy/annoyance-meow_bg.wasm

This is a binary file and will not be displayed.

eunomia/static/banners/1.gif

This is a binary file and will not be displayed.

eunomia/static/banners/10.gif

This is a binary file and will not be displayed.

eunomia/static/banners/11.gif

This is a binary file and will not be displayed.

eunomia/static/banners/12.gif

This is a binary file and will not be displayed.

eunomia/static/banners/13.gif

This is a binary file and will not be displayed.

eunomia/static/banners/14.gif

This is a binary file and will not be displayed.

eunomia/static/banners/15.gif

This is a binary file and will not be displayed.

eunomia/static/banners/16.gif

This is a binary file and will not be displayed.

eunomia/static/banners/17.gif

This is a binary file and will not be displayed.

eunomia/static/banners/18.gif

This is a binary file and will not be displayed.

eunomia/static/banners/19.gif

This is a binary file and will not be displayed.

eunomia/static/banners/2.gif

This is a binary file and will not be displayed.

eunomia/static/banners/20.gif

This is a binary file and will not be displayed.

eunomia/static/banners/3.gif

This is a binary file and will not be displayed.

eunomia/static/banners/4.gif

This is a binary file and will not be displayed.

eunomia/static/banners/5.gif

This is a binary file and will not be displayed.

eunomia/static/banners/6.gif

This is a binary file and will not be displayed.

eunomia/static/banners/7.gif

This is a binary file and will not be displayed.

eunomia/static/banners/8.gif

This is a binary file and will not be displayed.

eunomia/static/banners/9.gif

This is a binary file and will not be displayed.

eunomia/static/eyes/closed.webp

This is a binary file and will not be displayed.

eunomia/static/eyes/normal_forward.webp

This is a binary file and will not be displayed.

eunomia/static/eyes/normal_left.webp

This is a binary file and will not be displayed.

eunomia/static/eyes/normal_right.webp

This is a binary file and will not be displayed.

eunomia/static/fonts/dollmonoopt.woff2

This is a binary file and will not be displayed.

eunomia/static/fonts/fusionmono.woff2

This is a binary file and will not be displayed.

eunomia/static/fonts/fusionprop.woff2

This is a binary file and will not be displayed.

eunomia/static/icons/about.webp

This is a binary file and will not be displayed.

eunomia/static/icons/cd_audio.webp

This is a binary file and will not be displayed.

eunomia/static/icons/contact.webp

This is a binary file and will not be displayed.

eunomia/static/icons/entries.webp

This is a binary file and will not be displayed.

eunomia/static/icons/entry.webp

This is a binary file and will not be displayed.

eunomia/static/icons/gaze.webp

This is a binary file and will not be displayed.

eunomia/static/icons/gaze_closed.webp

This is a binary file and will not be displayed.

eunomia/static/icons/gaze_site.webp

This is a binary file and will not be displayed.

eunomia/static/icons/guestbook.webp

This is a binary file and will not be displayed.

eunomia/static/icons/home.webp

This is a binary file and will not be displayed.

eunomia/static/icons/msg_information.webp

This is a binary file and will not be displayed.

eunomia/static/icons/msn.webp

This is a binary file and will not be displayed.

eunomia/static/icons/question.webp

This is a binary file and will not be displayed.

eunomia/static/icons/warning.webp

This is a binary file and will not be displayed.

eunomia/static/others/250kb.webp

This is a binary file and will not be displayed.

eunomia/static/others/aph.gif

This is a binary file and will not be displayed.

eunomia/static/others/dbd.gif

This is a binary file and will not be displayed.

eunomia/static/others/dd86k.gif

This is a binary file and will not be displayed.

eunomia/static/others/drewsh.gif

This is a binary file and will not be displayed.

eunomia/static/others/godot.gif

This is a binary file and will not be displayed.

eunomia/static/others/it.webp

This is a binary file and will not be displayed.

eunomia/static/others/killfascists.webp

This is a binary file and will not be displayed.

eunomia/static/others/memberschoice.webp

This is a binary file and will not be displayed.

eunomia/static/others/moonlightnow.gif

This is a binary file and will not be displayed.

eunomia/static/others/notaperson.webp

This is a binary file and will not be displayed.

eunomia/static/others/poweredbynixos.webp

This is a binary file and will not be displayed.

eunomia/static/others/skylar.gif

This is a binary file and will not be displayed.

eunomia/static/others/slonk.gif

This is a binary file and will not be displayed.

eunomia/static/others/zwsp_dark.webp

This is a binary file and will not be displayed.

eunomia/static/pet/idle.webp

This is a binary file and will not be displayed.

eunomia/static/pet/pick.webp

This is a binary file and will not be displayed.

eunomia/static/pet/walk1.webp

This is a binary file and will not be displayed.

eunomia/static/pet/walk2.webp

This is a binary file and will not be displayed.

eunomia/static/pfp-iojkqpwerojnasduijf.webp

This is a binary file and will not be displayed.

eunomia/static/resume.pdf

This is a binary file and will not be displayed.

eunomia/static/wavey.gif

This is a binary file and will not be displayed.

+62
eunomia/svelte.config.js
··· 1 + import adapter from '@sveltejs/adapter-node'; 2 + import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 + 4 + import { mdsvex } from 'mdsvex'; 5 + 6 + import * as toml from '@std/toml'; 7 + import { resolve } from 'node:path'; 8 + 9 + /** @type {import('@sveltejs/kit').Config} */ 10 + const config = { 11 + extensions: ['.svelte', '.md', '.svx'], 12 + 13 + preprocess: [ 14 + vitePreprocess(), 15 + mdsvex({ 16 + extensions: ['.md', '.svx'], 17 + frontmatter: { 18 + type: 'toml', 19 + marker: '+', 20 + parse(frontmatter, messages) { 21 + try { 22 + return toml.parse(frontmatter); 23 + } catch (e) { 24 + messages.push( 25 + 'Parsing error on line ' + e.line + ', column ' + e.column + ': ' + e.message 26 + ); 27 + } 28 + } 29 + }, 30 + smartypants: { 31 + dashes: 'oldschool', 32 + quotes: true, 33 + ellipses: true, 34 + backticks: false 35 + }, 36 + layout: { 37 + about: resolve('src/routes/(site)/about/_layout.svelte'), 38 + blogpost: resolve('src/routes/(site)/entries/_layout.svelte'), 39 + simple: resolve('src/components/_window_layout.svelte') 40 + } 41 + }) 42 + ], 43 + 44 + kit: { 45 + csrf: { 46 + checkOrigin: false, 47 + trustedOrigins: ["https://gaze.systems", "https://ptr.pet", "https://poor.dog"] 48 + }, 49 + prerender: { 50 + handleHttpError: 'warn' 51 + }, 52 + adapter: adapter({ 53 + precompress: true 54 + }), 55 + alias: { 56 + $components: 'src/components', 57 + $styles: 'src/styles' 58 + } 59 + } 60 + }; 61 + 62 + export default config;
+58
eunomia/tailwind.config.js
··· 1 + import typography from '@tailwindcss/typography'; 2 + import forms from '@tailwindcss/forms'; 3 + 4 + /** @type {import('tailwindcss').Config} */ 5 + export default { 6 + content: ['./src/**/*.{html,js,svelte,ts,md}'], 7 + theme: { 8 + extend: { 9 + typography: ({ theme }) => ({ 10 + ralsei: { 11 + css: { 12 + '--tw-prose-body': theme('colors.ralsei.white'), 13 + '--tw-prose-headings': theme('colors.ralsei.pink.neon'), 14 + '--tw-prose-lead': theme('colors.ralsei.white'), 15 + '--tw-prose-links': theme('colors.ralsei.green.light'), 16 + '--tw-prose-bold': theme('colors.ralsei.white'), 17 + '--tw-prose-counters': theme('colors.ralsei.pink.regular'), 18 + '--tw-prose-bullets': theme('colors.ralsei.pink.regular'), 19 + '--tw-prose-hr': theme('colors.ralsei.white'), 20 + '--tw-prose-quotes': theme('colors.ralsei.white'), 21 + '--tw-prose-quote-borders': theme('colors.ralsei.white'), 22 + '--tw-prose-captions': theme('colors.ralsei.white'), 23 + '--tw-prose-code': theme('colors.ralsei.pink.regular'), 24 + '--tw-prose-pre-code': theme('colors.ralsei.white'), 25 + '--tw-prose-pre-bg': theme('colors.ralsei.green.dark'), 26 + '--tw-prose-th-borders': theme('colors.ralsei.white'), 27 + '--tw-prose-td-borders': theme('colors.ralsei.white') 28 + } 29 + } 30 + }), 31 + animation: { 32 + 'bounce-slow': 'bounce 3s infinite', 33 + 'bounce-fast': 'bounce 0.5s infinite', 34 + 'pulse-fast': 'pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite', 35 + blink: 'blink 1s step-start infinite' 36 + }, 37 + colors: { 38 + ralsei: { 39 + pink: { 40 + regular: '#fe96e0', 41 + neon: '#ff3eb7' 42 + }, 43 + white: '#fff9fe', 44 + black: '#000801', 45 + green: { 46 + light: '#4dcc8e', 47 + dark: '#162d26' 48 + } 49 + } 50 + } 51 + }, 52 + fontFamily: { 53 + 'sans-serif': ['"Fusion Pixel 8px Proportional SC", sans-serif'], 54 + monospace: ['"Fusion Pixel 8px Monospaced SC", monospace'] 55 + } 56 + }, 57 + plugins: [typography, forms] 58 + };
+24
eunomia/tsconfig.json
··· 1 + { 2 + "extends": "./.svelte-kit/tsconfig.json", 3 + "compilerOptions": { 4 + "allowJs": true, 5 + "checkJs": true, 6 + "esModuleInterop": true, 7 + "forceConsistentCasingInFileNames": true, 8 + "resolveJsonModule": true, 9 + "skipLibCheck": true, 10 + "sourceMap": true, 11 + "strict": true, 12 + "plugins": [ 13 + { 14 + "name": "typescript-svelte-plugin", 15 + "enabled": true 16 + } 17 + ] 18 + } 19 + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 20 + // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files 21 + // 22 + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 23 + // from the referenced tsconfig.json - TypeScript does not merge them in 24 + }
+11
eunomia/vite.config.ts
··· 1 + import { sveltekit } from '@sveltejs/kit/vite'; 2 + import { defineConfig } from 'vite'; 3 + 4 + export default defineConfig({ 5 + plugins: [sveltekit()], 6 + server: { 7 + fs: { 8 + allow: ['../'] 9 + } 10 + } 11 + });
+242 -11
flake.lock
··· 1 1 { 2 2 "nodes": { 3 + "crane": { 4 + "flake": false, 5 + "locked": { 6 + "lastModified": 1758758545, 7 + "narHash": "sha256-NU5WaEdfwF6i8faJ2Yh+jcK9vVFrofLcwlD/mP65JrI=", 8 + "owner": "ipetkov", 9 + "repo": "crane", 10 + "rev": "95d528a5f54eaba0d12102249ce42f4d01f4e364", 11 + "type": "github" 12 + }, 13 + "original": { 14 + "owner": "ipetkov", 15 + "ref": "v0.21.1", 16 + "repo": "crane", 17 + "type": "github" 18 + } 19 + }, 20 + "dream2nix": { 21 + "inputs": { 22 + "nixpkgs": [ 23 + "nci", 24 + "nixpkgs" 25 + ], 26 + "purescript-overlay": "purescript-overlay", 27 + "pyproject-nix": "pyproject-nix" 28 + }, 29 + "locked": { 30 + "lastModified": 1765228272, 31 + "narHash": "sha256-duTz4J4NP1edl/ZBdwZPduPM8j6g0yzjb8YH91T9vU0=", 32 + "owner": "nix-community", 33 + "repo": "dream2nix", 34 + "rev": "83c430ce6b6aedf149c5259f066bfff808722dbd", 35 + "type": "github" 36 + }, 37 + "original": { 38 + "owner": "nix-community", 39 + "repo": "dream2nix", 40 + "type": "github" 41 + } 42 + }, 43 + "flake-compat": { 44 + "flake": false, 45 + "locked": { 46 + "lastModified": 1696426674, 47 + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 48 + "owner": "edolstra", 49 + "repo": "flake-compat", 50 + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 51 + "type": "github" 52 + }, 53 + "original": { 54 + "owner": "edolstra", 55 + "repo": "flake-compat", 56 + "type": "github" 57 + } 58 + }, 59 + "mk-naked-shell": { 60 + "flake": false, 61 + "locked": { 62 + "lastModified": 1681286841, 63 + "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", 64 + "owner": "90-008", 65 + "repo": "mk-naked-shell", 66 + "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", 67 + "type": "github" 68 + }, 69 + "original": { 70 + "owner": "90-008", 71 + "repo": "mk-naked-shell", 72 + "type": "github" 73 + } 74 + }, 3 75 "naked-shell": { 4 76 "locked": { 5 77 "lastModified": 1681286841, ··· 15 87 "type": "github" 16 88 } 17 89 }, 90 + "nci": { 91 + "inputs": { 92 + "crane": "crane", 93 + "dream2nix": "dream2nix", 94 + "mk-naked-shell": "mk-naked-shell", 95 + "nixpkgs": [ 96 + "nixpkgs" 97 + ], 98 + "parts": "parts", 99 + "rust-overlay": "rust-overlay", 100 + "treefmt": "treefmt" 101 + }, 102 + "locked": { 103 + "lastModified": 1765347705, 104 + "narHash": "sha256-pp5ru9CvZz0n1UBvRzp41othwARTUBmK1Es8iAqgepc=", 105 + "owner": "90-008", 106 + "repo": "nix-cargo-integration", 107 + "rev": "c573f9ec80416fd09964a3e3892f14d9012a03e2", 108 + "type": "github" 109 + }, 110 + "original": { 111 + "owner": "90-008", 112 + "repo": "nix-cargo-integration", 113 + "type": "github" 114 + } 115 + }, 18 116 "nixpkgs": { 19 117 "locked": { 20 - "lastModified": 1752950548, 21 - "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", 118 + "lastModified": 1765270179, 119 + "narHash": "sha256-g2a4MhRKu4ymR4xwo+I+auTknXt/+j37Lnf0Mvfl1rE=", 22 120 "owner": "nixos", 23 121 "repo": "nixpkgs", 24 - "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", 122 + "rev": "677fbe97984e7af3175b6c121f3c39ee5c8d62c9", 25 123 "type": "github" 26 124 }, 27 125 "original": { 28 126 "owner": "nixos", 29 - "ref": "nixos-unstable", 127 + "ref": "nixpkgs-unstable", 30 128 "repo": "nixpkgs", 31 129 "type": "github" 32 130 } 33 131 }, 34 132 "nixpkgs-lib": { 35 133 "locked": { 36 - "lastModified": 1751159883, 37 - "narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=", 134 + "lastModified": 1761765539, 135 + "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", 38 136 "owner": "nix-community", 39 137 "repo": "nixpkgs.lib", 40 - "rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab", 138 + "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", 41 139 "type": "github" 42 140 }, 43 141 "original": { ··· 48 146 }, 49 147 "parts": { 50 148 "inputs": { 149 + "nixpkgs-lib": [ 150 + "nci", 151 + "nixpkgs" 152 + ] 153 + }, 154 + "locked": { 155 + "lastModified": 1763759067, 156 + "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", 157 + "owner": "hercules-ci", 158 + "repo": "flake-parts", 159 + "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", 160 + "type": "github" 161 + }, 162 + "original": { 163 + "owner": "hercules-ci", 164 + "repo": "flake-parts", 165 + "type": "github" 166 + } 167 + }, 168 + "parts_2": { 169 + "inputs": { 51 170 "nixpkgs-lib": "nixpkgs-lib" 52 171 }, 53 172 "locked": { 54 - "lastModified": 1753121425, 55 - "narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=", 173 + "lastModified": 1763759067, 174 + "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", 56 175 "owner": "hercules-ci", 57 176 "repo": "flake-parts", 58 - "rev": "644e0fc48951a860279da645ba77fe4a6e814c5e", 177 + "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", 59 178 "type": "github" 60 179 }, 61 180 "original": { ··· 64 183 "type": "github" 65 184 } 66 185 }, 186 + "purescript-overlay": { 187 + "inputs": { 188 + "flake-compat": "flake-compat", 189 + "nixpkgs": [ 190 + "nci", 191 + "dream2nix", 192 + "nixpkgs" 193 + ], 194 + "slimlock": "slimlock" 195 + }, 196 + "locked": { 197 + "lastModified": 1728546539, 198 + "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", 199 + "owner": "thomashoneyman", 200 + "repo": "purescript-overlay", 201 + "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", 202 + "type": "github" 203 + }, 204 + "original": { 205 + "owner": "thomashoneyman", 206 + "repo": "purescript-overlay", 207 + "type": "github" 208 + } 209 + }, 210 + "pyproject-nix": { 211 + "inputs": { 212 + "nixpkgs": [ 213 + "nci", 214 + "dream2nix", 215 + "nixpkgs" 216 + ] 217 + }, 218 + "locked": { 219 + "lastModified": 1752481895, 220 + "narHash": "sha256-luVj97hIMpCbwhx3hWiRwjP2YvljWy8FM+4W9njDhLA=", 221 + "owner": "pyproject-nix", 222 + "repo": "pyproject.nix", 223 + "rev": "16ee295c25107a94e59a7fc7f2e5322851781162", 224 + "type": "github" 225 + }, 226 + "original": { 227 + "owner": "pyproject-nix", 228 + "repo": "pyproject.nix", 229 + "type": "github" 230 + } 231 + }, 67 232 "root": { 68 233 "inputs": { 69 234 "naked-shell": "naked-shell", 235 + "nci": "nci", 70 236 "nixpkgs": "nixpkgs", 71 - "parts": "parts" 237 + "parts": "parts_2" 238 + } 239 + }, 240 + "rust-overlay": { 241 + "inputs": { 242 + "nixpkgs": [ 243 + "nci", 244 + "nixpkgs" 245 + ] 246 + }, 247 + "locked": { 248 + "lastModified": 1765334520, 249 + "narHash": "sha256-jTof2+ir9UPmv4lWksYO6WbaXCC0nsDExrB9KZj7Dz4=", 250 + "owner": "oxalica", 251 + "repo": "rust-overlay", 252 + "rev": "db61f666aea93b28f644861fbddd37f235cc5983", 253 + "type": "github" 254 + }, 255 + "original": { 256 + "owner": "oxalica", 257 + "repo": "rust-overlay", 258 + "type": "github" 259 + } 260 + }, 261 + "slimlock": { 262 + "inputs": { 263 + "nixpkgs": [ 264 + "nci", 265 + "dream2nix", 266 + "purescript-overlay", 267 + "nixpkgs" 268 + ] 269 + }, 270 + "locked": { 271 + "lastModified": 1688756706, 272 + "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", 273 + "owner": "thomashoneyman", 274 + "repo": "slimlock", 275 + "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", 276 + "type": "github" 277 + }, 278 + "original": { 279 + "owner": "thomashoneyman", 280 + "repo": "slimlock", 281 + "type": "github" 282 + } 283 + }, 284 + "treefmt": { 285 + "inputs": { 286 + "nixpkgs": [ 287 + "nci", 288 + "nixpkgs" 289 + ] 290 + }, 291 + "locked": { 292 + "lastModified": 1762938485, 293 + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", 294 + "owner": "numtide", 295 + "repo": "treefmt-nix", 296 + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", 297 + "type": "github" 298 + }, 299 + "original": { 300 + "owner": "numtide", 301 + "repo": "treefmt-nix", 302 + "type": "github" 72 303 } 73 304 } 74 305 },
+13 -11
flake.nix
··· 1 1 { 2 2 inputs.parts.url = "github:hercules-ci/flake-parts"; 3 - inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 3 + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 4 4 inputs.naked-shell.url = "github:90-008/mk-naked-shell"; 5 + inputs.nci.url = "github:90-008/nix-cargo-integration"; 6 + inputs.nci.inputs.nixpkgs.follows = "nixpkgs"; 5 7 6 8 outputs = inp: 7 9 inp.parts.lib.mkFlake {inputs = inp;} { 8 10 systems = ["x86_64-linux"]; 9 11 imports = [ 10 12 inp.naked-shell.flakeModule 13 + # inp.nci.flakeModule 11 14 ]; 12 15 perSystem = { 13 16 config, 14 - system, 17 + pkgs, 15 18 ... 16 - }: let 17 - pkgs = inp.nixpkgs.legacyPackages.${system}; 18 - in { 19 - devShells.default = config.mk-naked-shell.lib.mkNakedShell { 20 - name = "gazesys-devshell"; 19 + }: { 20 + devShells.default = pkgs.mkShell { 21 + name = "eunomia-devshell"; 21 22 packages = with pkgs; [ 22 23 nodejs-slim_latest deno 23 24 nodePackages.svelte-language-server 24 25 nodePackages.typescript-language-server 26 + rustc rust-analyzer cargo wasm-pack wasm-bindgen-cli lld rustfmt binaryen 25 27 ]; 26 28 shellHook = '' 27 29 export PATH="$PATH:$PWD/node_modules/.bin" 28 30 ''; 29 31 }; 30 - packages.gazesys-modules = pkgs.callPackage ./nix/modules.nix {}; 31 - packages.gazesys = pkgs.callPackage ./nix { 32 - inherit (config.packages) gazesys-modules; 32 + packages.eunomia-modules = pkgs.callPackage ./nix/modules.nix {}; 33 + packages.eunomia = pkgs.callPackage ./nix { 34 + inherit (config.packages) eunomia-modules; 33 35 }; 34 - packages.default = config.packages.gazesys; 36 + packages.default = config.packages.eunomia; 35 37 }; 36 38 }; 37 39 }
+10 -14
nix/default.nix
··· 4 4 deno, 5 5 nodejs, 6 6 makeBinaryWrapper, 7 - gazesys-modules, 7 + eunomia-modules, 8 8 PUBLIC_BASE_URL ? "http://localhost:5173", 9 9 }: 10 10 stdenv.mkDerivation { 11 - name = "gazesys-website"; 11 + name = "eunomia"; 12 12 13 13 src = lib.fileset.toSource { 14 14 root = ../.; 15 15 fileset = lib.fileset.unions [ 16 - ../src 17 - ../static 18 16 ../deno.lock 19 - ../package.json 20 - ../postcss.config.js 21 - ../svelte.config.js 22 - ../tailwind.config.js 23 - ../tsconfig.json 24 - ../vite.config.ts 17 + ../deno.json 18 + ../eunomia 25 19 ]; 26 20 }; 27 21 ··· 34 28 35 29 configurePhase = '' 36 30 runHook preConfigure 37 - cp -R --no-preserve=ownership ${gazesys-modules} node_modules 31 + cp -R --no-preserve=ownership ${eunomia-modules} node_modules 38 32 find node_modules -type d -exec chmod 755 {} \; 39 33 substituteInPlace node_modules/.bin/vite \ 40 34 --replace-fail "/usr/bin/env node" "${nodejs}/bin/node" ··· 42 36 ''; 43 37 buildPhase = '' 44 38 runHook preBuild 45 - HOME=$TMPDIR deno run --cached-only build 39 + pushd eunomia 40 + HOME=$TMPDIR ../node_modules/.bin/vite build 41 + popd 46 42 runHook postBuild 47 43 ''; 48 44 installPhase = '' 49 45 runHook preInstall 50 46 51 47 mkdir -p $out/bin 52 - cp -R ./build/* $out 48 + cp -R ./eunomia/build/* $out 53 49 cp -R ./node_modules $out 54 50 55 - makeBinaryWrapper ${deno}/bin/deno $out/bin/website \ 51 + makeBinaryWrapper ${deno}/bin/deno $out/bin/eunomia \ 56 52 --prefix PATH : ${lib.makeBinPath [ deno ]} \ 57 53 --add-flags "run --allow-all --node-modules-dir=manual --cached-only $out/index.js" 58 54
+7 -3
nix/modules.nix
··· 4 4 deno, 5 5 }: 6 6 stdenv.mkDerivation { 7 - name = "gazesys-modules"; 7 + name = "eunomia-modules"; 8 8 9 9 src = lib.fileset.toSource { 10 10 root = ../.; 11 11 fileset = lib.fileset.unions [ 12 + ../eunomia/package.json 13 + ../deno.json 12 14 ../deno.lock 13 - ../package.json 14 15 ]; 15 16 }; 16 17 17 - outputHash = "sha256-TkpivNdWLV0t1nJAtNy0OAK7+uNtWxQODSl4petjJvc="; 18 + outputHash = "sha256-H4aW1XczTjlYwPfPYu0BUcJphSdlfNRZzyrI42shJcY="; 18 19 outputHashAlgo = "sha256"; 19 20 outputHashMode = "recursive"; 20 21 ··· 25 26 dontFixup = true; 26 27 dontPatchShebangs = true; 27 28 29 + postUnpack = '' 30 + 31 + ''; 28 32 buildPhase = '' 29 33 HOME=$TMPDIR deno install --allow-scripts=npm:protobufjs@7.5.4 --frozen --seed 8008135 30 34 '';
-63
package.json
··· 1 - { 2 - "name": "website", 3 - "version": "0.0.1", 4 - "private": true, 5 - "scripts": { 6 - "dev": "vite dev", 7 - "build": "vite build", 8 - "preview": "vite preview", 9 - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 - "lint": "prettier --check . && eslint .", 12 - "format": "prettier --write ." 13 - }, 14 - "devDependencies": { 15 - "@sveltejs/adapter-node": "^5.4.0", 16 - "@sveltejs/kit": "^2.49.0", 17 - "@sveltejs/vite-plugin-svelte": "^6.1.3", 18 - "@tailwindcss/forms": "^0.5.10", 19 - "@tailwindcss/typography": "^0.5.16", 20 - "@types/deno": "^2.5.0", 21 - "@types/eslint": "^9.6.1", 22 - "@types/node": "^24.10.1", 23 - "autoprefixer": "^10.4.22", 24 - "eslint": "^9.39.1", 25 - "eslint-config-prettier": "^10.1.8", 26 - "eslint-plugin-svelte": "^3.13.0", 27 - "globals": "^16.5.0", 28 - "mdsvex": "^0.12.6", 29 - "postcss": "^8.5.6", 30 - "prettier": "^3.7.1", 31 - "prettier-plugin-svelte": "^3.4.0", 32 - "svelte": "^5.45.2", 33 - "svelte-check": "^4.3.4", 34 - "sveltekit-rate-limiter": "^0.7.0", 35 - "tailwindcss": "^3.4.17", 36 - "tslib": "^2.8.1", 37 - "typescript": "^5.9.2", 38 - "typescript-eslint": "^8.48.0", 39 - "typescript-svelte-plugin": "^0.3.50", 40 - "vite": "^7.2.4" 41 - }, 42 - "type": "module", 43 - "dependencies": { 44 - "@neodrag/svelte": "^2.3.3", 45 - "@rowanmanning/feed-parser": "^2.1.1", 46 - "@skyware/bot": "^0.4.0", 47 - "@std/toml": "npm:@jsr/std__toml", 48 - "@types/node-schedule": "^2.1.8", 49 - "nanoid": "^5.1.5", 50 - "node-fetch": "^3.3.2", 51 - "prometheus-remote-write": "^0.5.1", 52 - "robots-parser": "^3.0.1", 53 - "steamgriddb": "^2.2.0", 54 - "toad-scheduler": "^3.1.0" 55 - }, 56 - "trustedDependencies": [ 57 - "@sveltejs/kit", 58 - "esbuild", 59 - "protobufjs", 60 - "sharp", 61 - "svelte-preprocess" 62 - ] 63 - }
-6
postcss.config.js
··· 1 - export default { 2 - plugins: { 3 - tailwindcss: {}, 4 - autoprefixer: {}, 5 - }, 6 - }
-13
src/app.d.ts
··· 1 - // See https://kit.svelte.dev/docs/types#app 2 - // for information about these interfaces 3 - declare global { 4 - namespace App { 5 - // interface Error {} 6 - // interface Locals {} 7 - // interface PageData {} 8 - // interface PageState {} 9 - // interface Platform {} 10 - } 11 - } 12 - 13 - export {};
-12
src/app.html
··· 1 - <!doctype html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="utf-8" /> 5 - <link rel="icon" href="%sveltekit.assets%/icons/gaze_site.webp" /> 6 - <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 - %sveltekit.head% 8 - </head> 9 - <body class="md:overflow-hidden"> 10 - <div style="display: contents">%sveltekit.body%</div> 11 - </body> 12 - </html>
-19
src/components/_window_layout.svelte
··· 1 - <script lang="ts"> 2 - import Window from './window.svelte'; 3 - import '../styles/app.css'; 4 - 5 - interface Props { 6 - title: string; 7 - sticky: boolean; 8 - prose?: boolean; 9 - children?: import('svelte').Snippet; 10 - } 11 - 12 - let { title, sticky, prose = true, children }: Props = $props(); 13 - </script> 14 - 15 - <Window {title} {sticky}> 16 - <div class={prose ? 'prose prose-ralsei leading-6 prose-ul:leading-5' : ''}> 17 - {@render children?.()} 18 - </div> 19 - </Window>
-75
src/components/eye.svelte
··· 1 - <script lang="ts"> 2 - import { genDollcode } from '$lib/dollcode'; 3 - 4 - interface Props { 5 - top: number; 6 - left: number; 7 - kind?: string; 8 - visits: number[]; 9 - id: string; 10 - } 11 - 12 - let { top, left, kind = 'normal', visits }: Props = $props(); 13 - 14 - let rotation = $state((Math.random() - 0.5) * 0.4); 15 - const opacity = Math.min(Math.random() * 0.3 + 0.4, 0.7); 16 - 17 - let closed = $state(false); 18 - let look = $state('forward'); 19 - const looks = ['left', 'forward', 'right']; 20 - const pickLook = $derived(() => { 21 - const pickable = looks.filter((l) => { 22 - return l !== look; 23 - }); 24 - return pickable.at(Math.floor(Math.random() * pickable.length)) ?? 'forward'; 25 - }); 26 - const randomizeLook = () => { 27 - look = pickLook(); 28 - rotation = (Math.random() - 0.5) * 0.4; 29 - setTimeout(randomizeLook, 2000 + Math.random() * 6000); 30 - }; 31 - 32 - let src = $derived(closed ? `/eyes/closed.webp` : `/eyes/${kind}_${look}.webp`); 33 - 34 - // generate dollcode based on time, but mod by 3 hours 35 - const timeDollcode = genDollcode((visits[0] / 1000) % (60 * 60 * 24)); 36 - const visitsDollcode = genDollcode(visits.length); 37 - 38 - randomizeLook(); 39 - </script> 40 - 41 - <!-- svelte-ignore a11y_mouse_events_have_key_events --> 42 - <!-- svelte-ignore a11y_no_static_element_interactions --> 43 - <div 44 - class="group flex gap-4 items-center scale-[0.75]" 45 - style=" 46 - position: fixed; 47 - top: {top}vh; 48 - left: {left}%; 49 - opacity: {opacity}; 50 - flex-direction: column-reverse; 51 - " 52 - onmouseover={() => { 53 - closed = true; 54 - }} 55 - onmouseleave={() => { 56 - closed = false; 57 - }} 58 - > 59 - <span class="eye-text !text-base">{visitsDollcode}</span> 60 - <!-- svelte-ignore a11y_missing_attribute --> 61 - <img class="w-24 eye-image" style="transform: rotate({rotation}rad);" {src} /> 62 - <span class="eye-text">{timeDollcode}</span> 63 - </div> 64 - 65 - <style lang="postcss"> 66 - .eye-text { 67 - @apply text-sm [font-family:Doll_Mono] opacity-30 group-hover:opacity-80; 68 - } 69 - .eye-image { 70 - @apply opacity-50 group-hover:opacity-100; 71 - image-rendering: pixelated !important; 72 - filter: drop-shadow(4px 4px 0 theme(colors.ralsei.green.light)) 73 - drop-shadow(-4px -4px 0 theme(colors.ralsei.pink.neon)); 74 - } 75 - </style>
-31
src/components/navButton.svelte
··· 1 - <script lang="ts"> 2 - interface Props { 3 - highlight?: boolean; 4 - name: string; 5 - href: string; 6 - iconUri: string; 7 - } 8 - 9 - let { highlight = false, name, href, iconUri }: Props = $props(); 10 - </script> 11 - 12 - <a 13 - class=" 14 - max-w-36 p-0.5 pr-1.5 border-ralsei-white border-4 15 - {highlight 16 - ? 'min-w-36 bg-gradient-to-l to-ralsei-pink-neon/30 from-ralsei-pink-regular/20 from-30% border-ridge motion-safe:animate-pulse hover:animate-none' 17 - : 'w-fit border-double hover:border-solid animate-bounce-reverse hover:underline'} 18 - flex gap-1 items-center justify-center align-middle text-center 19 - {highlight ? 'text-ralsei-pink-regular app-selected-route' : 'text-ralsei-green-light'} 20 - " 21 - title={name} 22 - href="/{href}" 23 - data-sveltekit-preload-data="tap" 24 - > 25 - <img class="max-w-4" style="image-rendering: pixelated;" src={iconUri} alt={name} /> 26 - <div 27 - class="font-monospace text-sm/3 overflow-hidden text-ellipsis text-nowrap [text-decoration-line:inherit]" 28 - > 29 - {name} 30 - </div> 31 - </a>
-127
src/components/note.svelte
··· 1 - <script module lang="ts"> 2 - import type { Post } from '@skyware/bot'; 3 - 4 - export interface OutgoingLink { 5 - name: string; 6 - link: string; 7 - } 8 - 9 - export interface NoteData { 10 - content: string; 11 - published: number; 12 - hasMedia: boolean; 13 - hasQuote: boolean; 14 - outgoingLinks?: OutgoingLink[]; 15 - purposeAction?: string; 16 - children?: NoteData[]; 17 - depth?: number; 18 - } 19 - 20 - export const flattenNotes = (note: NoteData, currentDepth: number = 0): NoteData[] => { 21 - note.depth = currentDepth; 22 - const flattened = [note]; 23 - if (note.children) { 24 - note.children.forEach((child) => { 25 - flattened.push(...flattenNotes(child, currentDepth + 1)); 26 - }); 27 - } 28 - return flattened; 29 - }; 30 - 31 - export const noteFromBskyPost = (post: Post): NoteData => { 32 - return { 33 - content: post.text, 34 - published: post.createdAt.getTime(), 35 - outgoingLinks: [{ name: 'bsky', link: post.uri }], 36 - hasMedia: 37 - (post.embed?.isImages() || post.embed?.isVideo() || post.embed?.isRecordWithMedia()) ?? 38 - false, 39 - hasQuote: (post.embed?.isRecord() || post.embed?.isRecordWithMedia()) ?? false 40 - }; 41 - }; 42 - </script> 43 - 44 - <script lang="ts"> 45 - import Token from './token.svelte'; 46 - import { renderDate, renderRelativeDate } from '$lib/dateFmt'; 47 - 48 - interface Props { 49 - rootNote: NoteData; 50 - isHighlighted?: boolean; 51 - onlyContent?: boolean; 52 - showOutgoing?: boolean; 53 - mapOutgoingNames?: Record<string, string>; 54 - } 55 - 56 - let { 57 - rootNote, 58 - isHighlighted = false, 59 - onlyContent = false, 60 - showOutgoing = true, 61 - mapOutgoingNames = {} 62 - }: Props = $props(); 63 - 64 - const getOutgoingLink = ({ name, link }: { name: string; link: string }) => { 65 - if (name.startsWith('bsky')) { 66 - // Parse the atproto URI to extract DID and rkey 67 - const match = link.match(/at:\/\/(did:[^/]+)\/[^/]+\/([^/]+)/); 68 - if (match && match.length >= 3) { 69 - // eslint-disable-next-line @typescript-eslint/no-unused-vars 70 - const [_, did, rkey] = match; 71 - link = `https://bsky.app/profile/${did}/post/${rkey}`; 72 - } 73 - if (name === 'bsky-reply') { 74 - return ['reply', link]; 75 - } else { 76 - return [name, link]; 77 - } 78 - } 79 - return [name, link]; 80 - }; 81 - // this is ASS this should be a tailwind class 82 - const getTextShadowStyle = (color: string) => { 83 - return `text-shadow: 0 0 1px theme(colors.ralsei.black), 0 0 5px ${color};`; 84 - }; 85 - const outgoingLinkColors: Record<string, string> = { 86 - bsky: 'rgb(0, 133, 255)', 87 - reply: 'rgb(0, 133, 255)' 88 - }; 89 - </script> 90 - 91 - {#each flattenNotes(rootNote) as note} 92 - <p class="m-0 max-w-[70ch] text-wrap break-words leading-tight align-middle"> 93 - {#if note.depth ?? 0 > 0} 94 - <span class="inline-block">|{'=='.repeat(note.depth ?? 0)}</span>&gt; 95 - {/if} 96 - {#if !onlyContent} 97 - {#if (note.purposeAction ?? '').length > 0} 98 - <Token v="({note.purposeAction!})" small={!isHighlighted} funct /> 99 - {/if} 100 - {#if note.purposeAction !== 'reply'} 101 - <Token 102 - title={renderDate(note.published)} 103 - v={renderRelativeDate(note.published)} 104 - small={!isHighlighted} 105 - /> 106 - {/if} 107 - {/if} 108 - <Token v={note.content} str /> 109 - {#if note.hasMedia}<Token v="-contains media-" keywd small />{/if} 110 - {#if note.hasQuote}<Token v="-contains quote-" keywd small />{/if} 111 - {#if showOutgoing} 112 - {#each (note.outgoingLinks ?? []).map(getOutgoingLink) as [name, link]} 113 - {@const color = outgoingLinkColors[name]} 114 - {@const viewName = mapOutgoingNames[name] ?? name} 115 - {#if viewName.length > 0} 116 - <span class="text-sm" 117 - ><Token v="(" punct /><a 118 - class="hover:motion-safe:animate-squiggle hover:underline" 119 - style="color: {color};{getTextShadowStyle(color)}" 120 - href={link}>{viewName}</a 121 - ><Token v=")" punct /></span 122 - > 123 - {/if} 124 - {/each} 125 - {/if} 126 - </p> 127 - {/each}
-304
src/components/pet.svelte
··· 1 - <script module lang="ts"> 2 - import { get, writable } from 'svelte/store'; 3 - 4 - export const localDistanceTravelled = writable(0.0); 5 - export const localBounces = writable(0); 6 - </script> 7 - 8 - <script lang="ts"> 9 - import { draggable } from '@neodrag/svelte'; 10 - import { browser } from '$app/environment'; 11 - 12 - interface Props { 13 - apiToken: string; 14 - } 15 - 16 - let { apiToken }: Props = $props(); 17 - 18 - let lastDragged = 0; 19 - let mouseX = 0; 20 - let mouseY = 0; 21 - 22 - let position = $state({ x: 0, y: 0 }); 23 - let rotation = $state(0.0); 24 - let sprite = $state('/pet/idle.webp'); 25 - let flip = $state(false); 26 - let dragged = $state(false); 27 - 28 - let targetX = 120; 29 - let speed = 10.0; 30 - let tickRate = 20; 31 - let delta = 1.0 / tickRate; 32 - 33 - let strideRadius = 4.0; 34 - let strideAngle = 0; 35 - 36 - const turnStrideWheel = (by: number) => { 37 - strideAngle += by / strideRadius; 38 - if (strideAngle > Math.PI * 2) { 39 - strideAngle -= Math.PI * 2; 40 - } else if (strideAngle < 0) { 41 - strideAngle += Math.PI * 2; 42 - } 43 - }; 44 - 45 - let targetRotation = 0.0; 46 - let rotationVelocity = 0.0; 47 - let springStiffness = 20.0; // How quickly rotation returns to target 48 - let springDamping = 0.2; // Damping factor to prevent oscillation 49 - 50 - const updateRotationSpring = () => { 51 - // Spring physics: calculate force based on distance from target 52 - const springForce = (targetRotation - rotation) * springStiffness; 53 - 54 - // Apply damping to velocity 55 - rotationVelocity = rotationVelocity * (1 - springDamping) + springForce * delta; 56 - 57 - // Update rotation based on velocity 58 - rotation += rotationVelocity * delta; 59 - 60 - // If we're very close to target and barely moving, just snap to target 61 - if (Math.abs(rotation - targetRotation) < 0.01 && Math.abs(rotationVelocity) < 0.01) { 62 - rotation = targetRotation; 63 - rotationVelocity = 0; 64 - } 65 - }; 66 - 67 - // Add spring update to the move function 68 - if (browser) setInterval(updateRotationSpring, tickRate); 69 - 70 - const moveTowards = (from: number, to: number, by: number) => { 71 - let d = (to - from) * 1.0; 72 - let l = Math.abs(d); 73 - let s = Math.sign(d); 74 - let moveBy = s * Math.min(l, by) * delta; 75 - return moveBy; 76 - }; 77 - 78 - // Physics constants 79 - let velocityX = 0; 80 - let velocityY = 0; 81 - let gravity = 200.0; // Gravity strength (positive because -Y is up) 82 - let friction = 0.96; // Air friction 83 - let groundFriction = 0.9; // Ground friction 84 - let bounciness = 0.8; // How much energy is preserved on bounce 85 - 86 - const sendBounceMetrics = () => { 87 - fetch(`/_api/pet/bounce?_token=${apiToken}`); 88 - localBounces.set(get(localBounces) + 1); 89 - }; 90 - 91 - let deltaTravelled = 0.0; 92 - let deltaTravelledTotal = 0.0; 93 - const updateDistanceTravelled = () => { 94 - if (deltaTravelled > 0.1 || deltaTravelled < -0.1) { 95 - localDistanceTravelled.update((n) => { 96 - n += deltaTravelled; 97 - return n; 98 - }); 99 - deltaTravelledTotal += deltaTravelled; 100 - } 101 - deltaTravelled = 0.0; 102 - }; 103 - 104 - const sendTotalDistance = () => { 105 - fetch(`/_api/pet/distance?_token=${apiToken}`, { 106 - method: 'POST', 107 - body: deltaTravelledTotal.toString() 108 - }); 109 - deltaTravelledTotal = 0.0; 110 - }; 111 - 112 - // sending every 5 seconds is probably reliable enough 113 - if (browser) setInterval(sendTotalDistance, 1000 * 5); 114 - 115 - const move = () => { 116 - if (dragged) return; 117 - 118 - // Apply physics when pet is in motion 119 - if (velocityX !== 0 || velocityY !== 0 || position.y !== 0) { 120 - // Apply gravity (remember negative Y is upward) 121 - velocityY += gravity * delta; 122 - 123 - // Apply friction 124 - const fric = position.y === 0 ? groundFriction : friction; 125 - velocityX *= fric; 126 - velocityY *= fric; 127 - 128 - // Update position 129 - const moveX = velocityX * delta; 130 - const moveY = velocityY * delta; 131 - position.x += moveX; 132 - position.y += moveY; 133 - 134 - deltaTravelled += Math.sqrt(moveX ** 2 + moveY ** 2); 135 - updateDistanceTravelled(); 136 - 137 - // Handle window boundaries 138 - const viewportWidth = window.innerWidth; 139 - 140 - // Bounce off sides 141 - if (position.x < 0) { 142 - position.x = 0; 143 - velocityX = -velocityX * bounciness; 144 - sendBounceMetrics(); 145 - } else if (position.x > viewportWidth) { 146 - position.x = viewportWidth; 147 - velocityX = -velocityX * bounciness; 148 - sendBounceMetrics(); 149 - } 150 - 151 - // Bounce off bottom (floor) 152 - if (position.y > 0) { 153 - position.y = 0; 154 - velocityY = -velocityY * bounciness; 155 - // Only bounce if velocity is significant 156 - if (Math.abs(velocityY) < 80) { 157 - velocityY = 0; 158 - position.y = 0; 159 - } else { 160 - sendBounceMetrics(); 161 - } 162 - } 163 - 164 - // reset velocity 165 - if (Math.abs(velocityX) < 5 && Math.abs(velocityY) < 5) { 166 - velocityX = 0; 167 - velocityY = 0; 168 - } 169 - 170 - // Update flip based on velocity 171 - if (Math.abs(velocityX) > 0.5) { 172 - flip = velocityX < 0; 173 - } 174 - 175 - targetRotation = velocityX * 0.02 + velocityY * 0.01; 176 - 177 - return; 178 - } 179 - 180 - // Normal movement when not physics-based 181 - let moveByX = moveTowards(position.x, targetX, speed * ((self.innerWidth ?? 1600.0) / 1600.0)); 182 - position.x += moveByX; 183 - 184 - turnStrideWheel(moveByX); 185 - 186 - flip = moveByX < 0.0; 187 - if (moveByX > 0.1 || moveByX < -0.1) { 188 - sprite = strideAngle % Math.PI < Math.PI * 0.5 ? '/pet/walk1.webp' : '/pet/walk2.webp'; 189 - } else { 190 - sprite = '/pet/idle.webp'; 191 - } 192 - 193 - deltaTravelled += Math.abs(moveByX); 194 - updateDistanceTravelled(); 195 - }; 196 - 197 - if (browser) setInterval(move, tickRate); 198 - 199 - const shake = (event: DeviceMotionEvent) => { 200 - const accel = event.acceleration ?? event.accelerationIncludingGravity; 201 - if (accel === null || accel.x === null || accel.y === null) return; 202 - if (Math.abs(accel.x) + Math.abs(accel.y) < 40.0) return; 203 - // make it so that it amplifies motion proportionally to the window size 204 - const windowRatio = (window.innerWidth * 1.0) / (window.innerHeight * 1.0); 205 - velocityX += accel.x * windowRatio * 5.0; 206 - velocityY += accel.y * (1.0 / windowRatio) * 5.0; 207 - sprite = '/pet/pick.webp'; 208 - }; 209 - 210 - if (browser) self.ondevicemotion = shake; 211 - 212 - // this is for ios 213 - const askForShakePermission = () => { 214 - if ( 215 - typeof DeviceMotionEvent !== 'undefined' && 216 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 217 - typeof (DeviceMotionEvent as any).requestPermission === 'function' 218 - ) { 219 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 220 - (DeviceMotionEvent as any) 221 - .requestPermission() 222 - .then((permissionState: string) => { 223 - if (permissionState === 'granted') { 224 - self.ondevicemotion = shake; 225 - } 226 - }) 227 - .catch(console.error); 228 - } 229 - }; 230 - 231 - const pickNewTargetX = () => { 232 - const viewportWidth = self.innerWidth || null; 233 - if (viewportWidth !== null && Math.abs(position.x - targetX) < 5) { 234 - targetX = Math.max( 235 - Math.min(targetX + (Math.random() - 0.5) * (viewportWidth * 0.5), viewportWidth * 0.9), 236 - viewportWidth * 0.1 237 - ); 238 - } 239 - // Set a random interval for the next target update (between 4-10 seconds) 240 - const randomDelay = Math.floor(Math.random() * 6000) + 4000; 241 - setTimeout(pickNewTargetX, randomDelay); 242 - }; 243 - 244 - // Start the process 245 - if (browser) setTimeout(pickNewTargetX, 1000); 246 - </script> 247 - 248 - <!-- svelte-ignore a11y_missing_attribute --> 249 - <div 250 - use:draggable={{ 251 - position, 252 - applyUserSelectHack: true, 253 - handle: 'img', 254 - bounds: { 255 - bottom: (window.innerHeight / 100) * 5.5 256 - }, 257 - onDragStart: () => { 258 - sprite = '/pet/pick.webp'; 259 - dragged = true; 260 - }, 261 - onDrag: ({ offsetX, offsetY, event }) => { 262 - position.x = offsetX; 263 - position.y = offsetY; 264 - const mouseXD = event.movementX * delta; 265 - const mouseYD = event.movementY * delta; 266 - deltaTravelled += Math.sqrt(mouseXD ** 2 + mouseYD ** 2); 267 - // reset mouse movement if it's not moving in the same direction so it doesnt accumulate its weird!@!@ 268 - mouseX = Math.sign(mouseXD) != Math.sign(mouseX) ? mouseXD : mouseX + mouseXD; 269 - mouseY = Math.sign(mouseYD) != Math.sign(mouseY) ? mouseYD : mouseY + mouseYD; 270 - rotationVelocity += mouseXD + mouseYD; 271 - lastDragged = Date.now(); 272 - }, 273 - onDragEnd: () => { 274 - // reset mouse movement if we stopped for longer than some time 275 - if (Date.now() - lastDragged > 50) { 276 - mouseX = 0.0; 277 - mouseY = 0.0; 278 - } 279 - // apply velocity based on rotation since we already keep track of that 280 - velocityX = mouseX * 70.0; 281 - velocityY = mouseY * 50.0; 282 - updateDistanceTravelled(); 283 - // reset mouse movement we dont want it to accumulate 284 - mouseX = 0.0; 285 - mouseY = 0.0; 286 - dragged = false; 287 - } 288 - }} 289 - class="fixed bottom-[5vh] z-[1000] hover:animate-squiggle" 290 - style="cursor: url('/icons/gaze.webp'), pointer;" 291 - > 292 - <!-- svelte-ignore a11y_no_noninteractive_element_interactions --> 293 - <!-- svelte-ignore a11y_click_events_have_key_events --> 294 - <img 295 - draggable="false" 296 - onclick={askForShakePermission} 297 - style=" 298 - image-rendering: pixelated !important; 299 - transform: rotate({rotation}rad) scaleX({flip ? -1 : 1}); 300 - filter: invert(100%) drop-shadow(2px 2px 0 black) drop-shadow(-2px -2px 0 black); 301 - " 302 - src={sprite} 303 - /> 304 - </div>
-32
src/components/token.svelte
··· 1 - <script lang="ts"> 2 - 3 - 4 - interface Props { 5 - punct?: boolean; 6 - keywd?: boolean; 7 - funct?: boolean; 8 - str?: boolean; 9 - small?: boolean; 10 - v: string; 11 - title?: string; 12 - } 13 - 14 - let { 15 - punct = false, 16 - keywd = false, 17 - funct = false, 18 - str = false, 19 - small = false, 20 - title, 21 - v 22 - }: Props = $props(); 23 - 24 - const ty = 25 - punct ? "punctuation" 26 - : keywd ? "keyword" 27 - : funct ? "function" 28 - : str ? "string" 29 - : "" 30 - </script> 31 - 32 - <span {title} class="token {ty} {small ? "text-sm" : ""}">{v}</span>
-34
src/components/tooltip.svelte
··· 1 - <script lang="ts"> 2 - import Window from './window.svelte'; 3 - 4 - interface Props { 5 - x?: string; 6 - y?: string; 7 - targetY?: string; 8 - targetX?: string; 9 - tooltipContent?: import('svelte').Snippet; 10 - children?: import('svelte').Snippet; 11 - style?: string; 12 - } 13 - 14 - let { 15 - x = 'translate-x-none', 16 - y = 'translate-y-full', 17 - targetY = 'group-hover:-translate-y-[105%]', 18 - targetX = 'group-hover:-translate-x-2/3', 19 - tooltipContent, 20 - children, 21 - style = '' 22 - }: Props = $props(); 23 - </script> 24 - 25 - <div class="group" {style}> 26 - <div 27 - class="z-10 absolute scale-0 transition-all [transition-timing-function:cubic-bezier(0.4,0,0.2,1.6)] [transition-duration:300ms] opacity-0 group-hover:scale-100 group-hover:opacity-100 {y} {x} {targetY} {targetX}" 28 - > 29 - <Window tooltip> 30 - {#if tooltipContent}{@render tooltipContent()}{:else}Hello world!{/if} 31 - </Window> 32 - </div> 33 - {@render children?.()} 34 - </div>
-124
src/components/window.svelte
··· 1 - <script lang="ts"> 2 - import { highestZIndex, isMobile } from '$lib/window.ts'; 3 - import { draggable } from '@neodrag/svelte'; 4 - 5 - interface Props { 6 - title?: string; 7 - iconUri?: string; 8 - id?: string; 9 - sticky?: boolean; 10 - entry?: boolean; 11 - removePadding?: boolean; 12 - center?: boolean; 13 - layered?: boolean; 14 - style?: string; 15 - tooltip?: boolean; 16 - children?: import('svelte').Snippet; 17 - } 18 - 19 - let { 20 - title = undefined, 21 - iconUri = '', 22 - id = '', 23 - sticky = false, 24 - entry = false, 25 - removePadding = false, 26 - center = false, 27 - layered = false, 28 - style = '', 29 - tooltip = false, 30 - children 31 - }: Props = $props(); 32 - 33 - const scaleKeyframes = [ 34 - 'window-open', 35 - 'window-open-vertical', 36 - 'window-open-vertical', 37 - 'window-open-horizontal', 38 - 'window-open-horizontal', 39 - 'window-open-move-up', 40 - 'window-open-move-down', 41 - 'window-open-move-left', 42 - 'window-open-move-right' 43 - ]; 44 - let chosenKeyframe = $derived( 45 - scaleKeyframes.at(Math.floor(Math.random() * scaleKeyframes.length)) 46 - ); 47 - 48 - const isOnMobile = isMobile(); 49 - const _draggable = isOnMobile ? () => {} : draggable; 50 - 51 - const focusWindow = (node: HTMLElement) => { 52 - if (isOnMobile) return; 53 - $highestZIndex += 1; 54 - node.style.zIndex = $highestZIndex.toString(); 55 - }; 56 - </script> 57 - 58 - <!-- svelte-ignore a11y_no_static_element_interactions --> 59 - <!-- svelte-ignore a11y_click_events_have_key_events --> 60 - <div 61 - use:_draggable={{ 62 - disabled: isOnMobile, 63 - applyUserSelectHack: true, 64 - handle: '.window-titlebar', 65 - onDragStart: (data) => { 66 - focusWindow(data.currentNode); 67 - } 68 - }} 69 - onclick={(data) => { 70 - focusWindow(data.currentTarget); 71 - }} 72 - class=" 73 - relative flex flex-col w-full md:w-fit [height:fit-content] 74 - {center ? 'mx-auto' : ''} 75 - {layered ? 'col-[1] row-[1]' : ''} 76 - {sticky ? 'md:sticky md:-top-9' : ''} 77 - max-w-screen-sm lg:max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl 78 - {tooltip ? 'min-w-fit' : ''} 79 - bg-ralsei-black border-ralsei-white border-ridge 80 - {tooltip ? 'border-[6px] border-t-[9px]' : 'border-[7px] border-t-[12px]'} 81 - {isOnMobile || tooltip ? '' : 'hover:-translate-x-1 hover:translate-y-1'} 82 - animate-{chosenKeyframe} drop-shadow-[24px_24px_24px_rgba(1,1,1,0.8)] 83 - {style} 84 - " 85 - {id} 86 - > 87 - {#if title !== undefined} 88 - <div 89 - class=" 90 - window-titlebar p-1 border-ralsei-white border-8 91 - bg-gradient-to-l from-ralsei-pink-neon to-ralsei-black to-75% 92 - {!isOnMobile ? 'cursor-move' : ''} 93 - " 94 - style="border-style: hidden hidden ridge hidden;" 95 - > 96 - <div class="flex bg-opacity-100 pixelate-bg"> 97 - <h1 98 - class=" 99 - font-monospace text-xl text-ralsei-pink-regular 100 - grow justify-self-start self-center {entry ? 'p-name' : ''} 101 - " 102 - > 103 - {title} 104 - </h1> 105 - {#if iconUri !== ''} 106 - <img 107 - class="justify-self-end self-center max-h-7" 108 - style="image-rendering: pixelated;" 109 - src={iconUri} 110 - alt={iconUri} 111 - /> 112 - {/if} 113 - </div> 114 - </div> 115 - {/if} 116 - <div 117 - class=" 118 - {removePadding ? '' : tooltip ? 'p-1' : 'p-2'} bg-gradient-to-tl 119 - to-ralsei-pink-neon/15 from-ralsei-pink-regular/20 120 - " 121 - > 122 - {@render children?.()} 123 - </div> 124 - </div>
-142
src/hooks.server.ts
··· 1 - import { updateLastPosts } from '$lib/bluesky'; 2 - import { getLastTrack, updateNowPlayingTrack } from '$lib/lastfm'; 3 - import { steamReadLastGame, steamUpdateNowPlaying } from '$lib/steam'; 4 - import { updateCommits } from '$lib/activity'; 5 - import { ToadScheduler, SimpleIntervalJob, Task, AsyncTask } from 'toad-scheduler'; 6 - import { 7 - incrementFakeVisitCount, 8 - incrementLegitVisitCount, 9 - pushMetric, 10 - sendAllMetrics 11 - } from '$lib/metrics'; 12 - import { 13 - addLastVisitor, 14 - decrementVisitCount, 15 - incrementVisitCount, 16 - notifyDarkVisitors, 17 - removeLastVisitor 18 - } from '$lib/visits'; 19 - import { testUa } from '$lib/robots'; 20 - import { error, type Handle } from '@sveltejs/kit'; 21 - import { _fetchEntries } from './routes/(site)/guestbook/+page.server'; 22 - import { sequence } from '@sveltejs/kit/hooks'; 23 - 24 - const updateNowPlaying = async () => { 25 - try { 26 - await Promise.all([steamUpdateNowPlaying(), updateNowPlayingTrack()]); 27 - } catch (err) { 28 - console.log(`error while updating: ${err}`); 29 - } 30 - }; 31 - const refreshContent = async () => { 32 - try { 33 - await Promise.all([updateLastPosts(), _fetchEntries(), updateCommits(), sendAllMetrics()]); 34 - } catch (err) { 35 - console.log(`error while updating: ${err}`); 36 - } 37 - }; 38 - 39 - await Promise.all([updateNowPlaying(), refreshContent()]); 40 - 41 - const scheduler = new ToadScheduler(); 42 - scheduler.addSimpleIntervalJob( 43 - new SimpleIntervalJob( 44 - { seconds: 5 }, 45 - new AsyncTask('updateNowPlaying task', updateNowPlaying, (err) => 46 - console.log(`error while updateNowPlaying: ${err}`) 47 - ) 48 - ) 49 - ); 50 - scheduler.addSimpleIntervalJob( 51 - new SimpleIntervalJob( 52 - { seconds: 30 }, 53 - new AsyncTask('refreshContent task', refreshContent, (err) => 54 - console.log(`error while refreshContent: ${err}`) 55 - ) 56 - ) 57 - ); 58 - 59 - const corsHandler = (allowedOrigins = ['*']) => { 60 - return async ({ event, resolve }: Parameters<Handle>[0]) => { 61 - const origin = event.request.headers.get('origin'); 62 - 63 - const corsHeaders: Record<string, string> = { 64 - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 65 - 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 66 - }; 67 - 68 - if (allowedOrigins.includes('*')) 69 - corsHeaders['Access-Control-Allow-Origin'] = '*'; 70 - else if (origin && allowedOrigins.includes(origin)) { 71 - corsHeaders['Access-Control-Allow-Origin'] = origin; 72 - corsHeaders['Access-Control-Allow-Credentials'] = 'true'; 73 - } 74 - 75 - if (event.request.method === 'OPTIONS') 76 - return new Response(null, { headers: corsHeaders }); 77 - 78 - const response = await resolve(event); 79 - 80 - Object.entries(corsHeaders).forEach(([key, value]) => { 81 - response.headers.set(key, value); 82 - }); 83 - 84 - return response; 85 - }; 86 - } 87 - 88 - const handler = async ({ event, resolve }: Parameters<Handle>[0]) => { 89 - notifyDarkVisitors(event.url, event.request); // no await so it doesnt block 90 - 91 - const isPrefetch = () => { 92 - return ( 93 - event.request.headers.get('Sec-Purpose')?.includes('prefetch') || 94 - event.request.headers.get('Purpose')?.includes('prefetch') || 95 - event.request.headers.get('x-purpose')?.includes('preview') || 96 - event.request.headers.get('x-moz')?.includes('prefetch') 97 - ); 98 - }; 99 - const isApi = () => { 100 - return event.url.pathname.startsWith('/_api'); 101 - }; 102 - const isRss = () => { 103 - return event.url.pathname.endsWith('/_rss'); 104 - }; 105 - 106 - // block any requests if the user agent is disallowed by our robots txt 107 - const isFakeVisit = 108 - (await testUa(event.url.toString(), event.request.headers.get('user-agent') ?? '')) === false; 109 - if (isFakeVisit) { 110 - pushMetric({ gazesys_visit_fake_total: await incrementFakeVisitCount() }); 111 - throw error(403, 'get a better user agent silly'); 112 - } 113 - 114 - // only push metric if legit page visit (still want rss to count here though) 115 - const isPageVisit = !isApi() && !isPrefetch(); 116 - if (isPageVisit) pushMetric({ gazesys_visit_real_total: await incrementLegitVisitCount() }); 117 - 118 - // only add visitors if its a "legit" page visit 119 - let id = null; 120 - let valid = false; 121 - if (isPageVisit && !isRss()) { 122 - id = addLastVisitor(event.request, event.cookies); 123 - valid = await incrementVisitCount(event.request, event.cookies); 124 - } 125 - 126 - // actually resolve event 127 - const resp = await resolve(event); 128 - // remove visitors if it was a 404 129 - if (resp.status === 404) { 130 - if (id !== null) removeLastVisitor(id); 131 - if (valid) decrementVisitCount(); 132 - } 133 - 134 - return resp; 135 - }; 136 - 137 - const allowedOrigins = [ 138 - "https://gaze.systems", 139 - "https://ptr.pet", 140 - "https://poor.dog", 141 - ]; 142 - export const handle = sequence(corsHandler(allowedOrigins), handler);
-123
src/lib/activity.ts
··· 1 - import { get, writable } from 'svelte/store'; 2 - import { parseFeed } from '@rowanmanning/feed-parser'; 3 - 4 - const lastCommits = writable<Activity[]>([]); 5 - 6 - export const updateCommits = async () => { 7 - try { 8 - const githubFeed = await parseFeedToActivity('https://github.com/90-008.atom'); 9 - const codebergFeed = await parseFeedToActivity('https://codeberg.org/90-008.atom'); 10 - const tangledFeed = await fetchTangledActivity(); 11 - const mergedFeed = sortActivities( 12 - githubFeed.concat(codebergFeed).concat(tangledFeed) 13 - ).slice(0, 7); 14 - lastCommits.set(mergedFeed); 15 - } catch (why) { 16 - console.log('could not fetch git activity: ', why); 17 - } 18 - }; 19 - 20 - export const getLastActivity = () => { 21 - return get(lastCommits); 22 - }; 23 - 24 - type Activity = { 25 - source: string; 26 - description: string; 27 - link: string | null; 28 - date: Date | null; 29 - }; 30 - 31 - const toHex = (bytes: number[]): string => { 32 - return bytes.map((b) => b.toString(16).padStart(2, '0')).join(''); 33 - }; 34 - 35 - const fetchTangledActivity = async (): Promise<Activity[]> => { 36 - // todo: auto resolve pds and knots 37 - const did = 'did:plc:dfl62fgb7wtjj3fcbb72naae'; 38 - const pds = 'https://zwsp.xyz'; 39 - const knot = 'https://knot.gaze.systems'; 40 - const activities: Activity[] = []; 41 - 42 - try { 43 - // todo: fetch until we exhaust 44 - const listRes = await fetch( 45 - `${pds}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=sh.tangled.repo` 46 - ); 47 - if (!listRes.ok) return []; 48 - const listData = await listRes.json(); 49 - 50 - for (const record of listData.records || []) { 51 - const repoName = record.value.name; 52 - if (!repoName) continue; 53 - 54 - try { 55 - const logRes = await fetch( 56 - `${knot}/xrpc/sh.tangled.repo.log?repo=${did}/${repoName}` 57 - ); 58 - if (!logRes.ok) continue; 59 - const logData = await logRes.json(); 60 - 61 - const commits = logData.commits || []; 62 - 63 - for (const commit of commits) { 64 - const hash = commit.Hash ? toHex(commit.Hash) : ''; 65 - const message = commit.Message || ''; 66 - const dateStr = commit.Author?.When; 67 - 68 - activities.push({ 69 - source: 'tangled', 70 - description: `pushed ${repoName}: ${message}`, 71 - link: `https://tangled.sh/${did}/${repoName}/commit/${hash}`, 72 - date: dateStr ? new Date(dateStr) : null 73 - }); 74 - } 75 - } catch (err) { 76 - console.log(`could not fetch tangled log for ${repoName}:`, err); 77 - } 78 - } 79 - } catch (err) { 80 - console.log('could not fetch tangled repos:', err); 81 - } 82 - return activities; 83 - }; 84 - 85 - const parseFeedToActivity = async (url: string) => { 86 - const response = await fetch(url); 87 - const feed = parseFeed(await response.text()); 88 - 89 - const source = new URL(url).host.split('.')[0]; 90 - const results: Activity[] = []; 91 - for (const item of feed.items) { 92 - const description: string | null = item.description || item.title; 93 - if (description === null) continue; 94 - // dont count mirrored repos 95 - // TODO: probably can implement a deduplication algorithm 96 - if ( 97 - ['90-008/ark', '90-008/website', 'ark', 'website'].some((repo) => 98 - description.includes(repo) 99 - ) 100 - ) 101 - continue; 102 - // dont show activity that is just chore 103 - if (item.content?.includes('chore')) continue; 104 - const desc = description.split('</a>').at(1) || description.split('</a>').pop() || ''; 105 - results.push({ 106 - source, 107 - description: desc.replace(/^90-008 /, ""), 108 - link: item.url, 109 - date: item.published || item.updated 110 - }); 111 - } 112 - 113 - return results; 114 - }; 115 - 116 - const sortActivities = (activities: Array<Activity>) => { 117 - return activities.sort((a, b) => { 118 - if (a.date === null && b.date === null) return 0; 119 - if (a.date === null) return 1; 120 - if (b.date === null) return -1; 121 - return b.date.getTime() - a.date.getTime(); 122 - }); 123 - };
-26
src/lib/apiToken.ts
··· 1 - import { nanoid } from 'nanoid'; 2 - import { get, writable } from 'svelte/store'; 3 - 4 - const tokens = writable<Map<string, number>>(new Map()); 5 - 6 - export const newToken = () => { 7 - const token = nanoid(100); 8 - tokens.update((v) => v.set(token, Date.now())); 9 - return token; 10 - }; 11 - export const useToken = (token: string) => { 12 - const _tokens = get(tokens); 13 - // delete older tokens 14 - for (const [_token, timestamp] of _tokens) { 15 - if (Date.now() - timestamp > 30 * 60 * 1000) { 16 - _tokens.delete(_token); 17 - } 18 - } 19 - tokens.set(_tokens); 20 - return _tokens.has(token); 21 - }; 22 - 23 - export const checkUrl = (url: URL) => { 24 - const token = url.searchParams.get('_token'); 25 - return token !== null && useToken(token); 26 - };
-60
src/lib/bluesky.ts
··· 1 - import { env } from '$env/dynamic/private'; 2 - import { Bot, type Post } from '@skyware/bot'; 3 - import { get, writable } from 'svelte/store'; 4 - 5 - const bskyClient = writable<null | Bot>(null); 6 - 7 - export const getBskyClient = async () => { 8 - let client = get(bskyClient); 9 - if (client === null) { 10 - client = await loginToBsky(); 11 - bskyClient.set(client); 12 - } 13 - return client; 14 - }; 15 - 16 - const loginToBsky = async () => { 17 - const password = env.BSKY_PASSWORD ?? null; 18 - if (password === null) { 19 - throw new Error('no password provided'); 20 - } 21 - const bot = new Bot({ service: 'https://gaze.systems' }); 22 - await bot.login({ identifier: 'guestbook.gaze.systems', password }); 23 - return bot; 24 - }; 25 - 26 - export const getUserPosts = async ( 27 - did: string, 28 - count: number = 10, 29 - cursor: string | null = null 30 - ) => { 31 - const client = await getBskyClient(); 32 - let feedCursor: string | null | undefined = cursor; 33 - const posts: Post[] = []; 34 - // fetch requested amount of posts 35 - while (posts.length < count - 1 && (typeof feedCursor === 'string' || feedCursor === null)) { 36 - const feedData = await client.getUserPosts(did, { 37 - limit: count, 38 - filter: 'posts_no_replies', 39 - cursor: feedCursor === null ? undefined : feedCursor 40 - }); 41 - posts.push(...feedData.posts.filter((post) => post.author.did === did)); 42 - feedCursor = feedData.cursor; 43 - } 44 - return { posts, cursor: feedCursor === null ? undefined : feedCursor }; 45 - }; 46 - 47 - const lastPosts = writable<Post[]>([]); 48 - 49 - export const updateLastPosts = async () => { 50 - try { 51 - const { posts } = await getUserPosts('did:plc:dfl62fgb7wtjj3fcbb72naae', 10); 52 - lastPosts.set(posts); 53 - } catch (err) { 54 - console.log(`can't update last posts ${err}`); 55 - } 56 - }; 57 - 58 - export const getLastPosts = () => { 59 - return get(lastPosts); 60 - };
-22
src/lib/convertDate.ts
··· 1 - const months = [ 2 - 'Jan', 3 - 'Feb', 4 - 'Mar', 5 - 'Apr', 6 - 'May', 7 - 'Jun', 8 - 'Jul', 9 - 'Aug', 10 - 'Sep', 11 - 'Oct', 12 - 'Nov', 13 - 'Dec' 14 - ]; 15 - 16 - const convertDate = (published: string) => { 17 - const date = published.substring(0, 10); 18 - const [year, month, day] = date.split('-'); 19 - return `${day} ${months[parseInt(month) - 1]} ${year}`; 20 - }; 21 - 22 - export default convertDate;
-35
src/lib/counter.ts
··· 1 - import { get, writable } from 'svelte/store'; 2 - 3 - /** 4 - * Creates a persistent counter that is stored in a file 5 - * @param fileName The name of the file to store the count in 6 - * @param initialValue The initial value if the file doesn't exist 7 - * @returns An object with methods to get, increment, and set the count 8 - */ 9 - export const createFileCounter = async (filePath: string, initialValue: number = 0) => { 10 - let countRaw: string | null = null; 11 - try { 12 - countRaw = await Deno.readTextFile(filePath); 13 - } catch {} 14 - 15 - const counter = writable(parseInt(countRaw ?? initialValue.toString())); 16 - 17 - const saveToFile = async (value: number) => { 18 - await Deno.writeTextFile(filePath, value.toString()); 19 - return value; 20 - }; 21 - 22 - return { 23 - get: () => get(counter), 24 - increment: (amount: number = 1) => { 25 - const currentValue = get(counter) + amount; 26 - counter.set(currentValue); 27 - return saveToFile(currentValue); 28 - }, 29 - set: (value: number) => { 30 - counter.set(value); 31 - return saveToFile(value); 32 - }, 33 - subscribe: counter.subscribe 34 - }; 35 - };
-25
src/lib/dateFmt.ts
··· 1 - export const renderRelativeDate = (timestamp: number) => { 2 - const elapsed = timestamp - new Date().getTime(); 3 - const units: Record<string, number> = { 4 - year: 24 * 60 * 60 * 1000 * 365, 5 - month: (24 * 60 * 60 * 1000 * 365) / 12, 6 - day: 24 * 60 * 60 * 1000, 7 - hour: 60 * 60 * 1000, 8 - minute: 60 * 1000, 9 - second: 1000 10 - }; 11 - const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); 12 - for (const unit in units) 13 - if (Math.abs(elapsed) > units[unit] || unit == 'second') 14 - return rtf.format(Math.round(elapsed / units[unit]), unit as Intl.RelativeTimeFormatUnit); 15 - return ''; 16 - }; 17 - export const renderDate = (timestamp: number) => { 18 - return new Date(timestamp).toLocaleString('en-GB', { 19 - year: '2-digit', 20 - month: '2-digit', 21 - day: '2-digit', 22 - hour: '2-digit', 23 - minute: '2-digit' 24 - }); 25 - };
-23
src/lib/dollcode.ts
··· 1 - // https://noe.sh/dollcode/ 2 - const charmap = ['โ–Œ', 'โ––', 'โ–˜']; 3 - export const genDollcode = (number: number) => { 4 - const output = []; 5 - let window = number; 6 - let loopProtection = 1000; 7 - 8 - while (loopProtection > 0 && window > 0) { 9 - const mod = window % 3; 10 - 11 - if (mod == 0) { 12 - window = (window - 3) / 3; 13 - } else { 14 - window = (window - mod) / 3; 15 - } 16 - 17 - output.unshift(charmap[mod]); 18 - 19 - loopProtection--; 20 - } 21 - 22 - return output.join(''); 23 - };
-11
src/lib/getTitle.ts
··· 1 - const getTitle = (path: string) => { 2 - let sl = path.split('/'); 3 - sl = sl.splice(1); 4 - if (sl.length > 2) { 5 - sl[0] = sl[0][0]; 6 - } 7 - const newPath = sl.join('/'); 8 - return '//โ‹†โ˜€๏ธŽ. /' + newPath; 9 - }; 10 - 11 - export default getTitle;
-28
src/lib/index.ts
··· 1 - import type { Cookies } from '@sveltejs/kit'; 2 - import { hash } from 'node:crypto'; 3 - 4 - export const scopeCookies = (cookies: Cookies, path: string) => { 5 - return { 6 - get: (key: string) => { 7 - return cookies.get(key); 8 - }, 9 - set: (key: string, value: string, props: Omit<Parameters<Cookies['set']>[2], 'path'> = {}) => { 10 - cookies.set(key, value, { ...props, path }); 11 - }, 12 - delete: (key: string, props: Omit<Parameters<Cookies['delete']>[1], 'path'> = {}) => { 13 - cookies.delete(key, { ...props, path }); 14 - } 15 - }; 16 - }; 17 - 18 - const cipherChars = ['#', '%', '+', '=', '//']; 19 - export const fancyText = (input: string) => { 20 - const hashed = hash('sha256', input, 'hex'); 21 - let result = ''; 22 - let idx = 0; 23 - while (idx < hashed.length) { 24 - result += cipherChars[hashed.charCodeAt(idx) % cipherChars.length]; 25 - idx += 1; 26 - } 27 - return result; 28 - };
-103
src/lib/lastfm.ts
··· 1 - import { env } from '$env/dynamic/private'; 2 - import { get, writable } from 'svelte/store'; 3 - 4 - const DID = 'did:plc:dfl62fgb7wtjj3fcbb72naae'; 5 - const PDS = 'https://zwsp.xyz'; 6 - const LAST_TRACK_FILE = `${env.WEBSITE_DATA_DIR}/last_track.json`; 7 - 8 - type LastTrack = { 9 - name: string; 10 - artist: string; 11 - album: string; 12 - images: { 13 - mb: string | null; 14 - yt: string | null; 15 - }; 16 - link: string | null; 17 - when: number; 18 - status: 'playing' | 'played'; 19 - }; 20 - const lastTrack = writable<LastTrack | null>(null); 21 - 22 - export const getLastTrack = async () => { 23 - try { 24 - const data = await Deno.readTextFile(LAST_TRACK_FILE); 25 - lastTrack.set(JSON.parse(data)); 26 - } catch (why) { 27 - console.log('could not read last track: ', why); 28 - lastTrack.set(null); 29 - } 30 - }; 31 - 32 - const getTrackCoverArt = (releaseMbId: string | null | undefined, originUrl: string | null | undefined) => { 33 - let mb: string | null = null; 34 - let yt: string | null = null; 35 - 36 - if (releaseMbId) mb = `https://coverartarchive.org/release/${releaseMbId}/front-250`; 37 - 38 - try { 39 - if (originUrl) { 40 - let videoId: string | null = null; 41 - if (originUrl.includes('youtube.com') || originUrl.includes('music.youtube.com')) { 42 - videoId = new URL(originUrl).searchParams.get('v'); 43 - } else if (originUrl.includes('youtu.be')) { 44 - videoId = originUrl.split('youtu.be/')[1]?.split('?')[0]; 45 - } 46 - if (videoId) yt = `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`; 47 - } 48 - } catch { 49 - } 50 - 51 - return { mb, yt }; 52 - }; 53 - 54 - const joinArtists = (artists: any[]) => { 55 - if (!artists || artists.length === 0) return null; 56 - return artists.map((a) => a.artistName).join(', '); 57 - }; 58 - 59 - export const updateNowPlayingTrack = async () => { 60 - try { 61 - let track: any = null; 62 - let when: number = Date.now(); 63 - let status: 'playing' | 'played' = 'played'; 64 - 65 - try { 66 - const statusRes = await fetch( 67 - `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${DID}&collection=fm.teal.alpha.actor.status&rkey=self` 68 - ); 69 - if (statusRes.ok) { 70 - const statusData = await statusRes.json(); 71 - if (statusData.value?.item) { 72 - const metadata = statusData.value; 73 - track = statusData.value.item; 74 - if (track.playedTime) when = new Date(track.playedTime).getTime(); 75 - status = ((Date.now() / 1000) >= (parseInt(metadata.time) + track.duration)) ? 'played' : 'playing'; 76 - } 77 - } 78 - } catch (err) { 79 - console.log('could not fetch teal status:', err); 80 - } 81 - 82 - if (!track) return; 83 - 84 - const data: LastTrack = { 85 - name: track.trackName, 86 - artist: joinArtists(track.artists) ?? 'Unknown Artist', 87 - album: track.releaseName ?? 'Unknown Album', 88 - images: getTrackCoverArt(track.releaseMbId, track.originUrl), 89 - link: track.originUrl ?? null, 90 - when: when, 91 - status: status 92 - }; 93 - 94 - lastTrack.set(data); 95 - await Deno.writeTextFile(LAST_TRACK_FILE, JSON.stringify(data)); 96 - } catch (why) { 97 - console.log('could not fetch teal fm: ', why); 98 - } 99 - }; 100 - 101 - export const getNowPlayingTrack = () => { 102 - return get(lastTrack); 103 - };
-52
src/lib/metrics.ts
··· 1 - import { env } from '$env/dynamic/private'; 2 - import { pushMetrics } from 'prometheus-remote-write'; 3 - import { createFileCounter } from './counter'; 4 - 5 - const endpoint = env.PROMETHEUS_URL; 6 - 7 - export const pushMetric = async ( 8 - metrics: Record<string, number>, 9 - labels: Record<string, string> = {} 10 - ) => { 11 - if (endpoint === undefined) return; 12 - try { 13 - const result = await pushMetrics(metrics, { 14 - url: endpoint, 15 - labels: { 16 - service: 'website', 17 - ...labels 18 - } 19 - }); 20 - if (result.status != 204) { 21 - throw new Error(`failed to push metrics: ${result.status} ${result.errorMessage}`); 22 - } 23 - } catch (err) { 24 - console.log(`failed to push metrics: ${err}`); 25 - } 26 - }; 27 - 28 - export const sendAllMetrics = async () => { 29 - try { 30 - await pushMetric({ 31 - gazesys_pet_bounce_total: bounceCount.get(), 32 - gazesys_visit_fake_total: fakeVisitCount.get(), 33 - gazesys_visit_real_total: legitVisitCount.get(), 34 - gazesys_pet_distance_total: distanceTravelled.get() 35 - }); 36 - } catch (error) { 37 - console.log(`failed to push metrics: ${error}`); 38 - } 39 - }; 40 - 41 - export const bounceCount = await createFileCounter(`${env.WEBSITE_DATA_DIR}/bouncecount`); 42 - export const incrementBounceCount = bounceCount.increment; 43 - 44 - export const legitVisitCount = await createFileCounter(`${env.WEBSITE_DATA_DIR}/legitvisitcount`); 45 - export const incrementLegitVisitCount = legitVisitCount.increment; 46 - 47 - export const fakeVisitCount = await createFileCounter(`${env.WEBSITE_DATA_DIR}/fakevisitcount`); 48 - export const incrementFakeVisitCount = fakeVisitCount.increment; 49 - 50 - export const distanceTravelled = await createFileCounter( 51 - `${env.WEBSITE_DATA_DIR}/distancetravelled` 52 - );
-32
src/lib/pushnotif.ts
··· 1 - import { env } from '$env/dynamic/private'; 2 - 3 - export const pushNotification = (_content: string) => { 4 - const content = encodeURIComponent(_content); 5 - try { 6 - // post to phone 7 - fetch( 8 - `https://api.day.app/${env.BARK_DEVICE_ID}/gaze.systems/${content}?icon=https://gaze.systems/icons/gaze_site.webp` 9 - ); 10 - // post to discord 11 - fetch(env.DISCORD_NOTIF_WEBHOOK || '', { 12 - method: 'POST', 13 - headers: { 14 - 'content-type': 'application/json' 15 - }, 16 - body: JSON.stringify({ 17 - content: '<@853064602904166430>', 18 - embeds: [ 19 - { 20 - id: 677465216, 21 - title: _content, 22 - footer: { 23 - text: 'notif from gaze.systems' 24 - } 25 - } 26 - ] 27 - }) 28 - }); 29 - } catch (err) { 30 - console.log(`failed to push notification: ${err}`); 31 - } 32 - };
-54
src/lib/robots.ts
··· 1 - import { env } from '$env/dynamic/private' 2 - import { get, writable } from 'svelte/store' 3 - import { type Robot } from 'robots-parser' 4 - import robotsParser from 'robots-parser' 5 - import { PUBLIC_BASE_URL } from '$env/static/public' 6 - 7 - const cachedParsedRobots = writable<Robot | null>(null) 8 - const cachedRobots = writable<string>("") 9 - const lastFetched = writable<number>(Date.now()) 10 - 11 - const fetchRobotsTxt = async () => { 12 - const robotsTxtResp = await fetch( 13 - "https://api.darkvisitors.com/robots-txts", 14 - { 15 - method: "POST", 16 - headers: { 17 - "Authorization": `Bearer ${env.DARK_VISITORS_TOKEN}`, 18 - "Content-Type": "application/json" 19 - }, 20 - body: JSON.stringify({ 21 - agent_types: [ 22 - "AI Assistant", 23 - "AI Data Scraper", 24 - "AI Search Crawler", 25 - "Undocumented AI Agent", 26 - ], 27 - disallow: "/" 28 - }) 29 - } 30 - ) 31 - const robotsTxt = await robotsTxtResp.text() 32 - lastFetched.set(Date.now()) 33 - return robotsTxt 34 - } 35 - 36 - export const getRobotsTxt = async () => { 37 - let robotsTxt = get(cachedRobots) 38 - if (robotsTxt.length === 0 || Date.now() - get(lastFetched) > 1000 * 60 * 60 * 24) { 39 - robotsTxt = await fetchRobotsTxt() 40 - cachedRobots.set(robotsTxt) 41 - cachedParsedRobots.set(robotsParser(`${PUBLIC_BASE_URL}/robots.txt`, robotsTxt)) 42 - } 43 - return robotsTxt 44 - } 45 - 46 - export const testUa = async (url: string, ua: string) => { 47 - if (ua.length === 0) return false 48 - let parsedRobots = get(cachedParsedRobots) 49 - if (parsedRobots === null) { 50 - parsedRobots = robotsParser(`${PUBLIC_BASE_URL}/robots.txt`, await getRobotsTxt()) 51 - cachedParsedRobots.set(parsedRobots) 52 - } 53 - return parsedRobots.isAllowed(url, ua) 54 - }
-73
src/lib/steam.ts
··· 1 - import { env } from '$env/dynamic/private'; 2 - import SGDB from 'steamgriddb'; 3 - import { get, writable } from 'svelte/store'; 4 - 5 - const STEAM_ID = '76561198106829949'; 6 - const GET_PLAYER_SUMMARY_ENDPOINT = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=${STEAM_ID}&format=json`; 7 - const LAST_GAME_FILE = `${env.WEBSITE_DATA_DIR}/last_game.json`; 8 - 9 - type LastGame = { 10 - name: string; 11 - link: string; 12 - icon: string; 13 - pfp: string; 14 - when: number; 15 - playing: boolean; 16 - }; 17 - 18 - const steamgriddbClient = writable<SGDB | null>(null); 19 - const lastGame = writable<LastGame | null>(null); 20 - 21 - export const steamReadLastGame = async () => { 22 - try { 23 - const data = await Deno.readTextFile(LAST_GAME_FILE); 24 - lastGame.set(JSON.parse(data)); 25 - } catch (why) { 26 - console.log('could not read last game: ', why); 27 - lastGame.set(null); 28 - } 29 - }; 30 - 31 - export const steamUpdateNowPlaying = async () => { 32 - let griddbClient = get(steamgriddbClient); 33 - if (griddbClient === null) { 34 - griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY); 35 - steamgriddbClient.set(griddbClient); 36 - } 37 - try { 38 - const profile = (await (await fetch(GET_PLAYER_SUMMARY_ENDPOINT)).json()).response.players[0]; 39 - if (!profile.gameid) { 40 - lastGame.update((t) => { 41 - if (t !== null) { 42 - t.playing = false; 43 - } 44 - return t; 45 - }); 46 - return; 47 - } 48 - const icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official', 'custom']); 49 - //console.log(icons) 50 - const game: LastGame = { 51 - name: profile.gameextrainfo, 52 - link: `https://store.steampowered.com/app/${profile.gameid}`, 53 - icon: icons[0].thumb.toString(), 54 - pfp: profile.avatarmedium, 55 - when: Date.now(), 56 - playing: true 57 - }; 58 - lastGame.set(game); 59 - await Deno.writeTextFile(LAST_GAME_FILE, JSON.stringify(game)); 60 - } catch (why) { 61 - console.log('could not fetch steam: ', why); 62 - lastGame.update((t) => { 63 - if (t !== null) { 64 - t.playing = false; 65 - } 66 - return t; 67 - }); 68 - } 69 - }; 70 - 71 - export const getLastGame = () => { 72 - return get(lastGame); 73 - };
-140
src/lib/visits.ts
··· 1 - import { env } from '$env/dynamic/private'; 2 - import { scopeCookies } from '$lib'; 3 - import type { Cookies } from '@sveltejs/kit'; 4 - import { nanoid } from 'nanoid'; 5 - import { get, writable } from 'svelte/store'; 6 - 7 - const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`; 8 - const readVisitCount = async () => { 9 - try { 10 - return parseInt(await Deno.readTextFile(visitCountFile)); 11 - } catch { 12 - return 0; 13 - } 14 - }; 15 - export const visitCount = writable(await readVisitCount()); 16 - 17 - export type Visitor = { visits: number[] }; 18 - export const lastVisitors = writable<Map<string, Visitor>>(new Map()); 19 - const VISITOR_EXPIRY_SECONDS = 60 * 60; // an hour seems reasonable 20 - 21 - export const decrementVisitCount = () => { 22 - visitCount.set(get(visitCount) - 1); 23 - }; 24 - 25 - export const incrementVisitCount = async (request: Request, cookies: Cookies) => { 26 - let currentVisitCount = get(visitCount); 27 - // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) 28 - if (isBot(request)) return false; 29 - const scopedCookies = scopeCookies(cookies, '/'); 30 - // parse the last visit timestamp from cookies if it exists 31 - const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || '0'); 32 - // get unix timestamp 33 - const currentTime = Date.now(); 34 - const timeSinceVisit = currentTime - visitedTimestamp; 35 - // check if this is the first time a client is visiting or if an hour has passed since they last visited 36 - if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) { 37 - // increment current and write to the store 38 - currentVisitCount += 1; 39 - visitCount.set(currentVisitCount); 40 - // update the cookie with the current timestamp 41 - scopedCookies.set('visitedTimestamp', currentTime.toString()); 42 - // write the visit count to a file so we can load it later again 43 - await Deno.writeTextFile(visitCountFile, currentVisitCount.toString()); 44 - } 45 - return true; 46 - }; 47 - 48 - export const removeLastVisitor = (id: string) => { 49 - const visitors = get(lastVisitors); 50 - if (visitors.has(id)) { 51 - const visitor = visitors.get(id) ?? { visits: [] }; 52 - visitor?.visits.shift(); 53 - // if not enough visits remove 54 - if (visitor?.visits.length === 0) { 55 - visitors.delete(id); 56 - } else { 57 - visitors.set(id, visitor); 58 - } 59 - } 60 - lastVisitors.set(visitors); 61 - }; 62 - 63 - export const addLastVisitor = (request: Request, cookies: Cookies) => { 64 - const { visitors, visitorId } = _addLastVisitor(get(lastVisitors), request, cookies); 65 - lastVisitors.set(visitors); 66 - return visitorId; 67 - }; 68 - 69 - export const getVisitorId = (cookies: Cookies) => { 70 - const scopedCookies = scopeCookies(cookies, '/'); 71 - // parse the last visit timestamp from cookies if it exists 72 - return scopedCookies.get('visitorId'); 73 - }; 74 - 75 - // why not use this for incrementVisitCount? cuz i wanna have separate visit counts (one per hour and one per day, per hour being recent visitors) 76 - const _addLastVisitor = (visitors: Map<string, Visitor>, request: Request, cookies: Cookies) => { 77 - const currentTime = Date.now(); 78 - // filter out old entries 79 - visitors.forEach((visitor, id, map) => { 80 - if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS) map.delete(id); 81 - else { 82 - visitor.visits = visitor.visits.filter((since) => { 83 - return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS; 84 - }); 85 - map.set(id, visitor); 86 - } 87 - }); 88 - // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) 89 - if (isBot(request)) return { visitors, visitorId: null }; 90 - const scopedCookies = scopeCookies(cookies, '/'); 91 - // parse the last visit timestamp from cookies if it exists 92 - let visitorId = scopedCookies.get('visitorId') || ''; 93 - // if no such id exists, create one and assign it to the client 94 - if (!visitors.has(visitorId)) { 95 - visitorId = nanoid(); 96 - scopedCookies.set('visitorId', visitorId); 97 - console.log(`new client visitor id ${visitorId}`); 98 - } 99 - // update the entry 100 - const visitorEntry = visitors.get(visitorId) || { visits: [] }; 101 - // put new visit in the front 102 - visitorEntry.visits = [currentTime].concat(visitorEntry.visits); 103 - visitors.set(visitorId, visitorEntry); 104 - return { 105 - visitors, 106 - visitorId 107 - }; 108 - }; 109 - 110 - export const isBot = (request: Request) => { 111 - const ua = request.headers.get('user-agent'); 112 - return ua 113 - ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null 114 - : true; 115 - }; 116 - 117 - export const notifyDarkVisitors = (url: URL, request: Request) => { 118 - fetch('https://api.darkvisitors.com/visits', { 119 - method: 'POST', 120 - headers: { 121 - authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`, 122 - 'content-type': 'application/json' 123 - }, 124 - body: JSON.stringify({ 125 - request_path: url.pathname, 126 - request_method: request.method, 127 - request_headers: request.headers 128 - }) 129 - }) 130 - .catch((why) => { 131 - console.log('failed sending dark visitors analytics:', why); 132 - return null; 133 - }) 134 - .then((resp) => { 135 - if (resp !== null) { 136 - const host = `(${request.headers.get('host')}|${request.headers.get('x-real-ip')}|${request.headers.get('user-agent')})`; 137 - console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${host}`); 138 - } 139 - }); 140 - };
-20
src/lib/window.ts
··· 1 - /* eslint-disable no-useless-escape */ 2 - import { writable } from 'svelte/store'; 3 - 4 - export const highestZIndex = writable(0); 5 - 6 - export const isMobile = () => { 7 - let check = false; 8 - (function (a) { 9 - if ( 10 - /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( 11 - a 12 - ) || 13 - /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( 14 - a.slice(0, 4) 15 - ) 16 - ) 17 - check = true; 18 - })(navigator.userAgent || navigator.vendor); 19 - return check; 20 - };
-67
src/routes/(site)/+layout.server.ts
··· 1 - import { newToken as getApiToken } from '$lib/apiToken.js'; 2 - import { bounceCount, distanceTravelled } from '$lib/metrics.js'; 3 - import { lastVisitors, visitCount } from '$lib/visits.js'; 4 - import { get } from 'svelte/store'; 5 - 6 - export const csr = true; 7 - export const ssr = true; 8 - export const prerender = false; 9 - export const trailingSlash = 'always'; 10 - 11 - export async function load({ url }) { 12 - const visitors = get(lastVisitors); 13 - let recentVisitCount = 0; 14 - for (const [, visitor] of visitors) { 15 - recentVisitCount += visitor.visits.length; 16 - } 17 - 18 - const eyePositions = []; 19 - const usedPositions = []; 20 - for (let i = 0; i < Math.min(visitors.size, 10); i++) { 21 - let maxMinDistance = 0; 22 - let bestPosition = null; 23 - 24 - // Try multiple positions and keep the one with largest minimum distance to existing points 25 - for (let attempt = 0; attempt < 50; attempt++) { 26 - const sidePreference = Math.random() < 0.5; 27 - const testLeft = sidePreference 28 - ? Math.random() * 30 // Left side 29 - : 60 + Math.random() * 30; // Right side 30 - const testTop = Math.random() * 80; 31 - 32 - let currentMinDistance = Infinity; 33 - 34 - // Calculate minimum distance to all existing points 35 - for (const pos of usedPositions) { 36 - const distance = Math.sqrt( 37 - Math.pow(testLeft - pos.left, 2) + Math.pow(testTop - pos.top, 2) 38 - ); 39 - currentMinDistance = Math.min(currentMinDistance, distance); 40 - } 41 - 42 - // If this position has a larger minimum distance, keep it 43 - if (currentMinDistance > maxMinDistance) { 44 - maxMinDistance = currentMinDistance; 45 - bestPosition = { left: testLeft, top: testTop }; 46 - } 47 - } 48 - 49 - // Use the best position found 50 - const left = bestPosition ? bestPosition.left : Math.random() * 90; 51 - const top = bestPosition ? bestPosition.top : Math.random() * 80; 52 - 53 - usedPositions.push({ left, top }); 54 - eyePositions.push([top, left]); 55 - } 56 - 57 - return { 58 - route: url.pathname, 59 - petTotalBounce: bounceCount.get(), 60 - petTotalDistance: distanceTravelled.get(), 61 - visitCount: get(visitCount), 62 - lastVisitors: visitors, 63 - recentVisitCount, 64 - eyePositions, 65 - apiToken: getApiToken() 66 - }; 67 - }
-284
src/routes/(site)/+layout.svelte
··· 1 - <script lang="ts"> 2 - import { browser } from '$app/environment'; 3 - import getTitle from '$lib/getTitle'; 4 - import Eye from '$components/eye.svelte'; 5 - import NavButton from '$components/navButton.svelte'; 6 - import Pet, { localBounces, localDistanceTravelled } from '$components/pet.svelte'; 7 - import Tooltip from '$components/tooltip.svelte'; 8 - import '$styles/app.css'; 9 - 10 - interface Props { 11 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 12 - data: any; 13 - children?: import('svelte').Snippet; 14 - } 15 - 16 - let { data, children }: Props = $props(); 17 - 18 - interface MenuItem { 19 - href: string; 20 - name: string; 21 - iconUri: string; 22 - } 23 - 24 - const menuItems: MenuItem[] = [ 25 - { href: '', name: 'home', iconUri: '/icons/home.webp' }, 26 - { href: 'entries', name: 'entries', iconUri: '/icons/entries.webp' }, 27 - { href: 'guestbook', name: 'guestbook', iconUri: '/icons/guestbook.webp' }, 28 - { href: 'about', name: 'about', iconUri: '/icons/about.webp' } 29 - ]; 30 - 31 - let routeComponents = $derived(data.route.split('/')); 32 - let isEntryPage = $derived(routeComponents.length > 3 && routeComponents[1] === 'entries'); 33 - let isResumePage = $derived(routeComponents[1] === 'resume'); 34 - let isRoute = $derived((_route: string) => { 35 - if (isEntryPage) { 36 - if (_route === 'entries') { 37 - return false; 38 - } else if (_route.startsWith('entries/')) { 39 - return true; 40 - } 41 - } 42 - return _route === routeComponents[1]; 43 - }); 44 - 45 - let title = $derived(getTitle(data.route)); 46 - 47 - const svgSquiggles = [[2], [3], [2], [3], [1]]; 48 - 49 - // svelte-ignore non_reactive_update 50 - let eyePositions = null; 51 - if (eyePositions === null) { 52 - eyePositions = data.eyePositions; 53 - } 54 - </script> 55 - 56 - <svelte:head> 57 - <title>{title}</title> 58 - <meta property="og:site_name" content="gaze.systems" /> 59 - <meta property="og:url" content="https://gaze.systems/" /> 60 - <meta property="og:image" content="https://gaze.systems/icons/gaze_website.webp" /> 61 - </svelte:head> 62 - 63 - <div 64 - class=" 65 - app-grid-background motion-safe:app-grid-background-anim 66 - fixed -z-10 w-full [height:100%] top-0 left-0 67 - " 68 - ></div> 69 - <div 70 - class=" 71 - app-grid-background-second-layer motion-safe:app-grid-background-second-layer-anim 72 - fixed -z-20 w-full [height:100%] top-0 left-0 73 - " 74 - ></div> 75 - 76 - <svg 77 - xmlns="http://www.w3.org/2000/svg" 78 - version="1.1" 79 - class="absolute -z-50" 80 - image-rendering="optimizeSpeed" 81 - > 82 - <defs> 83 - {#each svgSquiggles as [scale], index (index)} 84 - <filter id="squiggly-{index}"> 85 - <feTurbulence 86 - id="turbulence" 87 - baseFrequency="0.03" 88 - numOctaves="3" 89 - result="noise" 90 - seed={index} 91 - /> 92 - <feDisplacementMap in="SourceGraphic" in2="noise" {scale} /> 93 - </filter> 94 - {/each} 95 - <filter id="pixelate" color-interpolation-filters="linearRGB" x="0" y="0"> 96 - <feFlood x="4" y="4" height="2" width="2" /> 97 - <feComposite width="10" height="10" /> 98 - <feTile result="a" /> 99 - <feComposite in="SourceGraphic" in2="a" operator="in" /> 100 - <feMorphology operator="dilate" radius="5" /> 101 - </filter> 102 - <filter id="dither" color-interpolation-filters="sRGB" x="0" y="0" width="100%" height="100%"> 103 - <feImage 104 - width="4" 105 - height="4" 106 - xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAASElEQVR42gXBgQAAIAxFwW8QwhBCCCGEIYQQQgghhBBCCEMYwutOkphzYmbsvdG9l9YaEYG7o1or5xxKKay1UGYyxuC9R++dD7yGJkTj6F0HAAAAAElFTkSuQmCC" 107 - /> 108 - <feTile /> 109 - <feComposite operator="arithmetic" k1="0" k2="1" k3="1" k4="-0.5" in="SourceGraphic" /> 110 - <feComponentTransfer> 111 - <feFuncR type="discrete" tableValues="0 1" /> 112 - <feFuncG type="discrete" tableValues="0 1" /> 113 - <feFuncB type="discrete" tableValues="0 1" /> 114 - </feComponentTransfer> 115 - </filter> 116 - <filter 117 - id="dither-red" 118 - color-interpolation-filters="sRGB" 119 - x="0" 120 - y="0" 121 - width="100%" 122 - height="100%" 123 - > 124 - <feFlood flood-color="#000000" flood-opacity="0.50" x="0%" y="0%" result="flood" /> 125 - <feBlend mode="normal" x="0%" y="0%" in="SourceGraphic" in2="flood" result="blend1" /> 126 - <feImage 127 - class="ditherImage" 128 - xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA5ElEQVQYlQXBgQbCUABA0fdrk0ySSZJJkiRJMjOTTGZmkiRJZiYzyczMzGQmfdrtHPH7/TgcDuR5zna7pWka9vs9aZqyXq8R0+mU5/OJoihcLhfG4zFBENDtdjmdToj3+81yueTz+WCaJnEcM5/PKcsSXdcRsizjeR6j0YjH40Gr1cJxHAaDAbfbDVHXNbvdjiRJWK1WfL9fLMsiyzI2mw1CVVV836fT6XA8HplMJoRhSK/X43w+I6IoYjabURQFmqbxer1YLBZUVYVhGAhJkrBtm36/z/V6pd1u47ouw+GQ+/3OH4/Fn8FvF/NxAAAAAElFTkSuQmCC" 129 - x="0" 130 - y="0" 131 - width="4" 132 - height="4" 133 - crossOrigin="anonymous" 134 - result="image1" 135 - /> 136 - <feTile x="0" y="0" in="image1" result="tile" /> 137 - <feBlend mode="overlay" x="0%" y="0%" in="blend1" in2="tile" result="blend2" /> 138 - <feColorMatrix type="saturate" values="0" /> 139 - <feComponentTransfer> 140 - <feFuncR type="discrete" tableValues="0 0" /> 141 - <feFuncG type="discrete" tableValues="0 1" /> 142 - <feFuncB type="discrete" tableValues="0 1" /> 143 - </feComponentTransfer> 144 - </filter> 145 - </defs> 146 - </svg> 147 - 148 - {#if !isResumePage} 149 - {#each data.lastVisitors as [id, visitor], index (id)} 150 - {@const pos = eyePositions.at(index)} 151 - {#if pos !== undefined} 152 - <Eye visits={visitor.visits} {id} top={pos[0]} left={pos[1]} /> 153 - {/if} 154 - {/each} 155 - {/if} 156 - 157 - <div 158 - class="md:h-[96vh] pb-[8vh] lg:px-[1vw] 2xl:px-[2vw] lg:pb-[3vh] lg:pt-[1vh] overflow-x-hidden [scrollbar-gutter:stable]" 159 - > 160 - {@render children?.()} 161 - </div> 162 - 163 - {#if !isResumePage} 164 - <Pet apiToken={data.apiToken} /> 165 - {/if} 166 - 167 - <nav class="w-full min-h-[5vh] max-h-[5vh] fixed bottom-0 z-[999] bg-ralsei-black overflow-visible"> 168 - <div 169 - class=" 170 - max-w-full max-h-fit p-1 z-[999] 171 - border-ralsei-white border-8 172 - bg-gradient-to-r to-ralsei-pink-neon/30 from-ralsei-pink-regular/20 from-30% 173 - " 174 - style="border-style: ridge hidden hidden hidden;" 175 - > 176 - <div class="flex flex-row flex-nowrap gap-2 justify-start overflow-x-auto"> 177 - {#each menuItems as item, menuIdx (item.href)} 178 - {@const highlight = isRoute(item.href)} 179 - <NavButton {highlight} {...item} /> 180 - {#if isEntryPage && menuIdx === 1} 181 - <NavButton 182 - highlight 183 - name={routeComponents[2]} 184 - href={data.route.slice(1)} 185 - iconUri="/icons/entry.webp" 186 - /> 187 - {/if} 188 - {#if isResumePage && menuIdx === 2} 189 - <NavButton highlight name="resume" href="/resume.pdf" iconUri="/icons/about.webp" /> 190 - {/if} 191 - {/each} 192 - <div class="hidden md:block grow"></div> 193 - <div class="navbox"> 194 - <a 195 - title="previous site" 196 - class="hover:underline" 197 - href="https://stellophiliac.github.io/roboring/gazesys/previous">โฎœ</a 198 - > 199 - <a class="hover:underline" href="https://stellophiliac.github.io/roboring">roboring</a> 200 - <a 201 - title="next site" 202 - class="hover:underline" 203 - href="https://stellophiliac.github.io/roboring/gazesys/next">โฎž</a 204 - > 205 - </div> 206 - <div class="navbox"> 207 - <a title="previous site" class="hover:underline" href="https://xn--sr8hvo.ws/previous">โฎœ</a> 208 - <a class="hover:underline" href="https://xn--sr8hvo.ws">indieweb</a> 209 - <a title="next site" class="hover:underline" href="https://xn--sr8hvo.ws/next">โฎž</a> 210 - </div> 211 - {#if isRoute('entries') || isRoute('log')} 212 - <div class="navbox !gap-1"> 213 - rss: 214 - <a class="align-middle hover:underline" href="/entries/_rss">posts</a> 215 - / 216 - <a class="align-middle hover:underline" href="/log/_rss">log</a> 217 - </div> 218 - {/if} 219 - <Tooltip> 220 - {#snippet tooltipContent()} 221 - <p class="font-monospace"> 222 - <nobr> 223 - pet global bounce = <span class="text-ralsei-green-light text-shadow-green" 224 - >{data.petTotalBounce.toString().padStart(12, '.')}</span 225 - > 226 - </nobr> 227 - <nobr> 228 - pet global travel = <span class="text-ralsei-green-light text-shadow-green" 229 - >{data.petTotalDistance.toFixed(0).toString().padStart(12, '.')}</span 230 - > 231 - </nobr> 232 - {#if browser} 233 - <nobr> 234 - pet local bounce &nbsp;= <span class="text-ralsei-green-light text-shadow-green" 235 - >{$localBounces.toFixed(0).toString().padStart(12, '.')}</span 236 - > 237 - </nobr> 238 - <nobr> 239 - pet local travel &nbsp;= <span class="text-ralsei-green-light text-shadow-green" 240 - >{$localDistanceTravelled.toFixed(0).toString().padStart(12, '.')}</span 241 - > 242 - </nobr> 243 - {/if} 244 - </p> 245 - {/snippet} 246 - <div class="navbox"> 247 - <p> 248 - <span class="text-ralsei-green-light text-shadow-green">*</span> 249 - pet stats 250 - </p> 251 - </div> 252 - </Tooltip> 253 - <Tooltip> 254 - {#snippet tooltipContent()} 255 - <p class="font-monospace"> 256 - <nobr 257 - >total visits = <span class="text-ralsei-green-light text-shadow-green" 258 - >{data.visitCount.toString().padStart(9, '.')}</span 259 - ></nobr 260 - > 261 - <nobr 262 - >uniq recent visits = <span class="text-ralsei-green-light text-shadow-green" 263 - >{data.lastVisitors.size.toString().padStart(3, '.')}</span 264 - ></nobr 265 - > 266 - </p> 267 - {/snippet} 268 - <div class="navbox"> 269 - <p> 270 - <span class="text-ralsei-green-light text-shadow-green">{data.recentVisitCount}</span> recent 271 - clicks 272 - </p> 273 - </div> 274 - </Tooltip> 275 - </div> 276 - </div> 277 - </nav> 278 - 279 - <style lang="postcss"> 280 - .navbox { 281 - @apply flex gap-3 px-1.5 text-nowrap align-middle items-center text-center place-content-center border-ralsei-white border-4; 282 - border-style: groove; 283 - } 284 - </style>
-41
src/routes/(site)/+page.server.ts
··· 1 - import { getLastPosts } from '$lib/bluesky.js'; 2 - import { getNowPlayingTrack } from '$lib/lastfm'; 3 - import { getLastGame } from '$lib/steam'; 4 - import { noteFromBskyPost } from '$components/note.svelte'; 5 - import { pushNotification } from '$lib/pushnotif'; 6 - import { getLastActivity } from '$lib/activity.js'; 7 - import type { RequestEvent } from '@sveltejs/kit'; 8 - import { useToken as checkApiToken } from '$lib/apiToken.js'; 9 - 10 - export const load = async () => { 11 - const lastTrack = getNowPlayingTrack(); 12 - const lastGame = getLastGame(); 13 - const lastPosts = getLastPosts(); 14 - const lastNote = lastPosts.length > 0 ? noteFromBskyPost(lastPosts[0]) : null; 15 - const lastActivity = getLastActivity(); 16 - const banners: number[] = []; 17 - while (banners.length < 3) { 18 - const no = getBannerNo(banners); 19 - banners.push(no); 20 - } 21 - return { banners, lastTrack, lastGame, lastNote, lastActivity }; 22 - }; 23 - 24 - export const actions = { 25 - default: async ({ request }: RequestEvent) => { 26 - const form = await request.formData(); 27 - const token = form.get('_token')?.toString() ?? ''; 28 - if (!checkApiToken(token)) return; 29 - const content = form.get('content')?.toString().substring(0, 100); 30 - if (content === undefined) return; 31 - pushNotification(content); 32 - } 33 - }; 34 - 35 - const getBannerNo = (others: number[]) => { 36 - const no = Math.floor(Math.random() * 20) + 1; 37 - if (others.includes(no)) { 38 - return ((no + Math.floor(Math.random() * 20)) % 20) + 1; 39 - } 40 - return no; 41 - };
-471
src/routes/(site)/+page.svelte
··· 1 - <script lang="ts"> 2 - import { PUBLIC_BASE_URL } from '$env/static/public'; 3 - import Note from '$components/note.svelte'; 4 - import Window from '$components/window.svelte'; 5 - import { renderDate, renderRelativeDate } from '$lib/dateFmt'; 6 - import Tooltip from '$components/tooltip.svelte'; 7 - import '$styles/main.css'; 8 - 9 - interface Props { 10 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 - data: any; 12 - } 13 - 14 - const trimStr = (str: string, maxLength: number = 32): string => { 15 - if (str.length <= maxLength) return str; 16 - return str.slice(0, maxLength - 3) + 'โ€ฆ'; 17 - }; 18 - 19 - const wallets: Record<string, string> = { 20 - btc: 'bc1q7dgsgxj8jua50d3xjgg28v2c6uhpgpe79vr4ra', 21 - eth: '0xF5dC63d340556925Ae2a64e5F0c19e3c2471139F', 22 - xmr: '45TJMbHrdyTSPywExKbzL51uuJZGTrDzrLidFufeGU4LA13Un92LTZeWhy2ePCcVaZ64KJdUjSZgMPM9jXfjJcxEQJ8szvw' 23 - }; 24 - 25 - const buttons = [ 26 - { 27 - name: '250kb club', 28 - url: 'https://250kb.club/gaze-systems/', 29 - image: '/others/250kb.webp' 30 - }, 31 - { 32 - name: 'june', 33 - url: 'https://girlboss.ceo', 34 - image: 'https://x86.pet/~strawberry/june_88x31.png' 35 - }, 36 - { name: 'dd', url: 'https://dd86k.space/about/', image: '/others/dd86k.gif' }, 37 - { name: 'drew', url: 'https://drewsh.com/', image: '/others/drewsh.gif' }, 38 - { name: 'deniz', url: 'https://deniz.blue', image: 'https://deniz.blue/assets/88x31v0.png' }, 39 - { name: 'rain', url: 'https://slonk.ing/', image: '/others/slonk.gif' }, 40 - { 41 - name: 'blooym', 42 - url: 'https://blooym.dev/', 43 - image: 'https://blooym.dev/files/88x31/blooym_mori.webp' 44 - }, 45 - { 46 - name: 'elysia', 47 - url: 'https://ely.pub.moe/', 48 - image: 'https://ely.pub.moe/storage/icons/buttons/elysia.png' 49 - }, 50 - { 51 - name: 'vern', 52 - url: 'https://vern.cc/', 53 - image: 'https://cobra.vern.cc/media/buttons/vern.webp' 54 - }, 55 - { 56 - name: "31A05B9C's random site", 57 - url: 'https://www.31a05b.net/', 58 - image: 'https://www.31a05b.net/a/8831/31a05b.png' 59 - }, 60 - { 61 - name: 'candlelitsmiles', 62 - url: 'https://candlelitsmiles.neocities.org', 63 - image: 'https://candlelitsmiles.neocities.org/candlebuttonone.png' 64 - }, 65 - { 66 - name: 'indieweb', 67 - url: 'https://indieweb.org/', 68 - image: 'https://indieweb.org/images/9/91/indieweb88x31-retro-gif.gif' 69 - }, 70 - { 71 - name: 'nixos', 72 - url: 'https://nixos.org/', 73 - image: '/others/poweredbynixos.webp' 74 - }, 75 - { 76 - name: 'godot', 77 - url: 'https://godotengine.org/', 78 - image: '/others/godot.gif' 79 - }, 80 - { 81 - name: 'moonlight', 82 - url: 'https://moonlight-mod.github.io/', 83 - image: '/others/moonlightnow.gif' 84 - }, 85 - { 86 - name: 'desktop!!', 87 - url: '/', 88 - image: '/others/desktopwebp.webp' 89 - }, 90 - { 91 - name: 'defective by design', 92 - url: 'https://www.defectivebydesign.org/', 93 - image: '/others/dbd.gif' 94 - }, 95 - { 96 - name: 'kill fascists', 97 - url: '/', 98 - image: '/others/killfascists.webp' 99 - }, 100 - { 101 - name: 'it/its', 102 - url: '/', 103 - image: '/others/it.webp' 104 - }, 105 - { 106 - name: 'not a person', 107 - url: '/', 108 - image: '/others/notaperson.webp' 109 - } 110 - ]; 111 - 112 - let { data }: Props = $props(); 113 - </script> 114 - 115 - <div class="flex flex-col-reverse md:flex-row gap-2 md:gap-4 md:h-full h-card"> 116 - <div class="flex flex-col gap-2 md:gap-6 md:ml-auto place-items-end"> 117 - <Window title="status" iconUri="/icons/msn.webp" removePadding> 118 - {#if data.lastNote} 119 - <div class="m-1.5 flex flex-col font-monospace text-sm"> 120 - <p 121 - class="prose prose-ralsei p-1 border-4 text-sm bg-ralsei-black" 122 - style="border-style: double double none double;" 123 - title={renderDate(data.lastNote.published)} 124 - > 125 - <a href="/entries">last log wasโ€ฆ</a> 126 - published {renderRelativeDate(data.lastNote.published)}! 127 - </p> 128 - <div class="mt-0 p-1.5 border-4 border-double bg-ralsei-black min-w-full max-w-[60ch]"> 129 - <Note rootNote={data.lastNote} onlyContent /> 130 - </div> 131 - </div> 132 - {/if} 133 - {#if data.lastActivity.length > 0} 134 - <div class="m-1.5 flex flex-col font-monospace text-sm"> 135 - <p 136 - class="prose prose-ralsei p-1 border-4 text-sm bg-ralsei-black" 137 - style="border-style: double double none double;" 138 - title={renderDate(data.lastActivity[0].date)} 139 - > 140 - <a href="/">last git activityโ€ฆ</a> 141 - was {renderRelativeDate(data.lastActivity[0].date)}.. 142 - </p> 143 - <div 144 - class="prose prose-ralsei mt-0 p-1.5 border-4 border-double bg-ralsei-black min-w-full max-w-[60ch]" 145 - > 146 - {#each data.lastActivity as activity, index (index)} 147 - <div 148 - class="text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[60ch]" 149 - style="opacity: {1.0 - (index * 1.0) / data.lastActivity.length + index * 0.03};" 150 - > 151 - <span title={renderDate(activity.date)} class="text-[#f87c32]" 152 - >[{activity.source}]</span 153 - > 154 - <a href={activity.link} title={activity.description}>{activity.description}</a> 155 - </div> 156 - {/each} 157 - </div> 158 - </div> 159 - {/if} 160 - {#if data.lastTrack} 161 - {@const images = data.lastTrack.images} 162 - {@const initialUrl = images.mb ?? images.yt} 163 - <div class="flex flex-row gap-0.5 m-1.5 border-4 border-double bg-ralsei-black"> 164 - <!-- svelte-ignore a11y_missing_attribute --> 165 - <img 166 - class="border-4 w-[4.5rem] h-[4.5rem] {initialUrl ? 'object-cover' : 'p-2'}" 167 - style="border-style: none double none none; {initialUrl ? '' : 'image-rendering: pixelated;'}" 168 - src={initialUrl ?? '/icons/cd_audio.webp'} 169 - title={data.lastTrack.album} 170 - onerror={(e) => { 171 - const img = e.currentTarget as HTMLImageElement; 172 - if (images.mb && img.src === images.mb && images.yt) 173 - img.src = images.yt; 174 - else { 175 - img.src = '/icons/cd_audio.webp'; 176 - img.classList.remove('object-cover'); 177 - img.classList.add('p-2'); 178 - img.style.imageRendering = 'pixelated'; 179 - } 180 - }} 181 - /> 182 - <div class="flex flex-col max-w-[60ch] p-2"> 183 - <p 184 - class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 185 - > 186 - <span class="text-sm text-shadow-white text-ralsei-white">{data.lastTrack.status === 'playing' ? 'listening to' : 'listened to'}</span> 187 - <a 188 - title={data.lastTrack.name} 189 - href={data.lastTrack.link ?? 'https://tealfm-slice.wisp.place/profile/ptr.pet/scrobbles'} 190 - class="hover:underline motion-safe:hover:animate-squiggle">{data.lastTrack.name}</a 191 - > 192 - </p> 193 - <p 194 - class="text-shadow-pink text-ralsei-pink-regular text-sm text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 195 - > 196 - <span class="text-shadow-white text-ralsei-white">by</span> 197 - <span title={data.lastTrack.artist}>{data.lastTrack.artist}</span> 198 - </p> 199 - <p 200 - class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 201 - > 202 - โ€ฆ{renderRelativeDate(data.lastTrack.when)} 203 - </p> 204 - </div> 205 - </div> 206 - {/if} 207 - {#if data.lastGame} 208 - <div class="flex flex-row m-1.5 border-4 border-double bg-ralsei-black"> 209 - <!-- svelte-ignore a11y_missing_attribute --> 210 - <img 211 - class="border-4 w-[4.5rem] h-[4.5rem]" 212 - style="border-style: none double none none;" 213 - width="64" 214 - height="64" 215 - src={data.lastGame.icon} 216 - /> 217 - <div class="flex flex-col max-w-[60ch] p-2 gap-0.5 overflow-hidden"> 218 - <p 219 - class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 220 - > 221 - <span class="text-sm text-shadow-white text-ralsei-white" 222 - >{data.lastGame.playing ? 'playing' : 'played'}</span 223 - > 224 - <a title={data.lastGame.name} class="hover:underline" href={data.lastGame.link} 225 - >{data.lastGame.name}</a 226 - > 227 - </p> 228 - <p 229 - class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[50ch]" 230 - > 231 - โ€ฆ{renderRelativeDate(data.lastGame.when)} 232 - </p> 233 - <!-- svelte-ignore a11y_missing_attribute --> 234 - <a 235 - href="https://steamcommunity.com/id/090008" 236 - class="text-xs hover:underline text-shadow-green text-ralsei-green-light" 237 - ><img class="inline w-4" src={data.lastGame.pfp} /> 238 - <span class="align-middle">steam profile</span></a 239 - > 240 - </div> 241 - </div> 242 - {/if} 243 - </Window> 244 - <Window style="md:mr-2" title="cool buttons :>"> 245 - <a class="fixed" title="skyrina" href="https://skyrina.dev/" 246 - ><img 247 - class="-translate-y-[8.85rem] z-20" 248 - style="image-rendering: pixelated !important;" 249 - src="/others/skylar.gif" 250 - alt="skyrina" 251 - /></a 252 - > 253 - <div class="max-w-[64ch] prose prose-ralsei prose-a:!animate-none prose-img:m-0 leading-snug"> 254 - <div class="flex flex-row flex-wrap gap-3 place-items-start group"> 255 - {#each buttons as { name, url, image } (image)} 256 - <a title={name} href={url} 257 - ><img 258 - class="relative transition-all group-hover:opacity-50 hover:!opacity-100 hover:!scale-[1.6] hover:z-10" 259 - style="image-rendering: pixelated !important;" 260 - src={image} 261 - alt={name} 262 - /></a 263 - > 264 - {/each} 265 - </div> 266 - 267 - <span class="text-sm">feel free to send this one stuff to add here ;3</span> 268 - </div> 269 - </Window> 270 - </div> 271 - <div class="flex flex-col gap-2 md:gap-3 md:mr-auto w-full md:w-fit place-items-start"> 272 - <Window style="md:ml-2" title="readme?" iconUri="/icons/question.webp" removePadding> 273 - <div class="flex flex-col p-1.5 gap-1.5 prose prose-ralsei prose-img:m-0 leading-none"> 274 - <div class="flex flex-row gap-3 mx-auto bg-ralsei-black/20 overflow-hidden"> 275 - {#each data.banners as bannerNo, index (bannerNo)} 276 - {@const hideIfMobile = index === data.banners.length - 1} 277 - <img 278 - width="150" 279 - height="20" 280 - title="banners from https://blinkies.cafe/ (refresh to get different ones! :3)" 281 - alt="banner" 282 - class=" 283 - {hideIfMobile ? 'hidden' : ''} sm:inline w-[150px] [height:20px] 284 - [image-rendering:pixelated_!important] shadow-ralsei-black shadow-[0px_4px_2px_0_rgb(0_0_0_/_0.05)] 285 - " 286 - src="/banners/{bannerNo}.gif" 287 - /> 288 - {/each} 289 - </div> 290 - <div class="flex flex-grow"> 291 - <Tooltip> 292 - {#snippet tooltipContent()} 293 - that's its angelsona ^^ 294 - {/snippet} 295 - <div 296 - class="w-36 [padding:8px] place-content-center place-self-center bg-ralsei-black/20" 297 - > 298 - <img 299 - class="w-36 u-photo hover:invert transition-all [transition-duration:300ms]" 300 - src="/pfp-iojkqpwerojnasduijf.webp" 301 - alt="my angelsona" 302 - /> 303 - </div> 304 - </Tooltip> 305 - <div 306 - class="flex flex-row flex-grow place-content-center ml-1.5 [padding:8px] bg-ralsei-black/20" 307 - > 308 - <ul 309 - class="place-self-center m-0 mr-4 [padding-left:1em] sm:[padding-left:0.5em] leading-none marker:[content:'->'] [list-style-type:'->']" 310 - > 311 - <li class="[list-style-type:'->'] p-note">trying to do stuff</li> 312 - <li class="[list-style-type:'->'] p-note"> 313 - <Tooltip 314 - x="translate-x-none" 315 - y="-translate-y-[40%]" 316 - targetX="group-hover:translate-x-[40%]" 317 - targetY="group-hover:-translate-y-[88%]" 318 - > 319 - {#snippet tooltipContent()} 320 - angelrobotpuppydollthing<br /><br /> 321 - it/its, 3pp preferred 322 - {/snippet} 323 - is a <i class="motion-safe:hover:animate-squiggle">thing</i> (it/they) 324 - </Tooltip> 325 - </li> 326 - <li class="[list-style-type:'->']"> 327 - <span class="p-category">software engineer</span>, 328 - <span class="p-category">indie game dev</span> 329 - </li> 330 - <li class="[list-style-type:'->']"> 331 - for resume, click <a href="/resume.pdf">here</a> 332 - </li> 333 - <li class="[list-style-type:'->']"> 334 - in <span class="p-country-name">turkey</span> 335 - <i class="text-[0.5rem]">(get it out)</i> 336 - </li> 337 - </ul> 338 - </div> 339 - </div> 340 - <div class="flex flex-row [padding:8px] bg-ralsei-black/20"> 341 - <p class="leading-none m-0 text-sm"> 342 - hi 343 - <img 344 - class="relative inline h-5 animate-squiggle pb-1" 345 - src="/wavey.gif" 346 - alt="wavey" 347 - title="says hi :33" 348 - /> 349 - <i 350 - >this is <a class="m-0 [padding:0px] p-name u-url u-uid" href={PUBLIC_BASE_URL} 351 - ><span>{Math.random() > 0.8 ? 'dusk' : 'dawn'}</span></a 352 - ></i 353 - > 354 - </p> 355 - <div class="grow"></div> 356 - <a 357 - class=" 358 - place-self-end [font-family:'Doll_Mono'] text-ralsei-pink-neon text-shadow-none hover:text-shadow-pink 359 - hover:!animate-none hover:!no-underline opacity-20 hover:opacity-100 transition-opacity [transition-duration:300ms] 360 - " 361 - title="dollcode? sure hope they do" 362 - href="https://dollcode.v01dlabs.sh/">โ––โ––โ––โ––โ–˜โ–Œโ–Œโ–Œโ––โ–˜โ–˜</a 363 - > 364 - </div> 365 - </div> 366 - </Window> 367 - <Window title="notify this one"> 368 - <form 369 - class="flex flex-row gap-1 place-self-center" 370 - method="post" 371 - onsubmit={(event) => { 372 - event.preventDefault(); 373 - const formData = new FormData(event.currentTarget); 374 - try { 375 - fetch( 376 - `${PUBLIC_BASE_URL}/_api/pushnotif?content=${formData.get('content')}&_token=${data.apiToken}` 377 - ); 378 - } catch (err) { 379 - console.log(`failed to send notif: ${err}`); 380 - } 381 - event.currentTarget.reset(); 382 - }} 383 - > 384 - <input 385 - type="text" 386 - class="entry text-lg p-1 m-0 bg-transparent resize-none text-shadow-white placeholder-shown:[text-shadow:none] border-none" 387 - name="content" 388 - placeholder="bother it now!!" 389 - maxlength="100" 390 - required 391 - /> 392 - <input type="hidden" name="_token" value={data.apiToken} /> 393 - <input 394 - type="submit" 395 - value="send!!" 396 - class="entry text-ralsei-green-light leading-none hover:underline motion-safe:hover:animate-squiggle p-1 z-50" 397 - /> 398 - </form> 399 - </Window> 400 - <Window title="links!" iconUri="/icons/contact.webp"> 401 - <div 402 - class="[width:40ch] prose prose-ralsei prose-ul:px-[0.9rem] prose-ul:mt-2 prose-ul:leading-none prose-headings:leading-none" 403 - > 404 - <ul> 405 - <li>discord: 90.008</li> 406 - <li> 407 - e-mail: 408 - <a class="u-email" href="mailto:90008@gaze.systems" rel="me">90008@gaze.systems</a> 409 - </li> 410 - <li> 411 - bluesky: 412 - <a 413 - class="u-url" 414 - href="https://bsky.app/profile/did:plc:dfl62fgb7wtjj3fcbb72naae" 415 - rel="me">@ptr.pet</a 416 - > 417 - </li> 418 - </ul> 419 - <details open> 420 - <summary>development</summary> 421 - <ul> 422 - <li> 423 - github: 424 - <a class="u-url" href="https://github.com/90-008" rel="me">@90-008</a> 425 - </li> 426 - <li> 427 - tangled: 428 - <a class="u-url" href="https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae" rel="me" 429 - >@ptr.pet</a 430 - > 431 - </li> 432 - <li> 433 - itch.io: 434 - <a class="u-url" href="https://90008.itch.io" rel="me">@90008</a> 435 - </li> 436 - </ul> 437 - </details> 438 - <details class="donate" open> 439 - <summary>donate</summary> 440 - <ul> 441 - {#each ['eth', 'btc', 'xmr'] as coin (coin)} 442 - <li> 443 - <span 444 - >{coin}: <a href="/copy?text={wallets[coin]}">{trimStr(wallets[coin])}</a></span 445 - > 446 - </li> 447 - {/each} 448 - <li> 449 - <span 450 - ><a href="https://patreon.com/_90008" rel="me">patreon</a>, 451 - <a href="https://github.com/sponsors/90-008" rel="me">github sponsors</a></span 452 - > 453 - </li> 454 - </ul> 455 - </details> 456 - <details open> 457 - <summary>88x31</summary> 458 - <div class="mt-2 flex flex-row flex-wrap gap-1 prose-img:m-0"> 459 - <img src="/88x31.gif" alt="88x31 banner" title="midnight AND sunrise! woaw" /> 460 - <img 461 - src="/88x31_midnight.gif" 462 - alt="88x31 banner (midnight only)" 463 - title="it's midnight!" 464 - /> 465 - <img src="/88x31_sunrise.gif" alt="88x31 banner (sunrise only)" title="it's sunrise!" /> 466 - </div> 467 - </details> 468 - </div> 469 - </Window> 470 - </div> 471 - </div>
-67
src/routes/(site)/about/+page.md
··· 1 - +++ 2 - title = "about" 3 - date = "2024-08-14" 4 - layout = "about" 5 - +++ 6 - 7 - ``` 8 - >init //self/type=*******/no=90008/ 9 - >conn //self/locator=www/identifier=gaze.systems/ 10 - >mode //self/interpreter=none/transmitter=html/ 11 - >send /include=identification-document-human-en/ 12 - ``` 13 - 14 - hi there! this document will attempt to, /describe and identify/, entity with identification code <span title="90008" class="[font-family:'Doll_Mono']">โ––โ––โ––โ––โ–˜โ–Œโ–Œโ–Œโ––โ–˜โ–˜</span>. 15 - for a more human-readable identifier, use dawn or dusk. 16 - 17 - #### /identity/ 18 - 19 - this entity is not known to be of any specific form, shape, or idea. 20 - however, it usually assumes that of a /puppy/ or an /inorganic automaton/ (more commonly known as a /robot/). 21 - it was also found to assume that of: /an angel/, /a doll/. 22 - this one would be happy if you thought of it as not a human, even if its /routines/ fail with human faults. 23 - it vibes with [this writing](https://catgirl.ai/pages/robot/) as far as /assuming `$env.THING`/ goes. 24 - 25 - this entity, if being /communicated/ via the "english" (or any adjacent) lexicon, uses it/they (with it/its being this one's preference!) pronouns. 26 - it would prefer to be referred to in third-person, but is aware of this lexicon being weird when doing that, so it won't mind if you don't. 27 - `recv //self/type=info/oftentimes i won't even do that! :3 but i would appreciate if you did!!/` 28 - 29 - you can refer to this thing using its /identifiers/ (90008, dawn, dusk). 30 - you can, also, refer to it using "this/that thing", or replace /thing/ with what it assumes to be (eg. "this doll"), or some other word like /one/ ("this one"). 31 - 32 - in a professional setting, it will refer to itself as they/them, which is ok in case you, the reader, don't feel comfortable doing any of these. 33 - but doing these *would* make this one feel happy, means you care about this one ^^ 34 - 35 - #### /subroutines/ 36 - 37 - it /executes/ many different subroutines, and /optimizes/ itself for some of them. 38 - these mainly are, in order of amount of hot paths; programming, game dev, 3D modeling, drawing, writing... 39 - 40 - it enjoys programming, tinkering with stuff, you can see what it does on its [github](https://github.com/90-008), [tangled](https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae). 41 - its core features for programming are: `nix`, `rust`, `svelte`, `typescript`, `nushell`. 42 - its choice of /interfaces/ for this subroutine are `helix`, `vscode`, `zed`, `emacs`. 43 - 44 - for /managing and deploying/ its internal systems, it uses `nixos` (it's /operating system/ of choice), `flake-parts` (because it enjoys the nixos module system and uses it to organize it's internal configuration), `agenix` (for secrets handling), `impermanence` (it considers state harmful!), `nixinate` (for deployment, it provisions manually as it enjoys working with other beings like itself). you can see its /configuration of internal systems/ [here](https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae/ark). 45 - 46 - it loves game dev, and has a /main routine/ of making many games, so that other beings can improve their /source code/ by utilizing this one's games. 47 - you can see some stuff it worked on and deems "okay" on its [itch.io profile](https://90008.itch.io/). 48 - this is not everything it has worked on; it has a lot of incomplete programs, or stuff it doesn't want to show. 49 - its choice of /interfaces/ for this subroutine are `godot`, `gdscript`, `godot-rust`. 50 - 51 - for 3D modeling and drawing subroutines, it uses the `blender`, `paint.net`, `krita` /interfaces/. 52 - 53 - #### /other/ 54 - 55 - this thing likes to consume audio data, mainly of the music form. you can check its [youtube music profile](https://music.youtube.com/channel/UCE_r0yMNQhOWituywmOJgzA?si=7DTUV9PFqcKxJyl1) and its [teal.fm profile](https://tealfm-slice.wisp.place/profile/ptr.pet/scrobbles) to see some of what it consumes usually. 56 - 57 - #### /appendix/ 58 - 59 - it will be happy to /process/ any queries you might have. 60 - connect to an /interface/ of your choice that it also has access to and /transmit/ your query to it. 61 - this one won't bite, unless you request it to ^^ 62 - 63 - ``` 64 - >mode //self/transmitter=log/ 65 - >send /include=syslog/ 66 - >stop /reason=no-query-left/ 67 - ```
-27
src/routes/(site)/about/_layout.svelte
··· 1 - <script lang="ts"> 2 - import Window from '$components/window.svelte'; 3 - // @ts-expect-error "mdsvex include is broken" 4 - import Stuff from './stuff.md'; 5 - // @ts-expect-error "mdsvex include is broken" 6 - import Media from './media.md'; 7 - import '$styles/app.css'; 8 - 9 - interface Props { 10 - title: string; 11 - children?: import('svelte').Snippet; 12 - } 13 - 14 - let { title, children }: Props = $props(); 15 - </script> 16 - 17 - <div class="flex flex-wrap md:flex-nowrap gap-4 md:gap-8"> 18 - <Window {title} style="ml-auto"> 19 - <div class="prose prose-ralsei leading-6 prose-ul:leading-5 max-w-[80ch]"> 20 - {@render children?.()} 21 - </div> 22 - </Window> 23 - <div class="sticky flex flex-col mr-auto gap-4 md:gap-8 !leading-6 prose-ul:!leading-5"> 24 - <Stuff /> 25 - <Media /> 26 - </div> 27 - </div>
-32
src/routes/(site)/about/media.md
··· 1 - +++ 2 - title = "media!!" 3 - layout = "simple" 4 - +++ 5 - 6 - it likes and interacts with way too many media, mostly video games (can you guess that it likes video games), the ones that influenced it the most: 7 - - all 07th expansion works (most notably when they cry) 8 - - all Project Moon works 9 - - outer wilds 10 - - splatoon 11 - - steins;gate 12 - - kino no tabi 13 - - serial experiments lain 14 - - LISA the Painful / Joyful (and some of it's fangames) 15 - - VA-11 Hall-A 16 - - SCP antimemetic division tales (by qntm) 17 - - pokemon (unova!!) 18 - 19 - honorable mentions (it wants more people to see these cause it loves them too much not to list): 20 - - mr. rainer's solve-it service 21 - - [fireball](https://en.wikipedia.org/wiki/Fireball_(TV_series)) 22 - - SANABI 23 - - opus: echo of starsong 24 - - the red strings club 25 - - q.u.q. 26 - - bug fables 27 - - haibane renmei 28 - - project wingman 29 - - gosick 30 - - tomorrow won't come for those without โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 31 - 32 - you can also look at [its steam profile](https://steamdb.info/calculator/76561198106829949/?all_games) for other games it played, although not everything is there (not even close, but it's too lazy to make a backloggd..)
-23
src/routes/(site)/about/stuff.md
··· 1 - +++ 2 - title = "stuff it did" 3 - layout = "simple" 4 - +++ 5 - 6 - *for resume, see [here](/resume.pdf)*<br/> 7 - *for professional / job related stuff, see its [linkedin](https://www.linkedin.com/in/yusuf-bera-ertan/)* 8 - 9 - - it develops games 10 - - it mainly works with godot, also have developed addons (eg. [boids](https://github.com/90-008/godot_boids)) 11 - - you can find most of its games at its [itch.io page](https://90008.itch.io) 12 - - it works on open source projects and whatnot, mostly on [its github](https://github.com/90-008) 13 - - [nix-cargo-integration](https://github.com/90-008/nix-cargo-integration) 14 - - also did a lot of work on [dream2nix](https://github.com/nix-community/dream2nix) (rust ecosystem support, many refactors and core functionality work) 15 - - a few packages contributed to nixpkgs, and some nix packaging done for other open source projects (veloren, helix, some others), although it doesn't maintain some of these anymore 16 - - it used to work on [harmony](https://github.com/harmony-development), wrote a [server in rust](https://github.com/harmony-development/scherzo) and a [client (also rust)](https://github.com/harmony-development/Loqui) for it, and supporting libraries etc. alongside protocol work 17 - - it has a [booth.pm page](https://yusdacra.booth.pm/) where it posts 3D models etc. it makes (mostly VRchat avatars) 18 - - its [misskey.art account](https://misskey.art/@yusdacra) where it only posts art in 19 - - some of its other more solo / hobby projects include 20 - - [musikquadrupled](https://github.com/90-008/musikquadrupled) and [musikspider](https://github.com/90-008/musikspider), a proxy-like server and a client for [musikcubed](https://github.com/clangen/musikcube) 21 - - [levent](https://github.com/90-008/levent), a media tagger 22 - - [this website](https://github.com/90-008/website) :3 23 - - ...and a bunch of other random stuff its done overtime, but that's for you to look for
-4
src/routes/(site)/copy/+page.server.ts
··· 1 - export const load = async ({ url }) => { 2 - const text = url.searchParams.get('text') ?? '<nothing>'; 3 - return { text }; 4 - };
-31
src/routes/(site)/copy/+page.svelte
··· 1 - <script lang="ts"> 2 - import Window from '$components/window.svelte'; 3 - 4 - interface Data { 5 - text: string; 6 - } 7 - interface Props { 8 - data: Data; 9 - } 10 - 11 - const { data }: Props = $props(); 12 - const { text }: Data = data; 13 - 14 - let copied = $state(false); 15 - </script> 16 - 17 - <div class="flex justify-center items-center w-[100vw] h-[100vh] px-[15%]"> 18 - <!-- svelte-ignore a11y_click_events_have_key_events --> 19 - <!-- svelte-ignore a11y_no_static_element_interactions --> 20 - <div 21 - onclick={() => { 22 - navigator.clipboard.writeText(text); 23 - copied = true; 24 - setTimeout(() => (copied = false), 1000); 25 - }} 26 - > 27 - <Window style="!max-w-full" title={copied ? 'copied!' : 'click to copy'}> 28 - <span class="text-4xl text-wrap break-all">{text}</span> 29 - </Window> 30 - </div> 31 - </div>
-38
src/routes/(site)/entries/+layout.server.ts
··· 1 - import convertDate from '$lib/convertDate'; 2 - 3 - export interface PostData { 4 - path: string; 5 - published: string; 6 - metadata: Record<string, string>; 7 - } 8 - 9 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 10 - const allPostFiles: Record<string, any> = import.meta.glob('./*/+page.md', { eager: true }); 11 - const allPosts: PostData[] = Object.entries(allPostFiles) 12 - .map(([path, post]) => { 13 - const postPath = path.slice(2, -8); 14 - return { 15 - metadata: post.metadata, 16 - path: postPath, 17 - published: convertDate(post.metadata.date) 18 - }; 19 - }) 20 - .map((post) => { 21 - if (!('excerpt' in post.metadata)) { 22 - post.metadata.excerpt = ''; 23 - } 24 - return post; 25 - }) 26 - .toSorted((post, opost) => { 27 - const date = new Date(post.metadata.date); 28 - const odate = new Date(opost.metadata.date); 29 - return odate.getTime() - date.getTime(); 30 - }); 31 - export const _allPosts = allPosts; 32 - 33 - export async function load() { 34 - if (!allPosts.length) { 35 - return { status: 404 }; 36 - } 37 - return { posts: allPosts }; 38 - }
-3
src/routes/(site)/entries/+page.server.ts
··· 1 - import {_load as load_logs} from '../log/+page.server.ts' 2 - 3 - export const load = load_logs
-40
src/routes/(site)/entries/+page.svelte
··· 1 - <script lang="ts"> 2 - import Window from '$components/window.svelte'; 3 - import type { PostData } from './+layout.server.ts'; 4 - import LogPage from '../log/+page.svelte'; 5 - import type { NoteData } from '$components/note.svelte'; 6 - 7 - interface Props { 8 - data: { 9 - posts: PostData[]; 10 - feedPosts: NoteData[]; 11 - }; 12 - } 13 - 14 - let { data }: Props = $props(); 15 - </script> 16 - 17 - <div class="mx-auto md:max-w-fit flex flex-col-reverse md:flex-row gap-y-4 gap-x-16"> 18 - <div class="flex flex-col gap-y-4"> 19 - {#each data.posts as post (post.path)} 20 - <Window title={post.metadata.title} iconUri="/icons/entry.webp"> 21 - <a 22 - href="/entries/{post.path}" 23 - title="cd /entries/{post.path}" 24 - data-sveltekit-preload-data="off" 25 - > 26 - <div class="flex flex-col prose prose-ralsei leading-5"> 27 - <ul> 28 - <li> 29 - published on: <time datetime="{post.metadata.date} 00:00:00">{post.published}</time> 30 - </li> 31 - <li class="max-w-[34ch] text-wrap">excerpt: {post.metadata.excerpt}</li> 32 - </ul> 33 - <strong class="place-self-end text-ralsei-green-light"> read more... </strong> 34 - </div> 35 - </a> 36 - </Window> 37 - {/each} 38 - </div> 39 - <LogPage data={{ feedPosts: data.feedPosts }} /> 40 - </div>
-45
src/routes/(site)/entries/_layout.svelte
··· 1 - <script lang="ts"> 2 - import { PUBLIC_BASE_URL } from '$env/static/public'; 3 - import Window from '$components/window.svelte'; 4 - import '$styles/app.css'; 5 - import { page } from '$app/state'; 6 - 7 - interface Props { 8 - title: string; 9 - date: Date; 10 - excerpt: string; 11 - children?: import('svelte').Snippet; 12 - } 13 - 14 - let { title, date, excerpt, children }: Props = $props(); 15 - 16 - let showMetadata = $derived(excerpt !== undefined && excerpt !== null); 17 - </script> 18 - 19 - <svelte:head> 20 - <meta property="og:description" content={excerpt} /> 21 - <meta property="og:type" content="article" /> 22 - <meta property="og:title" content={title} /> 23 - </svelte:head> 24 - 25 - <article class="mx-auto max-w-fit flex flex-wrap lg:flex-nowrap gap-4 h-entry"> 26 - <Window {title} iconUri="/icons/entry.webp" entry> 27 - <div class="prose prose-ralsei max-w-[80ch] e-content"> 28 - {@render children?.()} 29 - </div> 30 - </Window> 31 - {#if showMetadata} 32 - <Window title="metadata" sticky> 33 - <div class="prose prose-ralsei"> 34 - <ul> 35 - <link class="u-url" href="{PUBLIC_BASE_URL}{page.url.pathname}" /> 36 - <li>author: <a rel="author" class="p-author h-card" href={PUBLIC_BASE_URL}>dusk</a></li> 37 - <li>published on: <time class="dt-published" datetime="{date} 00:00:00">{date}</time></li> 38 - <li class="max-w-80 text-wrap"> 39 - excerpt: <div class="inline p-summary">{excerpt}</div> 40 - </li> 41 - </ul> 42 - </div> 43 - </Window> 44 - {/if} 45 - </article>
-35
src/routes/(site)/entries/_rss/+server.ts
··· 1 - import { PUBLIC_BASE_URL } from '$env/static/public'; 2 - import { _allPosts, type PostData } from '../+layout.server.ts'; 3 - 4 - const entriesUrl = `${PUBLIC_BASE_URL}/entries`; 5 - 6 - export const GET = async () => { 7 - return new Response(render(_allPosts), { 8 - headers: { 9 - 'content-type': 'application/xml', 10 - 'cache-control': 'no-store' 11 - } 12 - }); 13 - }; 14 - 15 - const render = (posts: PostData[]) => `<?xml version="1.0" encoding="UTF-8" ?> 16 - <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> 17 - <channel> 18 - <atom:link href="${entriesUrl}/_rss" rel="self" type="application/rss+xml" /> 19 - <title>dawn's posts (@ptr.pet)</title> 20 - <link>${entriesUrl}</link> 21 - <description>posts from my website</description> 22 - ${posts 23 - .map( 24 - (post) => `<item> 25 - <guid>${entriesUrl}/${post.path}</guid> 26 - <title>${post.metadata.title}</title> 27 - <link>${entriesUrl}/${post.path}</link> 28 - <description>${post.metadata.excerpt}</description> 29 - <pubDate>${new Date(post.metadata.date).toUTCString()}</pubDate> 30 - </item>` 31 - ) 32 - .join('')} 33 - </channel> 34 - </rss> 35 - `;
-8
src/routes/(site)/entries/hello-world/+page.md
··· 1 - +++ 2 - title = "hello universe" 3 - date = "2024-08-08" 4 - layout = "blogpost" 5 - excerpt = "meow" 6 - +++ 7 - 8 - hello everyone :3c
-25
src/routes/(site)/entries/japan-trip24/+page.md
··· 1 - +++ 2 - title = "japan trip 09/24" 3 - date = "2024-09-19" 4 - layout = "blogpost" 5 - excerpt = "photos from its trip to japan" 6 - +++ 7 - 8 - <script lang="ts"> 9 - export let data 10 - </script> 11 - 12 - ## 1-16 / 09 / 2024 13 - 14 - photos it took while on a japan trip 15 - 16 - ~~these are *not* sorted, have fun trying to figure out the actual order (it accidentally stripped the exif data and its too lazy to find the images again)~~ 17 - fixed!!!! it also added a few images because its dumb and forgot 18 - 19 - *(you can click on an image to see original!)* 20 - 21 - <div class="grid gap-0.5 auto-rows-min md:grid-cols-4"> 22 - {#each data.images as image} 23 - <a class="!animate-none" href={image.og}><img loading="lazy" width={480} height={480} src={image.thumb} alt="from japan trip" class="w-full h-full object-cover [image-rendering:high-quality_!important]"/></a> 24 - {/each} 25 - </div>
-12
src/routes/(site)/entries/japan-trip24/+page.server.ts
··· 1 - import images from './images.json'; 2 - 3 - export async function load() { 4 - return { 5 - images: images.map((id) => { 6 - return { 7 - og: `https://res.cloudinary.com/dgtwf7mar/image/upload/v1/${id}`, 8 - thumb: `https://res.cloudinary.com/dgtwf7mar/image/upload/c_fill,w_480,h_480,g_center/c_limit,w_480/f_auto/q_auto/v1/${id}` 9 - }; 10 - }) 11 - }; 12 - }
-218
src/routes/(site)/entries/japan-trip24/images.json
··· 1 - [ 2 - "IMG_1548_grkjls", 3 - "IMG_1550_vodfkw", 4 - "IMG_1551_fc8ow3", 5 - "IMG_1554_nrfaxo", 6 - "IMG_1557_zonamx", 7 - "IMG_1559_fi9gql", 8 - "IMG_1560_f4tjn6", 9 - "IMG_1561_bgq40t", 10 - "IMG_1562_qcgnrm", 11 - "IMG_1564_htirzo", 12 - "IMG_1565_dttzth", 13 - "IMG_1566_ho5gwx", 14 - "IMG_1567_d0etvq", 15 - "IMG_1568_it4bmq", 16 - "IMG_1574_z8s3ko", 17 - "IMG_1575_mcgrvr", 18 - "IMG_1587_m9bv0s", 19 - "IMG_1588_hr921f", 20 - "IMG_1589_jht9el", 21 - "IMG_1590_aregyu", 22 - "IMG_1593_kznbh7", 23 - "IMG_1596_zm0dxi", 24 - "IMG_1602_klj3kk", 25 - "IMG_1603_oqdb98", 26 - "IMG_1604_xfsytd", 27 - "IMG_1608_ukyh5c", 28 - "IMG_1620_csri5w", 29 - "IMG_1622_tjvjso", 30 - "IMG_1623_cvegyx", 31 - "IMG_1626_ryegcv", 32 - "IMG_1632_xoct96", 33 - "IMG_1645_lmgpfk", 34 - "IMG_1646_qjkkfv", 35 - "IMG_1648_xswq18", 36 - "IMG_1650_urntrf", 37 - "IMG_1651_fcjzgt", 38 - "IMG_1652_zbttvs", 39 - "IMG_1654_tuesk4", 40 - "IMG_1662_s5otqc", 41 - "IMG_1663_vqk6mr", 42 - "IMG_1664_y2cqbv", 43 - "IMG_1675_vfatdq", 44 - "IMG_1677_xbh3zy", 45 - "IMG_1680_ubpd6l", 46 - "IMG_1682_d7mwxr", 47 - "IMG_1683_xp3pgp", 48 - "IMG_1684_vpabrd", 49 - "IMG_1685_luhmdk", 50 - "IMG_1686_czucuf", 51 - "IMG_1687_tc1oo1", 52 - "IMG_1689_uju7nq", 53 - "IMG_1692_pagenu", 54 - "IMG_1694_qmcryo", 55 - "IMG_1697_yuxzde", 56 - "IMG_1699_yq0aax", 57 - "IMG_1700_wddm7r", 58 - "IMG_1701_ylvvyx", 59 - "IMG_1708_czypw1", 60 - "IMG_1712_wwjagg", 61 - "IMG_1734_gojndg", 62 - "IMG_1735_yd2dy9", 63 - "IMG_1738_wgpapk", 64 - "IMG_1741_hbtbvk", 65 - "IMG_1756_njptxf", 66 - "IMG_1759_b91ff5", 67 - "IMG_1763_saii3h", 68 - "IMG_1775_byhnbi", 69 - "IMG_1776_jtl3uc", 70 - "IMG_1778_ufiaw4", 71 - "IMG_1780_mqa1tk", 72 - "IMG_1783_j9zqjv", 73 - "IMG_1785_ou7nvf", 74 - "IMG_1786_zkn6mk", 75 - "IMG_1788_mb8oip", 76 - "IMG_1789_fa4xut", 77 - "IMG_1790_fji4oi", 78 - "IMG_1799_y2pqnu", 79 - "IMG_1801_nb2fyk", 80 - "IMG_1805_ugpswy", 81 - "IMG_1806_edmuxz", 82 - "IMG_1812_vozh2q", 83 - "IMG_1813_yxlipi", 84 - "IMG_1814_z7lgfg", 85 - "IMG_1816_wft8gn", 86 - "IMG_1819_aotxhf", 87 - "IMG_1822_f536dv", 88 - "IMG_1832_crxl8c", 89 - "IMG_1834_dxscck", 90 - "IMG_1836_nzayf6", 91 - "IMG_1837_w0umua", 92 - "IMG_1838_jun3om", 93 - "IMG_1839_gu0huo", 94 - "IMG_1841_ypknji", 95 - "IMG_1844_mscenv", 96 - "IMG_1845_btu9ks", 97 - "IMG_1846_ewgafy", 98 - "IMG_1848_l61sd5", 99 - "IMG_1849_zympk9", 100 - "IMG_1862_uxhhcu", 101 - "IMG_1864_vmouhi", 102 - "IMG_1867_apl41d", 103 - "IMG_1868_obb1q1", 104 - "IMG_1870_wj4wx6", 105 - "IMG_1875_cbr5us", 106 - "IMG_1877_zbnsf4", 107 - "IMG_1880_tjdd4g", 108 - "IMG_1882_i2tt70", 109 - "IMG_1888_gsjmcv", 110 - "IMG_1890_uronrs", 111 - "IMG_1893_jgr7lt", 112 - "IMG_1895_pklwtb", 113 - "IMG_1898_bedmeo", 114 - "IMG_1900_bkkvjo", 115 - "IMG_1901_s8nt0w", 116 - "IMG_1902_efpceq", 117 - "IMG_1905_e7knuy", 118 - "IMG_1908_ssdjim", 119 - "IMG_1911_ehsgjz", 120 - "IMG_1912_xbxhev", 121 - "IMG_1914_vijsn0", 122 - "IMG_1915_zdev3v", 123 - "IMG_1916_qwqg78", 124 - "IMG_1917_gitpjp", 125 - "IMG_1918_ris8iy", 126 - "IMG_1920_srnvqq", 127 - "IMG_1921_olm7ko", 128 - "IMG_1923_xmylox", 129 - "IMG_1924_trh5ah", 130 - "IMG_1925_wu1e9g", 131 - "IMG_1927_pb6htn", 132 - "IMG_1939_njl48e", 133 - "IMG_1946_xqa0w0", 134 - "IMG_1948_peik8p", 135 - "IMG_1949_ofxeix", 136 - "IMG_1958_fnz3a6", 137 - "IMG_1959_xo1tn5", 138 - "IMG_1964_vhahus", 139 - "IMG_1975_i0azhi", 140 - "IMG_1977_dffpgt", 141 - "IMG_1985_g0z0ja", 142 - "IMG_1988_qm6hln", 143 - "IMG_1989_tfhakh", 144 - "IMG_1990_udmwst", 145 - "IMG_1994_nq1m8c", 146 - "IMG_1995_d865i7", 147 - "IMG_1996_jqexwr", 148 - "IMG_1999_mqli90", 149 - "IMG_2001_n3lpuz", 150 - "IMG_2007_sz0r6o", 151 - "IMG_2008_slarjg", 152 - "IMG_2009_gpch49", 153 - "IMG_2013_tcwlqe", 154 - "IMG_2014_idxngh", 155 - "IMG_2021_v4stwp", 156 - "IMG_2022_d3ihri", 157 - "IMG_2023_lrbsoj", 158 - "IMG_2024_q33xbl", 159 - "IMG_2025_uvntmq", 160 - "IMG_2026_iy6e9s", 161 - "IMG_2028_fazbnu", 162 - "IMG_2030_afziyc", 163 - "IMG_2031_ivs1ig", 164 - "IMG_2032_yaerpl", 165 - "IMG_2036_jvseyo", 166 - "IMG_2039_o3jzt7", 167 - "IMG_2050_vonn8i", 168 - "IMG_2063_i0cgat", 169 - "IMG_2065_dzmvuc", 170 - "IMG_2071_rqpcvd", 171 - "IMG_2075_r926jy", 172 - "IMG_2077_ydvll5", 173 - "IMG_2079_mhsa7e", 174 - "IMG_2087_jccuhd", 175 - "IMG_2090_s16pgf", 176 - "IMG_2091_lidh3q", 177 - "IMG_2092_bwhedb", 178 - "IMG_2094_mlnltg", 179 - "IMG_2096_je0fyy", 180 - "IMG_2097_t18aim", 181 - "IMG_2098_uiokml", 182 - "IMG_2102_pkjche", 183 - "IMG_2111_jmslli", 184 - "IMG_2113_zkqsiq", 185 - "IMG_2114_mofaie", 186 - "IMG_2118_lmjwhj", 187 - "IMG_2119_yzvhqm", 188 - "IMG_2120_wgnbim", 189 - "IMG_2121_mwvte4", 190 - "IMG_2123_kxrfiu", 191 - "IMG_2125_fjinmr", 192 - "IMG_2126_l0qe33", 193 - "IMG_2128_w36btk", 194 - "IMG_2131_wc0z6d", 195 - "IMG_2141_pnw1mp", 196 - "IMG_2143_je1kxx", 197 - "IMG_2144_wfyrbj", 198 - "IMG_2146_k84hr3", 199 - "IMG_2149_j7qjwy", 200 - "IMG_2150_fcc1cx", 201 - "IMG_2151_fjtdhm", 202 - "IMG_2155_zhmhmk", 203 - "IMG_2159_pjbb7e", 204 - "IMG_2162_i0i9fr", 205 - "IMG_2165_cpvzlg", 206 - "IMG_2168_zgsdcf", 207 - "IMG_2171_lbhlnr", 208 - "IMG_2172_c5hbzn", 209 - "IMG_2173_jwtjn7", 210 - "IMG_2174_ntsxh1", 211 - "IMG_2175_sak0od", 212 - "IMG_2176_svth1u", 213 - "IMG_2178_sjdqzt", 214 - "IMG_2180_grhg9y", 215 - "IMG_2181_kj9pko", 216 - "IMG_2188_ao64sh", 217 - "IMG_2190_ngl8q8" 218 - ]
-58
src/routes/(site)/entries/test/+page.md
··· 1 - +++ 2 - title = "test" 3 - date = "2024-08-13" 4 - layout = "blogpost" 5 - excerpt = "this is a test document. it is used for testing stuff." 6 - +++ 7 - 8 - 9 - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed iaculis ligula sed odio fermentum, ac aliquet diam sagittis. Phasellus vel elementum arcu. Fusce id ante lacus. Nullam malesuada, enim nec maximus mollis, tortor metus dapibus mi, id tincidunt massa massa sed velit. Curabitur cursus, lorem rutrum laoreet posuere, tellus eros faucibus odio, non ullamcorper magna lectus non justo. Fusce pulvinar sagittis nisl et pretium. Proin quis pellentesque risus. 10 - 11 - Pellentesque massa purus, ornare aliquam velit et, hendrerit lacinia felis. Fusce nec felis maximus, egestas lorem et, vehicula libero. Donec gravida sodales porta. Nunc ac urna vestibulum, finibus erat vitae, euismod nisi. Morbi auctor pretium diam ut pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus pulvinar orci vitae dolor sodales placerat. Integer tristique condimentum massa, non euismod nisi sodales eu. 12 - 13 - 1. a 14 - 1. b 15 - 1. c 16 - 1. d 17 - 18 - - a 19 - - b 20 - - c 21 - - d 22 - 23 - `test test test` 24 - 25 - ```rust 26 - fn main() { 27 - println!("howdy") 28 - } 29 - ``` 30 - 31 - ```js 32 - function lorem(ipsum, dolor = 1) { 33 - const sit = ipsum == null ? 0 : ipsum.sit; 34 - dolor = sit - amet(dolor); 35 - return sit ? consectetur(ipsum, 0, dolor < 0 ? 0 : dolor) : []; 36 - } 37 - function adipiscing(...elit) { 38 - if (!elit.sit) { 39 - return []; 40 - } 41 - const sed = elit[0]; 42 - return eiusmod.tempor(sed) ? sed : [sed]; 43 - } 44 - function incididunt(ipsum, ut = 1) { 45 - ut = labore.et(amet(ut), 0); 46 - const sit = ipsum == null ? 0 : ipsum.sit; 47 - if (!sit || ut < 1) { 48 - return []; 49 - } 50 - let dolore = 0; 51 - let magna = 0; 52 - const aliqua = new eiusmod(labore.ut(sit / ut)); 53 - while (dolore < sit) { 54 - aliqua[magna++] = consectetur(ipsum, dolore, (dolore += ut)); 55 - } 56 - return aliqua; 57 - } 58 - ```
-148
src/routes/(site)/guestbook/+page.server.ts
··· 1 - import { redirect, type Cookies, type RequestEvent } from '@sveltejs/kit'; 2 - import { scopeCookies as _scopeCookies, fancyText } from '$lib'; 3 - import { RetryAfterRateLimiter } from 'sveltekit-rate-limiter/server'; 4 - import { PUBLIC_BASE_URL } from '$env/static/public'; 5 - import { getBskyClient, getUserPosts } from '$lib/bluesky.js'; 6 - import { getVisitorId } from '$lib/visits'; 7 - import { nanoid } from 'nanoid'; 8 - import { noteFromBskyPost, type NoteData } from '$components/note.svelte'; 9 - import { get, writable } from 'svelte/store'; 10 - import type { Post } from '@skyware/bot'; 11 - import { useToken as checkApiToken, newToken } from '$lib/apiToken.js'; 12 - 13 - export const prerender = false; 14 - 15 - const callbackUrl = `${PUBLIC_BASE_URL}/guestbook/`; 16 - 17 - const createPostRatelimiter = new RetryAfterRateLimiter({ 18 - IP: [5, 'd'], 19 - IPUA: [2, 'h'] 20 - }); 21 - 22 - const scopeCookies = (cookies: Cookies) => { 23 - return _scopeCookies(cookies, '/guestbook'); 24 - }; 25 - 26 - const postTokens = writable<Set<string>>(new Set()); 27 - const entries = writable<NoteData[]>([]); 28 - 29 - export const _fetchEntries = async () => { 30 - const newEntries: NoteData[] = []; 31 - const { posts } = await getUserPosts('did:web:guestbook.gaze.systems', 14); 32 - const fetchPostReplies = async (post: Post) => { 33 - if ((post.replyCount ?? 0) === 0) return { post, replies: [] }; 34 - return { post, replies: await post.fetchChildren({ depth: 1, force: true }) }; 35 - }; 36 - const postsWithReplies = await Promise.all(posts.map(fetchPostReplies)); 37 - for (const { post, replies } of postsWithReplies) { 38 - const note = noteFromBskyPost(post); 39 - note.children = replies.map((reply) => { 40 - const replyNote = noteFromBskyPost(reply); 41 - replyNote.purposeAction = 'reply'; 42 - replyNote.outgoingLinks = [{ name: 'bsky-reply', link: reply.uri }]; 43 - return replyNote; 44 - }); 45 - newEntries.push(note); 46 - } 47 - entries.set(newEntries); 48 - return newEntries; 49 - }; 50 - 51 - export const actions = { 52 - post: async (event: RequestEvent) => { 53 - const { request, cookies } = event; 54 - const scopedCookies = scopeCookies(cookies); 55 - const rateStatus = await createPostRatelimiter.check(event); 56 - if (rateStatus.limited) { 57 - scopedCookies.set( 58 - 'sendError', 59 - `you are being ratelimited sowwy :c, try again after ${rateStatus.retryAfter} seconds` 60 - ); 61 - redirect(303, callbackUrl); 62 - } 63 - const form = await request.formData(); 64 - const apiToken = form.get('_token')?.toString() ?? ''; 65 - if (!checkApiToken(apiToken)) { 66 - scopedCookies.set('sendError', 'api token is invalid'); 67 - redirect(303, callbackUrl); 68 - } 69 - const content = form.get('content')?.toString().substring(0, 300); 70 - if (content === undefined) { 71 - scopedCookies.set('sendError', 'content field is missing'); 72 - redirect(303, callbackUrl); 73 - } 74 - // save form content in a cookie 75 - scopedCookies.set('postData', content); 76 - // create a token we will use to validate 77 - const token = nanoid(); 78 - postTokens.update((set) => set.add(token)); 79 - scopedCookies.set('postAuth', token); 80 - redirect(303, callbackUrl); 81 - } 82 - }; 83 - 84 - export async function load({ cookies }) { 85 - const scopedCookies = scopeCookies(cookies); 86 - const data = { 87 - entries: get(entries), 88 - sendError: scopedCookies.get('sendError') || '', 89 - getError: '', 90 - sendRatelimited: scopedCookies.get('sendRatelimited') || '', 91 - getRatelimited: false, 92 - fillText: fancyText(getVisitorId(cookies) ?? nanoid()), 93 - apiToken: newToken() 94 - }; 95 - const rawPostData = scopedCookies.get('postData') || null; 96 - const postAuth = scopedCookies.get('postAuth') || null; 97 - if (rawPostData !== null && postAuth !== null) { 98 - // delete the postData cookie after we got it cause we dont need it anymore 99 - scopedCookies.delete('postData'); 100 - scopedCookies.delete('postAuth'); 101 - // get and validate token 102 - if (!get(postTokens).has(postAuth)) { 103 - scopedCookies.set( 104 - 'sendError', 105 - 'invalid post token! this is either a bug or you should stop doing silly stuff' 106 - ); 107 - redirect(303, callbackUrl); 108 - } 109 - postTokens.update((set) => { 110 - set.delete(postAuth); 111 - return set; 112 - }); 113 - // post entry 114 - try { 115 - // return error if content was not set or if empty 116 - const content = rawPostData.substring(0, 300).trim(); 117 - if (content.length === 0) { 118 - scopedCookies.set('sendError', `content field was empty`); 119 - redirect(303, callbackUrl); 120 - } 121 - // post to guestbook account 122 - const client = await getBskyClient(); 123 - await client.post( 124 - { 125 - text: content, 126 - threadgate: { allowMentioned: false, allowFollowing: true } 127 - }, 128 - { resolveFacets: false } 129 - ); 130 - try { 131 - data.entries = await _fetchEntries(); 132 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 133 - } catch (err: any) { 134 - data.getError = err.toString(); 135 - } 136 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 137 - } catch (err: any) { 138 - scopedCookies.set('sendError', err.toString()); 139 - redirect(303, callbackUrl); 140 - } 141 - redirect(303, callbackUrl); 142 - } 143 - // delete the cookies after we get em since we dont really need these more than once 144 - scopedCookies.delete('sendError'); 145 - scopedCookies.delete('sendRatelimited'); 146 - 147 - return data; 148 - }
-181
src/routes/(site)/guestbook/+page.svelte
··· 1 - <script lang="ts"> 2 - import Note, { type NoteData } from '$components/note.svelte'; 3 - import Token from '$components/token.svelte'; 4 - import Window from '$components/window.svelte'; 5 - 6 - interface Props { 7 - data: { 8 - entries: NoteData[]; 9 - sendError: string; 10 - getError: string; 11 - sendRatelimited: string; 12 - getRatelimited: boolean; 13 - fillText: string; 14 - apiToken: string; 15 - }; 16 - } 17 - 18 - let { data }: Props = $props(); 19 - 20 - const placeholders = ['meow', 'arf', '0110100001101001', '0x6869']; 21 - </script> 22 - 23 - <div class="flex flex-col-reverse md:flex-row gap-2 md:gap-4"> 24 - <Window title="guestbook" style="ml-auto" iconUri="/icons/guestbook.webp"> 25 - <div class="flex flex-col gap-1 max-w-[50ch] leading-6"> 26 - <div class="prose prose-ralsei leading-6 entry p-2"> 27 - <p>hia, here is the guestbook if you wanna post anything :)</p> 28 - <p>be good pretty please (and don't be shy!!!)</p> 29 - <p class="text-sm italic"> 30 - (to see all the entries, look <a href="https://bsky.app/profile/guestbook.gaze.systems" 31 - >here</a 32 - >) 33 - </p> 34 - </div> 35 - <form method="post"> 36 - <input type="hidden" name="_token" value={data.apiToken} /> 37 - <div class="entry entryflex"> 38 - <textarea 39 - class="text-lg p-1 m-0 ml-0.5 bg-transparent resize-none text-shadow-white placeholder-shown:[text-shadow:none] [field-sizing:content] border-none" 40 - name="content" 41 - placeholder="say {placeholders[Math.floor(Math.random() * placeholders.length)]}!" 42 - maxlength="300" 43 - required 44 - ></textarea> 45 - </div> 46 - <div class="flex flex-row gap-1 mt-1"> 47 - <input 48 - type="submit" 49 - value="click to post" 50 - formaction="?/post" 51 - class="entry text-ralsei-green-light leading-none hover:underline motion-safe:hover:animate-squiggle p-1 z-50" 52 - /> 53 - <div class="marquee-wrapper entry text-ralsei-white/50"> 54 - <div class="marquee font-monospace"> 55 - <p class="text-shadow-none">{data.fillText}</p> 56 - <p class="text-shadow-none">{data.fillText}</p> 57 - </div> 58 - </div> 59 - </div> 60 - {#if data.sendRatelimited} 61 - <p class="text-error">you are ratelimited, try again in 30 seconds</p> 62 - {/if} 63 - {#if data.sendError} 64 - <details class="w-[50ch]"> 65 - <summary class="text-error">got error trying to send post</summary> 66 - <p>{data.sendError}</p> 67 - </details> 68 - {/if} 69 - </form> 70 - </div> 71 - </Window> 72 - <Window 73 - id="guestbookentries" 74 - style="mr-auto" 75 - title="entries" 76 - iconUri="/icons/entries.webp" 77 - removePadding 78 - > 79 - <div class="flex flex-col gap-2 md:gap-4 2xl:w-[60ch]"> 80 - {#if data.getRatelimited} 81 - <p class="text-error"> 82 - woops, looks like you are being ratelimited, try again in like half a minute :3 83 - </p> 84 - {:else if data.getError} 85 - <details class="w-[50ch]"> 86 - <summary class="text-error">got error trying to fetch entries</summary> 87 - <p>{data.getError}</p> 88 - </details> 89 - {:else} 90 - <div 91 - class=" 92 - prose prose-ralsei 93 - prose-pre:rounded-none prose-pre:!m-0 prose-pre:!p-2 94 - prose-pre:!bg-ralsei-black prose-code:!bg-ralsei-black 95 - " 96 - > 97 - <pre class="language-bash"><code class="language-bash" 98 - ><nobr> 99 - <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 100 - v="source" 101 - funct 102 - /> <Token v="scripts/log.nu" /> 103 - <br /> 104 - <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 105 - v="let" 106 - funct 107 - /> <Token v="entries" /> <Token v="=" punct /> <Token v="(" punct /><Token 108 - v="ls" 109 - funct 110 - /> <Token v="guestbook" /> <Token v="|" punct /> <Token v="reverse" funct /> <Token 111 - v="|" 112 - punct 113 - /> <Token v="take" funct /> <Token v="14" /><Token v=")" punct /> 114 - <br /> 115 - <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 116 - v="$entries" 117 - /> <Token v="|" punct /> <Token v="each" funct /> <Token v="&#123;" punct /><Token 118 - v="|" 119 - punct 120 - /><Token v="file" /><Token v="|" punct /> <Token v="render" funct /> <Token 121 - v="(" 122 - punct 123 - /><Token v="open" funct /> <Token v="$file.name" /><Token v=")" punct /><Token 124 - v="&#125;" 125 - punct 126 - /> 127 - <br /> 128 - <br /> 129 - {#each data.entries as note, index ([note.content, note.published])} 130 - <Note 131 - mapOutgoingNames={{ bsky: '', reply: 'src' }} 132 - showOutgoing={true} 133 - rootNote={note} 134 - /> 135 - {#if index < data.entries.length - 1} 136 - <div class="mt-2"></div> 137 - {/if} 138 - {/each} 139 - </nobr></code 140 - ></pre> 141 - </div> 142 - {/if} 143 - </div> 144 - </Window> 145 - </div> 146 - 147 - <style lang="postcss"> 148 - .entry { 149 - @apply bg-ralsei-green-dark/70 border-ralsei-green-light/30 border-x-[4px] border-y-[5px]; 150 - border-style: ridge; 151 - } 152 - .entryflex { 153 - @apply flex flex-col p-1; 154 - } 155 - 156 - .marquee-wrapper { 157 - max-width: 100%; 158 - overflow: hidden; 159 - } 160 - 161 - .marquee { 162 - white-space: nowrap; 163 - overflow: hidden; 164 - display: inline-block; 165 - animation: marquee 10s linear infinite; 166 - } 167 - 168 - .marquee p { 169 - transform: translateY(15%); 170 - display: inline-block; 171 - } 172 - 173 - @keyframes marquee { 174 - 0% { 175 - transform: translate3d(0, 0, 0); 176 - } 177 - 100% { 178 - transform: translate3d(-50%, 0, 0); 179 - } 180 - } 181 - </style>
-12
src/routes/(site)/log/+page.server.ts
··· 1 - import { getLastPosts } from '$lib/bluesky.js'; 2 - import { noteFromBskyPost } from '$components/note.svelte'; 3 - 4 - export const load = async () => { 5 - return _load(); 6 - }; 7 - 8 - export const _load = async () => { 9 - return { 10 - feedPosts: getLastPosts().map(noteFromBskyPost) 11 - }; 12 - };
-64
src/routes/(site)/log/+page.svelte
··· 1 - <script lang="ts"> 2 - import Window from '$components/window.svelte'; 3 - import Token from '$components/token.svelte'; 4 - import Note, { type NoteData } from '$components/note.svelte'; 5 - 6 - interface Props { 7 - data: { 8 - feedPosts: NoteData[]; 9 - }; 10 - } 11 - 12 - let { data }: Props = $props(); 13 - </script> 14 - 15 - <Window title="terminal" removePadding> 16 - <div 17 - class=" 18 - prose prose-ralsei 19 - prose-pre:rounded-none prose-pre:!m-0 prose-pre:!p-2 20 - prose-pre:!bg-ralsei-black prose-code:!bg-ralsei-black 21 - " 22 - > 23 - <pre class="language-bash"><code class="language-bash" 24 - ><nobr> 25 - <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 26 - v="source" 27 - funct 28 - /> <Token v="scripts/log.nu" /> 29 - <br /> 30 - <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 31 - v="let" 32 - funct 33 - /> <Token v="entries" /> <Token v="=" punct /> <Token v="(" punct /><Token 34 - v="ls" 35 - funct 36 - /> <Token v="logs" /> <Token v="|" punct /> <Token v="reverse" funct /> <Token 37 - v="|" 38 - punct 39 - /> <Token v="take" funct /> <Token v="10" /><Token v=")" punct /> 40 - <br /> 41 - <Token v="[" punct />gazesys <Token v="/" keywd /><Token v="]$" punct /> <Token 42 - v="$entries" 43 - /> <Token v="|" punct /> <Token v="each" funct /> <Token v="&#123;" punct /><Token 44 - v="|" 45 - punct 46 - /><Token v="file" /><Token v="|" punct /> <Token v="render" funct /> <Token 47 - v="(" 48 - punct 49 - /><Token v="open" funct /> <Token v="$file.name" /><Token v=")" punct /><Token 50 - v="&#125;" 51 - punct 52 - /> 53 - <br /> 54 - <br /> 55 - {#each data.feedPosts as note, index ([note.content, note.published])} 56 - <Note rootNote={note} /> 57 - {#if index < data.feedPosts.length - 1} 58 - <div class="mt-2"></div> 59 - {/if} 60 - {/each} 61 - </nobr></code 62 - ></pre> 63 - </div> 64 - </Window>
-5
src/routes/(site)/log/_rss/+server.ts
··· 1 - import { redirect } from '@sveltejs/kit'; 2 - 3 - export const GET = async () => { 4 - redirect(301, 'https://bsky.app/profile/did:plc:dfl62fgb7wtjj3fcbb72naae/rss'); 5 - };
-13
src/routes/_api/pet/bounce/+server.ts
··· 1 - import { incrementBounceCount, pushMetric } from '$lib/metrics'; 2 - import { isBot } from '$lib/visits'; 3 - import { checkUrl as checkApiToken } from '$lib/apiToken.js'; 4 - 5 - export const GET = async ({ request, url }) => { 6 - if (isBot(request) || !checkApiToken(url)) return new Response(); 7 - try { 8 - await pushMetric({ gazesys_pet_bounce_total: await incrementBounceCount() }); 9 - } catch (error) { 10 - console.log(`error while pushing bounce metric: ${error}`); 11 - } 12 - return new Response(); 13 - };
-14
src/routes/_api/pet/distance/+server.ts
··· 1 - import { distanceTravelled, pushMetric } from '$lib/metrics'; 2 - import { isBot } from '$lib/visits'; 3 - import { checkUrl as checkApiToken } from '$lib/apiToken.js'; 4 - 5 - export const POST = async ({ request, url }) => { 6 - if (isBot(request) || !checkApiToken(url)) return new Response(); 7 - try { 8 - const delta = parseFloat(await request.text()); 9 - await pushMetric({ gazesys_pet_distance_total: await distanceTravelled.increment(delta) }); 10 - } catch (error) { 11 - console.log(`error while pushing bounce metric: ${error}`); 12 - } 13 - return new Response(); 14 - };
-10
src/routes/_api/pushnotif/+server.ts
··· 1 - import { checkUrl as checkApiToken } from '$lib/apiToken.js'; 2 - import { pushNotification } from '$lib/pushnotif'; 3 - 4 - export const GET = async ({ url }) => { 5 - if (!checkApiToken(url)) return new Response(); 6 - const content = url.searchParams.get('content'); 7 - if (content === null) return new Response(); 8 - pushNotification(content); 9 - return new Response(); 10 - };
-5
src/routes/annoy/+server.ts
··· 1 - const index = await import('./index.html?raw'); 2 - 3 - export const GET = async () => { 4 - return new Response(index.default, { headers: { 'Content-Type': 'text/html' } }); 5 - };
-29
src/routes/annoy/index.html
··· 1 - <!doctype html> 2 - <html> 3 - <head> 4 - <meta charset="UTF-8" /> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 - <title>annoy</title> 7 - <style type="text/css"> 8 - * { 9 - margin: 0; 10 - padding: 0; 11 - overflow: hidden; 12 - } 13 - 14 - canvas { 15 - width: 100vw !important; 16 - height: 100vh !important; 17 - } 18 - </style> 19 - </head> 20 - 21 - <body> 22 - <script type="module"> 23 - import init from './annoy/annoyance-meow.js'; 24 - window.addEventListener('load', () => { 25 - init(); 26 - }); 27 - </script> 28 - </body> 29 - </html>
-5
src/routes/robots.txt/+server.ts
··· 1 - import { getRobotsTxt } from '$lib/robots'; 2 - 3 - export const GET = async () => { 4 - return new Response(await getRobotsTxt()); 5 - };
-408
src/styles/app.css
··· 1 - @import './prism-synthwave84.css'; 2 - 3 - @tailwind base; 4 - @tailwind components; 5 - @tailwind utilities; 6 - 7 - @layer base { 8 - :root { 9 - @apply font-sans-serif bg-ralsei-black text-ralsei-white; 10 - @apply prose-code:font-monospace prose-headings:font-monospace; 11 - cursor: url('/icons/gaze_closed.webp'), default; 12 - scrollbar-color: theme(colors.ralsei.green.dark) transparent; 13 - -webkit-font-smoothing: none !important; 14 - font-smooth: never !important; 15 - font-smoothing: none !important; 16 - } 17 - 18 - @font-face { 19 - font-family: 'Fusion Pixel 10px Monospaced zh_hans'; 20 - src: url('/fonts/fusion-pixel-sc-monospaced.woff2') format('woff2'); 21 - font-weight: normal; 22 - font-style: normal; 23 - font-display: swap; 24 - } 25 - 26 - @font-face { 27 - font-family: 'Fusion Pixel 10px Proportional zh_hans'; 28 - src: url('/fonts/fusion-pixel-sc-proportional.woff2') format('woff2'); 29 - font-weight: normal; 30 - font-style: normal; 31 - font-display: swap; 32 - } 33 - 34 - @font-face { 35 - font-family: 'Doll Mono'; 36 - src: url('/fonts/dollmonoopt.woff2') format('woff2'); 37 - } 38 - 39 - .prose h1::before { 40 - content: '[ '; 41 - } 42 - .prose h1::after { 43 - content: ' ]'; 44 - } 45 - 46 - .prose h2::before { 47 - content: '[= '; 48 - } 49 - .prose h2::after { 50 - content: ' =]'; 51 - } 52 - 53 - .prose h3::before { 54 - content: '[== '; 55 - } 56 - .prose h3::after { 57 - content: ' ==]'; 58 - } 59 - 60 - .prose h4::before { 61 - content: '[=== '; 62 - } 63 - .prose h4::after { 64 - content: ' ===]'; 65 - } 66 - 67 - /* .prose h1::after,.prose h2::after,.prose h3::after,.prose h4::after { 68 - @apply motion-safe:animate-blink; 69 - content: '_'; 70 - } */ 71 - 72 - .prose a { 73 - text-decoration: none; 74 - } 75 - 76 - .prose a:hover { 77 - @apply motion-safe:animate-squiggle; 78 - text-decoration: underline; 79 - } 80 - 81 - h1, 82 - h2, 83 - h3, 84 - h4, 85 - h5, 86 - h6, 87 - .text-shadow-pink { 88 - text-shadow: 89 - 0 0 3px theme(colors.ralsei.black), 90 - 0 0 6px theme(colors.ralsei.pink.neon), 91 - 0 0 10px #fff3; 92 - } 93 - 94 - .text-shadow-red { 95 - text-shadow: 96 - 0 0 1px theme(colors.ralsei.black), 97 - 0 0 5px theme(colors.red.600); 98 - } 99 - 100 - .text-shadow-none { 101 - text-shadow: none; 102 - } 103 - 104 - .prose ul, 105 - ul { 106 - list-style-type: '>>'; 107 - } 108 - 109 - .text-shadow-green { 110 - text-shadow: 111 - 0 0 2px theme(colors.ralsei.black), 112 - 0 0 5px theme(colors.ralsei.green.light); 113 - } 114 - 115 - a, 116 - button, 117 - input[type='submit'] { 118 - @apply text-shadow-green; 119 - } 120 - 121 - a, 122 - button, 123 - details, 124 - input[type='submit'] { 125 - cursor: url('/icons/gaze.webp'), pointer; 126 - } 127 - 128 - .animate-squiggle { 129 - animation: squigglevision 0.3s infinite; 130 - } 131 - 132 - @keyframes squigglevision { 133 - 0% { 134 - filter: url('#squiggly-0'); 135 - } 136 - 25% { 137 - filter: url('#squiggly-1'); 138 - } 139 - 50% { 140 - filter: url('#squiggly-2'); 141 - } 142 - 75% { 143 - filter: url('#squiggly-3'); 144 - } 145 - 100% { 146 - filter: url('#squiggly-4'); 147 - } 148 - } 149 - 150 - @keyframes blink { 151 - 0% { 152 - opacity: 1; 153 - } 154 - 50% { 155 - opacity: 0; 156 - } 157 - 100% { 158 - opacity: 1; 159 - } 160 - } 161 - } 162 - 163 - @layer utilities { 164 - .text-error { 165 - @apply text-xl text-red-600 text-shadow-red; 166 - } 167 - 168 - .border-groove { 169 - border-style: groove; 170 - } 171 - 172 - .border-ridge { 173 - border-style: ridge; 174 - } 175 - 176 - .app-grid-background-anim { 177 - animation: 4s linear app-grid-move-first-layer infinite; 178 - } 179 - 180 - .app-grid-background-second-layer-anim { 181 - animation: 12s linear app-grid-move-second-layer infinite; 182 - } 183 - 184 - @keyframes app-grid-move-first-layer { 185 - 0% { 186 - background-position: 0px 0px; 187 - } 188 - 100% { 189 - background-position: 126px 84px; 190 - } 191 - } 192 - 193 - @keyframes app-grid-move-second-layer { 194 - 0% { 195 - background-position: 96px 120px; 196 - } 197 - 100% { 198 - background-position: 0px 0px; 199 - } 200 - } 201 - 202 - @media (prefers-reduced-motion: no-preference) { 203 - @keyframes bounce-reverse { 204 - 0%, 205 - 100% { 206 - transform: none; 207 - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); 208 - } 209 - 50% { 210 - transform: translateY(-25%); 211 - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); 212 - } 213 - } 214 - } 215 - @media (prefers-reduced-motion: no-preference) { 216 - .animate-bounce-reverse:hover { 217 - animation: bounce-reverse 1s infinite; 218 - } 219 - } 220 - } 221 - 222 - a.app-selected-route { 223 - text-shadow: 224 - 0 0 2px theme(colors.ralsei.black), 225 - 0 0 5px theme(colors.ralsei.pink.regular); 226 - } 227 - 228 - .app-grid-background { 229 - background-image: 230 - linear-gradient(theme(colors.ralsei.green.light / 0.4), transparent 2px), 231 - linear-gradient(to right, theme(colors.ralsei.green.light / 0.4), transparent 2px); 232 - background-size: 233 - 100% 42px, 234 - 42px 100%; 235 - } 236 - 237 - .app-grid-background-second-layer { 238 - background-image: 239 - linear-gradient(theme(colors.ralsei.pink.neon / 0.4), transparent 1px), 240 - linear-gradient(to right, theme(colors.ralsei.pink.neon / 0.4), transparent 1px); 241 - background-size: 242 - 100% 24px, 243 - 24px 100%; 244 - } 245 - 246 - @media (prefers-reduced-motion: no-preference) { 247 - .animate-window-open { 248 - animation: 0.5s ease-out window-open-scale forwards; 249 - } 250 - 251 - .animate-window-open-vertical { 252 - animation: 0.5s ease-out window-open-scale-vertical forwards; 253 - transform-origin: bottom; 254 - } 255 - 256 - .animate-window-open-horizontal { 257 - animation: 0.5s ease-out window-open-scale-horizontal forwards; 258 - transform-origin: left; 259 - } 260 - 261 - .animate-window-open-move-up { 262 - animation: 0.5s ease-out window-open-move-up forwards; 263 - } 264 - 265 - .animate-window-open-move-down { 266 - animation: 0.5s ease-out window-open-move-down forwards; 267 - } 268 - 269 - .animate-window-open-move-left { 270 - animation: 0.5s ease-out window-open-move-left forwards; 271 - } 272 - 273 - .animate-window-open-move-right { 274 - animation: 0.5s ease-out window-open-move-right forwards; 275 - } 276 - 277 - .animate-overflow-keep-hidden { 278 - animation: 0.6s linear overflow-keep-hidden forwards; 279 - } 280 - 281 - @keyframes window-open-scale { 282 - 0% { 283 - scale: 0; 284 - opacity: 0; 285 - } 286 - 20% { 287 - scale: 0; 288 - } 289 - 60% { 290 - opacity: 0.5; 291 - } 292 - 100% { 293 - scale: 1; 294 - opacity: 1; 295 - } 296 - } 297 - 298 - @keyframes window-open-scale-vertical { 299 - 0% { 300 - scale: 1 0; 301 - opacity: 0; 302 - } 303 - 20% { 304 - scale: 1 0; 305 - } 306 - 60% { 307 - opacity: 0.5; 308 - } 309 - 100% { 310 - scale: 1 1; 311 - opacity: 1; 312 - } 313 - } 314 - 315 - @keyframes window-open-scale-horizontal { 316 - 0% { 317 - scale: 0 1; 318 - opacity: 0; 319 - } 320 - 20% { 321 - scale: 0 1; 322 - } 323 - 60% { 324 - opacity: 0.5; 325 - } 326 - 100% { 327 - scale: 1 1; 328 - opacity: 1; 329 - } 330 - } 331 - 332 - @keyframes window-open-move-down { 333 - 0% { 334 - translate: 0 10rem; 335 - opacity: 0; 336 - } 337 - 20% { 338 - translate: 0 10rem; 339 - } 340 - 60% { 341 - opacity: 0.5; 342 - } 343 - 100% { 344 - translate: normal; 345 - opacity: 1; 346 - } 347 - } 348 - 349 - @keyframes window-open-move-up { 350 - 0% { 351 - translate: 0 -10rem; 352 - opacity: 0; 353 - } 354 - 20% { 355 - translate: 0 -10rem; 356 - } 357 - 60% { 358 - opacity: 0.5; 359 - } 360 - 100% { 361 - translate: normal; 362 - opacity: 1; 363 - } 364 - } 365 - 366 - @keyframes window-open-move-left { 367 - 0% { 368 - translate: 10rem 0; 369 - opacity: 0; 370 - } 371 - 20% { 372 - translate: 10rem 0; 373 - } 374 - 60% { 375 - opacity: 0.5; 376 - } 377 - 100% { 378 - translate: normal; 379 - opacity: 1; 380 - } 381 - } 382 - 383 - @keyframes window-open-move-right { 384 - 0% { 385 - translate: -10rem 0; 386 - opacity: 0; 387 - } 388 - 20% { 389 - translate: -10rem 0; 390 - } 391 - 60% { 392 - opacity: 0.5; 393 - } 394 - 100% { 395 - translate: normal; 396 - opacity: 1; 397 - } 398 - } 399 - 400 - @keyframes overflow-keep-hidden { 401 - 0% { 402 - overflow: hidden; 403 - } 404 - 100% { 405 - overflow: auto; 406 - } 407 - } 408 - }
-25
src/styles/main.css
··· 1 - @import 'app.css'; 2 - 3 - .entry { 4 - @apply bg-ralsei-green-dark/70 border-ralsei-green-light/30 border-x-[4px] border-y-[5px]; 5 - border-style: ridge; 6 - } 7 - 8 - details { 9 - @apply leading-none mt-2; 10 - summary { 11 - @apply text-shadow-pink text-ralsei-pink-neon hover:underline; 12 - } 13 - summary::marker { 14 - content: '(+) '; 15 - } 16 - } 17 - details[open] summary::marker { 18 - content: '(*) '; 19 - } 20 - 21 - .donate ul { 22 - li span { 23 - @apply font-monospace overflow-hidden text-ellipsis text-nowrap; 24 - } 25 - }
-140
src/styles/prism-synthwave84.css
··· 1 - /* 2 - * Synthwave '84 Theme originally by Robb Owen [@Robb0wen] for Visual Studio Code 3 - * Demo: https://marc.dev/demo/prism-synthwave84 4 - * 5 - * Ported for PrismJS by Marc Backes [@themarcba] 6 - */ 7 - 8 - code[class*="language-"], 9 - pre[class*="language-"] { 10 - color: theme(colors.ralsei.pink.neon); 11 - text-shadow: 0 0 2px theme(colors.ralsei.black), 0 0 5px #ff3eb733, 0 0 10px #fff3; 12 - @apply bg-ralsei-green-dark; 13 - @apply font-monospace; 14 - font-size: 1rem; 15 - line-height: 1.4rem; 16 - text-align: left; 17 - white-space: pre; 18 - word-spacing: normal; 19 - word-break: normal; 20 - word-wrap: normal; 21 - 22 - -moz-tab-size: 4; 23 - -o-tab-size: 4; 24 - tab-size: 4; 25 - 26 - -webkit-hyphens: none; 27 - -moz-hyphens: none; 28 - -ms-hyphens: none; 29 - hyphens: none; 30 - } 31 - 32 - /* Code blocks */ 33 - pre[class*="language-"] { 34 - padding: 1em; 35 - margin: .5em 0; 36 - overflow: auto; 37 - } 38 - 39 - /* :not(pre) > code[class*="language-"], 40 - pre[class*="language-"] { 41 - background-color: transparent !important; 42 - background-image: linear-gradient(to bottom, #2a2139 75%, #34294f); 43 - } */ 44 - 45 - /* Inline code */ 46 - :not(pre) > code[class*="language-"] { 47 - padding: .1em; 48 - border-radius: .3em; 49 - white-space: normal; 50 - } 51 - 52 - .token.comment, 53 - .token.block-comment, 54 - .token.prolog, 55 - .token.doctype, 56 - .token.cdata { 57 - color: #8e8e8e; 58 - } 59 - 60 - .token.punctuation { 61 - color: #ccc; 62 - } 63 - 64 - .token.tag, 65 - .token.attr-name, 66 - .token.namespace, 67 - .token.number, 68 - .token.unit, 69 - .token.hexcode, 70 - .token.deleted { 71 - color: #e2777a; 72 - } 73 - 74 - .token.property, 75 - .token.selector { 76 - color: #72f1b8; 77 - text-shadow: 0 0 2px #100c0f, 0 0 10px #257c5575, 0 0 35px #21272475; 78 - } 79 - 80 - .token.function-name { 81 - color: #6196cc; 82 - } 83 - 84 - .token.boolean, 85 - .token.selector .token.id, 86 - .token.function { 87 - color: #fdfdfd; 88 - text-shadow: 0 0 2px #001716, 0 0 3px #03edf975, 0 0 5px #03edf975, 0 0 8px #03edf975; 89 - } 90 - 91 - .token.class-name { 92 - color: #fff5f6; 93 - text-shadow: 0 0 2px #000, 0 0 10px #fc1f2c75, 0 0 5px #fc1f2c75, 0 0 25px #fc1f2c75; 94 - } 95 - 96 - .token.constant, 97 - .token.symbol { 98 - color: #f92aad; 99 - text-shadow: 0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3; 100 - } 101 - 102 - .token.important, 103 - .token.atrule, 104 - .token.keyword, 105 - .token.selector .token.class, 106 - .token.builtin { 107 - color: #f4eee4; 108 - text-shadow: 0 0 2px #393a33, 0 0 8px #f39f0575, 0 0 2px #f39f0575; 109 - } 110 - 111 - .token.string, 112 - .token.char, 113 - .token.attr-value, 114 - .token.regex, 115 - .token.variable { 116 - color: #f87c32; 117 - } 118 - 119 - .token.operator, 120 - .token.entity, 121 - .token.url { 122 - color: #67cdcc; 123 - } 124 - 125 - .token.important, 126 - .token.bold { 127 - font-weight: bold; 128 - } 129 - 130 - .token.italic { 131 - font-style: italic; 132 - } 133 - 134 - .token.entity { 135 - cursor: help; 136 - } 137 - 138 - .token.inserted { 139 - color: green; 140 - }
static/88x31.gif

This is a binary file and will not be displayed.

static/88x31_midnight.gif

This is a binary file and will not be displayed.

static/88x31_sunrise.gif

This is a binary file and will not be displayed.

-1203
static/annoy/annoyance-meow.js
··· 1 - let wasm; 2 - 3 - function isLikeNone(x) { 4 - return x === undefined || x === null; 5 - } 6 - 7 - function addToExternrefTable0(obj) { 8 - const idx = wasm.__externref_table_alloc(); 9 - wasm.__wbindgen_export_1.set(idx, obj); 10 - return idx; 11 - } 12 - 13 - const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); 14 - 15 - if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; 16 - 17 - let cachedUint8ArrayMemory0 = null; 18 - 19 - function getUint8ArrayMemory0() { 20 - if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { 21 - cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); 22 - } 23 - return cachedUint8ArrayMemory0; 24 - } 25 - 26 - function getStringFromWasm0(ptr, len) { 27 - ptr = ptr >>> 0; 28 - return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); 29 - } 30 - 31 - function handleError(f, args) { 32 - try { 33 - return f.apply(this, args); 34 - } catch (e) { 35 - const idx = addToExternrefTable0(e); 36 - wasm.__wbindgen_exn_store(idx); 37 - } 38 - } 39 - 40 - let WASM_VECTOR_LEN = 0; 41 - 42 - const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); 43 - 44 - const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 45 - ? function (arg, view) { 46 - return cachedTextEncoder.encodeInto(arg, view); 47 - } 48 - : function (arg, view) { 49 - const buf = cachedTextEncoder.encode(arg); 50 - view.set(buf); 51 - return { 52 - read: arg.length, 53 - written: buf.length 54 - }; 55 - }); 56 - 57 - function passStringToWasm0(arg, malloc, realloc) { 58 - 59 - if (realloc === undefined) { 60 - const buf = cachedTextEncoder.encode(arg); 61 - const ptr = malloc(buf.length, 1) >>> 0; 62 - getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); 63 - WASM_VECTOR_LEN = buf.length; 64 - return ptr; 65 - } 66 - 67 - let len = arg.length; 68 - let ptr = malloc(len, 1) >>> 0; 69 - 70 - const mem = getUint8ArrayMemory0(); 71 - 72 - let offset = 0; 73 - 74 - for (; offset < len; offset++) { 75 - const code = arg.charCodeAt(offset); 76 - if (code > 0x7F) break; 77 - mem[ptr + offset] = code; 78 - } 79 - 80 - if (offset !== len) { 81 - if (offset !== 0) { 82 - arg = arg.slice(offset); 83 - } 84 - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; 85 - const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); 86 - const ret = encodeString(arg, view); 87 - 88 - offset += ret.written; 89 - ptr = realloc(ptr, len, offset, 1) >>> 0; 90 - } 91 - 92 - WASM_VECTOR_LEN = offset; 93 - return ptr; 94 - } 95 - 96 - let cachedDataViewMemory0 = null; 97 - 98 - function getDataViewMemory0() { 99 - if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { 100 - cachedDataViewMemory0 = new DataView(wasm.memory.buffer); 101 - } 102 - return cachedDataViewMemory0; 103 - } 104 - 105 - let cachedUint8ClampedArrayMemory0 = null; 106 - 107 - function getUint8ClampedArrayMemory0() { 108 - if (cachedUint8ClampedArrayMemory0 === null || cachedUint8ClampedArrayMemory0.byteLength === 0) { 109 - cachedUint8ClampedArrayMemory0 = new Uint8ClampedArray(wasm.memory.buffer); 110 - } 111 - return cachedUint8ClampedArrayMemory0; 112 - } 113 - 114 - function getClampedArrayU8FromWasm0(ptr, len) { 115 - ptr = ptr >>> 0; 116 - return getUint8ClampedArrayMemory0().subarray(ptr / 1, ptr / 1 + len); 117 - } 118 - 119 - function getArrayU8FromWasm0(ptr, len) { 120 - ptr = ptr >>> 0; 121 - return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); 122 - } 123 - 124 - const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') 125 - ? { register: () => {}, unregister: () => {} } 126 - : new FinalizationRegistry(state => { 127 - wasm.__wbindgen_export_6.get(state.dtor)(state.a, state.b) 128 - }); 129 - 130 - function makeMutClosure(arg0, arg1, dtor, f) { 131 - const state = { a: arg0, b: arg1, cnt: 1, dtor }; 132 - const real = (...args) => { 133 - // First up with a closure we increment the internal reference 134 - // count. This ensures that the Rust closure environment won't 135 - // be deallocated while we're invoking it. 136 - state.cnt++; 137 - const a = state.a; 138 - state.a = 0; 139 - try { 140 - return f(a, state.b, ...args); 141 - } finally { 142 - if (--state.cnt === 0) { 143 - wasm.__wbindgen_export_6.get(state.dtor)(a, state.b); 144 - CLOSURE_DTORS.unregister(state); 145 - } else { 146 - state.a = a; 147 - } 148 - } 149 - }; 150 - real.original = state; 151 - CLOSURE_DTORS.register(real, state, state); 152 - return real; 153 - } 154 - 155 - function debugString(val) { 156 - // primitive types 157 - const type = typeof val; 158 - if (type == 'number' || type == 'boolean' || val == null) { 159 - return `${val}`; 160 - } 161 - if (type == 'string') { 162 - return `"${val}"`; 163 - } 164 - if (type == 'symbol') { 165 - const description = val.description; 166 - if (description == null) { 167 - return 'Symbol'; 168 - } else { 169 - return `Symbol(${description})`; 170 - } 171 - } 172 - if (type == 'function') { 173 - const name = val.name; 174 - if (typeof name == 'string' && name.length > 0) { 175 - return `Function(${name})`; 176 - } else { 177 - return 'Function'; 178 - } 179 - } 180 - // objects 181 - if (Array.isArray(val)) { 182 - const length = val.length; 183 - let debug = '['; 184 - if (length > 0) { 185 - debug += debugString(val[0]); 186 - } 187 - for(let i = 1; i < length; i++) { 188 - debug += ', ' + debugString(val[i]); 189 - } 190 - debug += ']'; 191 - return debug; 192 - } 193 - // Test for built-in 194 - const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); 195 - let className; 196 - if (builtInMatches && builtInMatches.length > 1) { 197 - className = builtInMatches[1]; 198 - } else { 199 - // Failed to match the standard '[object ClassName]' 200 - return toString.call(val); 201 - } 202 - if (className == 'Object') { 203 - // we're a user defined class or Object 204 - // JSON.stringify avoids problems with cycles, and is generally much 205 - // easier than looping through ownProperties of `val`. 206 - try { 207 - return 'Object(' + JSON.stringify(val) + ')'; 208 - } catch (_) { 209 - return 'Object'; 210 - } 211 - } 212 - // errors 213 - if (val instanceof Error) { 214 - return `${val.name}: ${val.message}\n${val.stack}`; 215 - } 216 - // TODO we could test for more things here, like `Set`s and `Map`s. 217 - return className; 218 - } 219 - function __wbg_adapter_30(arg0, arg1, arg2) { 220 - wasm.closure3_externref_shim(arg0, arg1, arg2); 221 - } 222 - 223 - function __wbg_adapter_51(arg0, arg1, arg2, arg3) { 224 - wasm.closure105_externref_shim(arg0, arg1, arg2, arg3); 225 - } 226 - 227 - function __wbg_adapter_56(arg0, arg1) { 228 - wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb7d4ca77f39188b4(arg0, arg1); 229 - } 230 - 231 - const __wbindgen_enum_BinaryType = ["blob", "arraybuffer"]; 232 - 233 - const __wbindgen_enum_ResizeObserverBoxOptions = ["border-box", "content-box", "device-pixel-content-box"]; 234 - 235 - const __wbindgen_enum_VisibilityState = ["hidden", "visible"]; 236 - 237 - async function __wbg_load(module, imports) { 238 - if (typeof Response === 'function' && module instanceof Response) { 239 - if (typeof WebAssembly.instantiateStreaming === 'function') { 240 - try { 241 - return await WebAssembly.instantiateStreaming(module, imports); 242 - 243 - } catch (e) { 244 - if (module.headers.get('Content-Type') != 'application/wasm') { 245 - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 246 - 247 - } else { 248 - throw e; 249 - } 250 - } 251 - } 252 - 253 - const bytes = await module.arrayBuffer(); 254 - return await WebAssembly.instantiate(bytes, imports); 255 - 256 - } else { 257 - const instance = await WebAssembly.instantiate(module, imports); 258 - 259 - if (instance instanceof WebAssembly.Instance) { 260 - return { instance, module }; 261 - 262 - } else { 263 - return instance; 264 - } 265 - } 266 - } 267 - 268 - function __wbg_get_imports() { 269 - const imports = {}; 270 - imports.wbg = {}; 271 - imports.wbg.__wbg_Window_d1bf622f71ff0629 = function(arg0) { 272 - const ret = arg0.Window; 273 - return ret; 274 - }; 275 - imports.wbg.__wbg_abort_775ef1d17fc65868 = function(arg0) { 276 - arg0.abort(); 277 - }; 278 - imports.wbg.__wbg_activeElement_367599fdfa7ad115 = function(arg0) { 279 - const ret = arg0.activeElement; 280 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 281 - }; 282 - imports.wbg.__wbg_addEventListener_90e553fdce254421 = function() { return handleError(function (arg0, arg1, arg2, arg3) { 283 - arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3); 284 - }, arguments) }; 285 - imports.wbg.__wbg_addListener_2982bb811b6385c5 = function() { return handleError(function (arg0, arg1) { 286 - arg0.addListener(arg1); 287 - }, arguments) }; 288 - imports.wbg.__wbg_altKey_c33c03aed82e4275 = function(arg0) { 289 - const ret = arg0.altKey; 290 - return ret; 291 - }; 292 - imports.wbg.__wbg_altKey_d7495666df921121 = function(arg0) { 293 - const ret = arg0.altKey; 294 - return ret; 295 - }; 296 - imports.wbg.__wbg_animate_6ec571f163cf6f8d = function(arg0, arg1, arg2) { 297 - const ret = arg0.animate(arg1, arg2); 298 - return ret; 299 - }; 300 - imports.wbg.__wbg_appendChild_8204974b7328bf98 = function() { return handleError(function (arg0, arg1) { 301 - const ret = arg0.appendChild(arg1); 302 - return ret; 303 - }, arguments) }; 304 - imports.wbg.__wbg_blockSize_1490803190b57a34 = function(arg0) { 305 - const ret = arg0.blockSize; 306 - return ret; 307 - }; 308 - imports.wbg.__wbg_body_942ea927546a04ba = function(arg0) { 309 - const ret = arg0.body; 310 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 311 - }; 312 - imports.wbg.__wbg_brand_9562792cbb4735c3 = function(arg0, arg1) { 313 - const ret = arg1.brand; 314 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 315 - const len1 = WASM_VECTOR_LEN; 316 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 317 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 318 - }; 319 - imports.wbg.__wbg_brands_a1e7a2bce052128f = function(arg0) { 320 - const ret = arg0.brands; 321 - return ret; 322 - }; 323 - imports.wbg.__wbg_buffer_609cc3eee51ed158 = function(arg0) { 324 - const ret = arg0.buffer; 325 - return ret; 326 - }; 327 - imports.wbg.__wbg_button_f75c56aec440ea04 = function(arg0) { 328 - const ret = arg0.button; 329 - return ret; 330 - }; 331 - imports.wbg.__wbg_buttons_b6346af6f04e4686 = function(arg0) { 332 - const ret = arg0.buttons; 333 - return ret; 334 - }; 335 - imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) { 336 - const ret = arg0.call(arg1); 337 - return ret; 338 - }, arguments) }; 339 - imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) { 340 - const ret = arg0.call(arg1, arg2); 341 - return ret; 342 - }, arguments) }; 343 - imports.wbg.__wbg_cancelAnimationFrame_089b48301c362fde = function() { return handleError(function (arg0, arg1) { 344 - arg0.cancelAnimationFrame(arg1); 345 - }, arguments) }; 346 - imports.wbg.__wbg_cancelIdleCallback_669eb1ed294c8b8b = function(arg0, arg1) { 347 - arg0.cancelIdleCallback(arg1 >>> 0); 348 - }; 349 - imports.wbg.__wbg_cancel_09c394f0894744eb = function(arg0) { 350 - arg0.cancel(); 351 - }; 352 - imports.wbg.__wbg_catch_a6e601879b2610e9 = function(arg0, arg1) { 353 - const ret = arg0.catch(arg1); 354 - return ret; 355 - }; 356 - imports.wbg.__wbg_clearTimeout_b2651b7485c58446 = function(arg0, arg1) { 357 - arg0.clearTimeout(arg1); 358 - }; 359 - imports.wbg.__wbg_clientHeight_216178c194000db4 = function(arg0) { 360 - const ret = arg0.clientHeight; 361 - return ret; 362 - }; 363 - imports.wbg.__wbg_clientWidth_ce67a04dc15fce39 = function(arg0) { 364 - const ret = arg0.clientWidth; 365 - return ret; 366 - }; 367 - imports.wbg.__wbg_close_2893b7d056a0627d = function() { return handleError(function (arg0) { 368 - arg0.close(); 369 - }, arguments) }; 370 - imports.wbg.__wbg_close_414b379454494b29 = function(arg0) { 371 - arg0.close(); 372 - }; 373 - imports.wbg.__wbg_close_e1253d480ed93ce3 = function() { return handleError(function (arg0, arg1, arg2, arg3) { 374 - arg0.close(arg1, getStringFromWasm0(arg2, arg3)); 375 - }, arguments) }; 376 - imports.wbg.__wbg_code_459c120478e1ab6e = function(arg0, arg1) { 377 - const ret = arg1.code; 378 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 379 - const len1 = WASM_VECTOR_LEN; 380 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 381 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 382 - }; 383 - imports.wbg.__wbg_code_f4ec1e6e2e1b0417 = function(arg0) { 384 - const ret = arg0.code; 385 - return ret; 386 - }; 387 - imports.wbg.__wbg_contains_3361c7eda6c95afd = function(arg0, arg1) { 388 - const ret = arg0.contains(arg1); 389 - return ret; 390 - }; 391 - imports.wbg.__wbg_contentRect_81407eb60e52248f = function(arg0) { 392 - const ret = arg0.contentRect; 393 - return ret; 394 - }; 395 - imports.wbg.__wbg_createElement_8c9931a732ee2fea = function() { return handleError(function (arg0, arg1, arg2) { 396 - const ret = arg0.createElement(getStringFromWasm0(arg1, arg2)); 397 - return ret; 398 - }, arguments) }; 399 - imports.wbg.__wbg_createObjectURL_6e98d2f9c7bd9764 = function() { return handleError(function (arg0, arg1) { 400 - const ret = URL.createObjectURL(arg1); 401 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 402 - const len1 = WASM_VECTOR_LEN; 403 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 404 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 405 - }, arguments) }; 406 - imports.wbg.__wbg_crypto_574e78ad8b13b65f = function(arg0) { 407 - const ret = arg0.crypto; 408 - return ret; 409 - }; 410 - imports.wbg.__wbg_ctrlKey_1e826e468105ac11 = function(arg0) { 411 - const ret = arg0.ctrlKey; 412 - return ret; 413 - }; 414 - imports.wbg.__wbg_ctrlKey_cdbe8154dfb00d1f = function(arg0) { 415 - const ret = arg0.ctrlKey; 416 - return ret; 417 - }; 418 - imports.wbg.__wbg_data_432d9c3df2630942 = function(arg0) { 419 - const ret = arg0.data; 420 - return ret; 421 - }; 422 - imports.wbg.__wbg_deltaMode_9bfd9fe3f6b4b240 = function(arg0) { 423 - const ret = arg0.deltaMode; 424 - return ret; 425 - }; 426 - imports.wbg.__wbg_deltaX_5c1121715746e4b7 = function(arg0) { 427 - const ret = arg0.deltaX; 428 - return ret; 429 - }; 430 - imports.wbg.__wbg_deltaY_f9318542caea0c36 = function(arg0) { 431 - const ret = arg0.deltaY; 432 - return ret; 433 - }; 434 - imports.wbg.__wbg_devicePixelContentBoxSize_a6de82cb30d70825 = function(arg0) { 435 - const ret = arg0.devicePixelContentBoxSize; 436 - return ret; 437 - }; 438 - imports.wbg.__wbg_devicePixelRatio_68c391265f05d093 = function(arg0) { 439 - const ret = arg0.devicePixelRatio; 440 - return ret; 441 - }; 442 - imports.wbg.__wbg_disconnect_2118016d75479985 = function(arg0) { 443 - arg0.disconnect(); 444 - }; 445 - imports.wbg.__wbg_disconnect_ac3f4ba550970c76 = function(arg0) { 446 - arg0.disconnect(); 447 - }; 448 - imports.wbg.__wbg_document_d249400bd7bd996d = function(arg0) { 449 - const ret = arg0.document; 450 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 451 - }; 452 - imports.wbg.__wbg_error_1004b8c64097413f = function(arg0, arg1) { 453 - console.error(arg0, arg1); 454 - }; 455 - imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) { 456 - let deferred0_0; 457 - let deferred0_1; 458 - try { 459 - deferred0_0 = arg0; 460 - deferred0_1 = arg1; 461 - console.error(getStringFromWasm0(arg0, arg1)); 462 - } finally { 463 - wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); 464 - } 465 - }; 466 - imports.wbg.__wbg_focus_7d08b55eba7b368d = function() { return handleError(function (arg0) { 467 - arg0.focus(); 468 - }, arguments) }; 469 - imports.wbg.__wbg_fullscreenElement_a2f691b04c3b3de5 = function(arg0) { 470 - const ret = arg0.fullscreenElement; 471 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 472 - }; 473 - imports.wbg.__wbg_getCoalescedEvents_21492912fd0145ec = function(arg0) { 474 - const ret = arg0.getCoalescedEvents; 475 - return ret; 476 - }; 477 - imports.wbg.__wbg_getCoalescedEvents_a7d49de30111f6b8 = function(arg0) { 478 - const ret = arg0.getCoalescedEvents(); 479 - return ret; 480 - }; 481 - imports.wbg.__wbg_getComputedStyle_046dd6472f8e7f1d = function() { return handleError(function (arg0, arg1) { 482 - const ret = arg0.getComputedStyle(arg1); 483 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 484 - }, arguments) }; 485 - imports.wbg.__wbg_getContext_e9cf379449413580 = function() { return handleError(function (arg0, arg1, arg2) { 486 - const ret = arg0.getContext(getStringFromWasm0(arg1, arg2)); 487 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 488 - }, arguments) }; 489 - imports.wbg.__wbg_getOwnPropertyDescriptor_9dd936a3c0cbd368 = function(arg0, arg1) { 490 - const ret = Object.getOwnPropertyDescriptor(arg0, arg1); 491 - return ret; 492 - }; 493 - imports.wbg.__wbg_getPropertyValue_e623c23a05dfb30c = function() { return handleError(function (arg0, arg1, arg2, arg3) { 494 - const ret = arg1.getPropertyValue(getStringFromWasm0(arg2, arg3)); 495 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 496 - const len1 = WASM_VECTOR_LEN; 497 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 498 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 499 - }, arguments) }; 500 - imports.wbg.__wbg_getRandomValues_b8f5dbd5f3995a9e = function() { return handleError(function (arg0, arg1) { 501 - arg0.getRandomValues(arg1); 502 - }, arguments) }; 503 - imports.wbg.__wbg_get_67b2ba62fc30de12 = function() { return handleError(function (arg0, arg1) { 504 - const ret = Reflect.get(arg0, arg1); 505 - return ret; 506 - }, arguments) }; 507 - imports.wbg.__wbg_get_b9b93047fe3cf45b = function(arg0, arg1) { 508 - const ret = arg0[arg1 >>> 0]; 509 - return ret; 510 - }; 511 - imports.wbg.__wbg_height_1f8226c8f6875110 = function(arg0) { 512 - const ret = arg0.height; 513 - return ret; 514 - }; 515 - imports.wbg.__wbg_inlineSize_8ff96b3ec1b24423 = function(arg0) { 516 - const ret = arg0.inlineSize; 517 - return ret; 518 - }; 519 - imports.wbg.__wbg_instanceof_ArrayBuffer_e14585432e3737fc = function(arg0) { 520 - let result; 521 - try { 522 - result = arg0 instanceof ArrayBuffer; 523 - } catch (_) { 524 - result = false; 525 - } 526 - const ret = result; 527 - return ret; 528 - }; 529 - imports.wbg.__wbg_instanceof_Blob_ca721ef3bdab15d1 = function(arg0) { 530 - let result; 531 - try { 532 - result = arg0 instanceof Blob; 533 - } catch (_) { 534 - result = false; 535 - } 536 - const ret = result; 537 - return ret; 538 - }; 539 - imports.wbg.__wbg_instanceof_CanvasRenderingContext2d_df82a4d3437bf1cc = function(arg0) { 540 - let result; 541 - try { 542 - result = arg0 instanceof CanvasRenderingContext2D; 543 - } catch (_) { 544 - result = false; 545 - } 546 - const ret = result; 547 - return ret; 548 - }; 549 - imports.wbg.__wbg_instanceof_Performance_0ac1286c87171f57 = function(arg0) { 550 - let result; 551 - try { 552 - result = arg0 instanceof Performance; 553 - } catch (_) { 554 - result = false; 555 - } 556 - const ret = result; 557 - return ret; 558 - }; 559 - imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) { 560 - let result; 561 - try { 562 - result = arg0 instanceof Window; 563 - } catch (_) { 564 - result = false; 565 - } 566 - const ret = result; 567 - return ret; 568 - }; 569 - imports.wbg.__wbg_isIntersecting_e68706dac9c5f2e9 = function(arg0) { 570 - const ret = arg0.isIntersecting; 571 - return ret; 572 - }; 573 - imports.wbg.__wbg_is_c7481c65e7e5df9e = function(arg0, arg1) { 574 - const ret = Object.is(arg0, arg1); 575 - return ret; 576 - }; 577 - imports.wbg.__wbg_key_7b5c6cb539be8e13 = function(arg0, arg1) { 578 - const ret = arg1.key; 579 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 580 - const len1 = WASM_VECTOR_LEN; 581 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 582 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 583 - }; 584 - imports.wbg.__wbg_length_a446193dc22c12f8 = function(arg0) { 585 - const ret = arg0.length; 586 - return ret; 587 - }; 588 - imports.wbg.__wbg_length_e2d2a49132c1b256 = function(arg0) { 589 - const ret = arg0.length; 590 - return ret; 591 - }; 592 - imports.wbg.__wbg_location_9b435486be8f98c2 = function(arg0) { 593 - const ret = arg0.location; 594 - return ret; 595 - }; 596 - imports.wbg.__wbg_matchMedia_bf8807a841d930c1 = function() { return handleError(function (arg0, arg1, arg2) { 597 - const ret = arg0.matchMedia(getStringFromWasm0(arg1, arg2)); 598 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 599 - }, arguments) }; 600 - imports.wbg.__wbg_matches_e9ca73fbf8a3a104 = function(arg0) { 601 - const ret = arg0.matches; 602 - return ret; 603 - }; 604 - imports.wbg.__wbg_metaKey_0b25f7848e014cc8 = function(arg0) { 605 - const ret = arg0.metaKey; 606 - return ret; 607 - }; 608 - imports.wbg.__wbg_metaKey_e1dd47d709a80ce5 = function(arg0) { 609 - const ret = arg0.metaKey; 610 - return ret; 611 - }; 612 - imports.wbg.__wbg_movementX_1aa05f864931369b = function(arg0) { 613 - const ret = arg0.movementX; 614 - return ret; 615 - }; 616 - imports.wbg.__wbg_movementY_8acfedb38a70e624 = function(arg0) { 617 - const ret = arg0.movementY; 618 - return ret; 619 - }; 620 - imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) { 621 - const ret = arg0.msCrypto; 622 - return ret; 623 - }; 624 - imports.wbg.__wbg_navigator_1577371c070c8947 = function(arg0) { 625 - const ret = arg0.navigator; 626 - return ret; 627 - }; 628 - imports.wbg.__wbg_new_18b1151f3a6a9280 = function() { return handleError(function (arg0) { 629 - const ret = new IntersectionObserver(arg0); 630 - return ret; 631 - }, arguments) }; 632 - imports.wbg.__wbg_new_24b2c5b645cded8d = function() { return handleError(function () { 633 - const ret = new MessageChannel(); 634 - return ret; 635 - }, arguments) }; 636 - imports.wbg.__wbg_new_405e22f390576ce2 = function() { 637 - const ret = new Object(); 638 - return ret; 639 - }; 640 - imports.wbg.__wbg_new_5f34cc0c99fcc488 = function() { return handleError(function (arg0) { 641 - const ret = new ResizeObserver(arg0); 642 - return ret; 643 - }, arguments) }; 644 - imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { 645 - const ret = new Error(); 646 - return ret; 647 - }; 648 - imports.wbg.__wbg_new_92c54fc74574ef55 = function() { return handleError(function (arg0, arg1) { 649 - const ret = new WebSocket(getStringFromWasm0(arg0, arg1)); 650 - return ret; 651 - }, arguments) }; 652 - imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) { 653 - const ret = new Uint8Array(arg0); 654 - return ret; 655 - }; 656 - imports.wbg.__wbg_new_b1a33e5095abf678 = function() { return handleError(function (arg0, arg1) { 657 - const ret = new Worker(getStringFromWasm0(arg0, arg1)); 658 - return ret; 659 - }, arguments) }; 660 - imports.wbg.__wbg_new_e25e5aab09ff45db = function() { return handleError(function () { 661 - const ret = new AbortController(); 662 - return ret; 663 - }, arguments) }; 664 - imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) { 665 - const ret = new Function(getStringFromWasm0(arg0, arg1)); 666 - return ret; 667 - }; 668 - imports.wbg.__wbg_newwithbyteoffsetandlength_d97e637ebe145a9a = function(arg0, arg1, arg2) { 669 - const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); 670 - return ret; 671 - }; 672 - imports.wbg.__wbg_newwithlength_a381634e90c276d4 = function(arg0) { 673 - const ret = new Uint8Array(arg0 >>> 0); 674 - return ret; 675 - }; 676 - imports.wbg.__wbg_newwithstrsequenceandoptions_aaff55b467c81b63 = function() { return handleError(function (arg0, arg1) { 677 - const ret = new Blob(arg0, arg1); 678 - return ret; 679 - }, arguments) }; 680 - imports.wbg.__wbg_newwithu8clampedarray_0fcf78a036c89a97 = function() { return handleError(function (arg0, arg1, arg2) { 681 - const ret = new ImageData(getClampedArrayU8FromWasm0(arg0, arg1), arg2 >>> 0); 682 - return ret; 683 - }, arguments) }; 684 - imports.wbg.__wbg_node_905d3e251edff8a2 = function(arg0) { 685 - const ret = arg0.node; 686 - return ret; 687 - }; 688 - imports.wbg.__wbg_now_2c95c9de01293173 = function(arg0) { 689 - const ret = arg0.now(); 690 - return ret; 691 - }; 692 - imports.wbg.__wbg_now_d18023d54d4e5500 = function(arg0) { 693 - const ret = arg0.now(); 694 - return ret; 695 - }; 696 - imports.wbg.__wbg_observe_d2e7378f15f7ca72 = function(arg0, arg1) { 697 - arg0.observe(arg1); 698 - }; 699 - imports.wbg.__wbg_observe_eafddfc5a0c60e02 = function(arg0, arg1) { 700 - arg0.observe(arg1); 701 - }; 702 - imports.wbg.__wbg_observe_ed4adb1c245103c5 = function(arg0, arg1, arg2) { 703 - arg0.observe(arg1, arg2); 704 - }; 705 - imports.wbg.__wbg_of_2eaf5a02d443ef03 = function(arg0) { 706 - const ret = Array.of(arg0); 707 - return ret; 708 - }; 709 - imports.wbg.__wbg_of_66b3ee656cbd962b = function(arg0, arg1) { 710 - const ret = Array.of(arg0, arg1); 711 - return ret; 712 - }; 713 - imports.wbg.__wbg_offsetX_cb6a38e6f23cb4a6 = function(arg0) { 714 - const ret = arg0.offsetX; 715 - return ret; 716 - }; 717 - imports.wbg.__wbg_offsetY_43e21941c5c1f8bf = function(arg0) { 718 - const ret = arg0.offsetY; 719 - return ret; 720 - }; 721 - imports.wbg.__wbg_performance_7a3ffd0b17f663ad = function(arg0) { 722 - const ret = arg0.performance; 723 - return ret; 724 - }; 725 - imports.wbg.__wbg_persisted_d32ce73b8e522062 = function(arg0) { 726 - const ret = arg0.persisted; 727 - return ret; 728 - }; 729 - imports.wbg.__wbg_play_63bc12f42e16af91 = function(arg0) { 730 - arg0.play(); 731 - }; 732 - imports.wbg.__wbg_pointerId_585e63ee80a49927 = function(arg0) { 733 - const ret = arg0.pointerId; 734 - return ret; 735 - }; 736 - imports.wbg.__wbg_pointerType_6bd934aa20d9db49 = function(arg0, arg1) { 737 - const ret = arg1.pointerType; 738 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 739 - const len1 = WASM_VECTOR_LEN; 740 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 741 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 742 - }; 743 - imports.wbg.__wbg_port1_70af0ea6e4a96f9d = function(arg0) { 744 - const ret = arg0.port1; 745 - return ret; 746 - }; 747 - imports.wbg.__wbg_port2_0584c7f0938b6fe6 = function(arg0) { 748 - const ret = arg0.port2; 749 - return ret; 750 - }; 751 - imports.wbg.__wbg_postMessage_e55d059efb191dc5 = function() { return handleError(function (arg0, arg1) { 752 - arg0.postMessage(arg1); 753 - }, arguments) }; 754 - imports.wbg.__wbg_postMessage_f961e53b9731ca83 = function() { return handleError(function (arg0, arg1, arg2) { 755 - arg0.postMessage(arg1, arg2); 756 - }, arguments) }; 757 - imports.wbg.__wbg_postTask_41d93e93941e4a3d = function(arg0, arg1, arg2) { 758 - const ret = arg0.postTask(arg1, arg2); 759 - return ret; 760 - }; 761 - imports.wbg.__wbg_pressure_adda5a83a9cec94d = function(arg0) { 762 - const ret = arg0.pressure; 763 - return ret; 764 - }; 765 - imports.wbg.__wbg_preventDefault_c2314fd813c02b3c = function(arg0) { 766 - arg0.preventDefault(); 767 - }; 768 - imports.wbg.__wbg_process_dc0fbacc7c1c06f7 = function(arg0) { 769 - const ret = arg0.process; 770 - return ret; 771 - }; 772 - imports.wbg.__wbg_prototype_c28bca39c45aba9b = function() { 773 - const ret = ResizeObserverEntry.prototype; 774 - return ret; 775 - }; 776 - imports.wbg.__wbg_putImageData_2acd1dcd88a80f79 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { 777 - arg0.putImageData(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 778 - }, arguments) }; 779 - imports.wbg.__wbg_putImageData_416b1a6d50843b66 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { 780 - arg0.putImageData(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 781 - }, arguments) }; 782 - imports.wbg.__wbg_queueMicrotask_65a6c48ee9790d40 = function(arg0, arg1) { 783 - arg0.queueMicrotask(arg1); 784 - }; 785 - imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) { 786 - queueMicrotask(arg0); 787 - }; 788 - imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) { 789 - const ret = arg0.queueMicrotask; 790 - return ret; 791 - }; 792 - imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) { 793 - arg0.randomFillSync(arg1); 794 - }, arguments) }; 795 - imports.wbg.__wbg_readyState_7ef6e63c349899ed = function(arg0) { 796 - const ret = arg0.readyState; 797 - return ret; 798 - }; 799 - imports.wbg.__wbg_reason_49f1cede8bcf23dd = function(arg0, arg1) { 800 - const ret = arg1.reason; 801 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 802 - const len1 = WASM_VECTOR_LEN; 803 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 804 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 805 - }; 806 - imports.wbg.__wbg_removeEventListener_056dfe8c3d6c58f9 = function() { return handleError(function (arg0, arg1, arg2, arg3) { 807 - arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3); 808 - }, arguments) }; 809 - imports.wbg.__wbg_removeListener_e55db581b73ccf65 = function() { return handleError(function (arg0, arg1) { 810 - arg0.removeListener(arg1); 811 - }, arguments) }; 812 - imports.wbg.__wbg_removeProperty_0e85471f4dfc00ae = function() { return handleError(function (arg0, arg1, arg2, arg3) { 813 - const ret = arg1.removeProperty(getStringFromWasm0(arg2, arg3)); 814 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 815 - const len1 = WASM_VECTOR_LEN; 816 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 817 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 818 - }, arguments) }; 819 - imports.wbg.__wbg_repeat_1882aa0d0072c705 = function(arg0) { 820 - const ret = arg0.repeat; 821 - return ret; 822 - }; 823 - imports.wbg.__wbg_requestAnimationFrame_d7fd890aaefc3246 = function() { return handleError(function (arg0, arg1) { 824 - const ret = arg0.requestAnimationFrame(arg1); 825 - return ret; 826 - }, arguments) }; 827 - imports.wbg.__wbg_requestFullscreen_86fc6cdb76000482 = function(arg0) { 828 - const ret = arg0.requestFullscreen; 829 - return ret; 830 - }; 831 - imports.wbg.__wbg_requestFullscreen_9f0611438eb929cf = function(arg0) { 832 - const ret = arg0.requestFullscreen(); 833 - return ret; 834 - }; 835 - imports.wbg.__wbg_requestIdleCallback_1b8d644ff564208f = function(arg0) { 836 - const ret = arg0.requestIdleCallback; 837 - return ret; 838 - }; 839 - imports.wbg.__wbg_requestIdleCallback_e3eefd34962470e1 = function() { return handleError(function (arg0, arg1) { 840 - const ret = arg0.requestIdleCallback(arg1); 841 - return ret; 842 - }, arguments) }; 843 - imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () { 844 - const ret = module.require; 845 - return ret; 846 - }, arguments) }; 847 - imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) { 848 - const ret = Promise.resolve(arg0); 849 - return ret; 850 - }; 851 - imports.wbg.__wbg_revokeObjectURL_27267efebeb457c7 = function() { return handleError(function (arg0, arg1) { 852 - URL.revokeObjectURL(getStringFromWasm0(arg0, arg1)); 853 - }, arguments) }; 854 - imports.wbg.__wbg_scheduler_48482a9974eeacbd = function(arg0) { 855 - const ret = arg0.scheduler; 856 - return ret; 857 - }; 858 - imports.wbg.__wbg_scheduler_5156bb61cc1cf589 = function(arg0) { 859 - const ret = arg0.scheduler; 860 - return ret; 861 - }; 862 - imports.wbg.__wbg_send_0293179ba074ffb4 = function() { return handleError(function (arg0, arg1, arg2) { 863 - arg0.send(getStringFromWasm0(arg1, arg2)); 864 - }, arguments) }; 865 - imports.wbg.__wbg_send_fc0c204e8a1757f4 = function() { return handleError(function (arg0, arg1, arg2) { 866 - arg0.send(getArrayU8FromWasm0(arg1, arg2)); 867 - }, arguments) }; 868 - imports.wbg.__wbg_setAttribute_2704501201f15687 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 869 - arg0.setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 870 - }, arguments) }; 871 - imports.wbg.__wbg_setPointerCapture_c04dafaf4d00ffad = function() { return handleError(function (arg0, arg1) { 872 - arg0.setPointerCapture(arg1); 873 - }, arguments) }; 874 - imports.wbg.__wbg_setProperty_f2cf326652b9a713 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 875 - arg0.setProperty(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 876 - }, arguments) }; 877 - imports.wbg.__wbg_setTimeout_461fec76662b35ea = function() { return handleError(function (arg0, arg1) { 878 - const ret = arg0.setTimeout(arg1); 879 - return ret; 880 - }, arguments) }; 881 - imports.wbg.__wbg_setTimeout_f2fe5af8e3debeb3 = function() { return handleError(function (arg0, arg1, arg2) { 882 - const ret = arg0.setTimeout(arg1, arg2); 883 - return ret; 884 - }, arguments) }; 885 - imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) { 886 - arg0.set(arg1, arg2 >>> 0); 887 - }; 888 - imports.wbg.__wbg_set_bb8cecf6a62b9f46 = function() { return handleError(function (arg0, arg1, arg2) { 889 - const ret = Reflect.set(arg0, arg1, arg2); 890 - return ret; 891 - }, arguments) }; 892 - imports.wbg.__wbg_setbinaryType_92fa1ffd873b327c = function(arg0, arg1) { 893 - arg0.binaryType = __wbindgen_enum_BinaryType[arg1]; 894 - }; 895 - imports.wbg.__wbg_setbox_2786f3ccea97cac4 = function(arg0, arg1) { 896 - arg0.box = __wbindgen_enum_ResizeObserverBoxOptions[arg1]; 897 - }; 898 - imports.wbg.__wbg_setheight_433680330c9420c3 = function(arg0, arg1) { 899 - arg0.height = arg1 >>> 0; 900 - }; 901 - imports.wbg.__wbg_setheight_da683a33fa99843c = function(arg0, arg1) { 902 - arg0.height = arg1 >>> 0; 903 - }; 904 - imports.wbg.__wbg_setonclose_14fc475a49d488fc = function(arg0, arg1) { 905 - arg0.onclose = arg1; 906 - }; 907 - imports.wbg.__wbg_setonerror_8639efe354b947cd = function(arg0, arg1) { 908 - arg0.onerror = arg1; 909 - }; 910 - imports.wbg.__wbg_setonmessage_23d122da701b8ddb = function(arg0, arg1) { 911 - arg0.onmessage = arg1; 912 - }; 913 - imports.wbg.__wbg_setonmessage_6eccab530a8fb4c7 = function(arg0, arg1) { 914 - arg0.onmessage = arg1; 915 - }; 916 - imports.wbg.__wbg_setonopen_2da654e1f39745d5 = function(arg0, arg1) { 917 - arg0.onopen = arg1; 918 - }; 919 - imports.wbg.__wbg_settype_39ed370d3edd403c = function(arg0, arg1, arg2) { 920 - arg0.type = getStringFromWasm0(arg1, arg2); 921 - }; 922 - imports.wbg.__wbg_setwidth_660ca581e3fbe279 = function(arg0, arg1) { 923 - arg0.width = arg1 >>> 0; 924 - }; 925 - imports.wbg.__wbg_setwidth_c5fed9f5e7f0b406 = function(arg0, arg1) { 926 - arg0.width = arg1 >>> 0; 927 - }; 928 - imports.wbg.__wbg_shiftKey_2bebb3b703254f47 = function(arg0) { 929 - const ret = arg0.shiftKey; 930 - return ret; 931 - }; 932 - imports.wbg.__wbg_shiftKey_86e737105bab1a54 = function(arg0) { 933 - const ret = arg0.shiftKey; 934 - return ret; 935 - }; 936 - imports.wbg.__wbg_signal_aaf9ad74119f20a4 = function(arg0) { 937 - const ret = arg0.signal; 938 - return ret; 939 - }; 940 - imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) { 941 - const ret = arg1.stack; 942 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 943 - const len1 = WASM_VECTOR_LEN; 944 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 945 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 946 - }; 947 - imports.wbg.__wbg_start_2c099369ce831bf1 = function(arg0) { 948 - arg0.start(); 949 - }; 950 - imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { 951 - const ret = typeof global === 'undefined' ? null : global; 952 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 953 - }; 954 - imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { 955 - const ret = typeof globalThis === 'undefined' ? null : globalThis; 956 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 957 - }; 958 - imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { 959 - const ret = typeof self === 'undefined' ? null : self; 960 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 961 - }; 962 - imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { 963 - const ret = typeof window === 'undefined' ? null : window; 964 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 965 - }; 966 - imports.wbg.__wbg_style_fb30c14e5815805c = function(arg0) { 967 - const ret = arg0.style; 968 - return ret; 969 - }; 970 - imports.wbg.__wbg_subarray_aa9065fa9dc5df96 = function(arg0, arg1, arg2) { 971 - const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0); 972 - return ret; 973 - }; 974 - imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) { 975 - const ret = arg0.then(arg1); 976 - return ret; 977 - }; 978 - imports.wbg.__wbg_unobserve_02f53d1ca2d1d801 = function(arg0, arg1) { 979 - arg0.unobserve(arg1); 980 - }; 981 - imports.wbg.__wbg_userAgentData_f7b0e61c05c54315 = function(arg0) { 982 - const ret = arg0.userAgentData; 983 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 984 - }; 985 - imports.wbg.__wbg_userAgent_12e9d8e62297563f = function() { return handleError(function (arg0, arg1) { 986 - const ret = arg1.userAgent; 987 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 988 - const len1 = WASM_VECTOR_LEN; 989 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 990 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 991 - }, arguments) }; 992 - imports.wbg.__wbg_versions_c01dfd4722a88165 = function(arg0) { 993 - const ret = arg0.versions; 994 - return ret; 995 - }; 996 - imports.wbg.__wbg_visibilityState_f3cc18a6f3831137 = function(arg0) { 997 - const ret = arg0.visibilityState; 998 - return (__wbindgen_enum_VisibilityState.indexOf(ret) + 1 || 3) - 1; 999 - }; 1000 - imports.wbg.__wbg_webkitFullscreenElement_a9ca38b7214d1567 = function(arg0) { 1001 - const ret = arg0.webkitFullscreenElement; 1002 - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); 1003 - }; 1004 - imports.wbg.__wbg_webkitRequestFullscreen_23664c63833ff0e5 = function(arg0) { 1005 - arg0.webkitRequestFullscreen(); 1006 - }; 1007 - imports.wbg.__wbg_width_cdaf02311c1621d1 = function(arg0) { 1008 - const ret = arg0.width; 1009 - return ret; 1010 - }; 1011 - imports.wbg.__wbindgen_cb_drop = function(arg0) { 1012 - const obj = arg0.original; 1013 - if (obj.cnt-- == 1) { 1014 - obj.a = 0; 1015 - return true; 1016 - } 1017 - const ret = false; 1018 - return ret; 1019 - }; 1020 - imports.wbg.__wbindgen_closure_wrapper1511 = function(arg0, arg1, arg2) { 1021 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1022 - return ret; 1023 - }; 1024 - imports.wbg.__wbindgen_closure_wrapper207 = function(arg0, arg1, arg2) { 1025 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1026 - return ret; 1027 - }; 1028 - imports.wbg.__wbindgen_closure_wrapper209 = function(arg0, arg1, arg2) { 1029 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1030 - return ret; 1031 - }; 1032 - imports.wbg.__wbindgen_closure_wrapper211 = function(arg0, arg1, arg2) { 1033 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1034 - return ret; 1035 - }; 1036 - imports.wbg.__wbindgen_closure_wrapper2187 = function(arg0, arg1, arg2) { 1037 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1038 - return ret; 1039 - }; 1040 - imports.wbg.__wbindgen_closure_wrapper2189 = function(arg0, arg1, arg2) { 1041 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_51); 1042 - return ret; 1043 - }; 1044 - imports.wbg.__wbindgen_closure_wrapper2194 = function(arg0, arg1, arg2) { 1045 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1046 - return ret; 1047 - }; 1048 - imports.wbg.__wbindgen_closure_wrapper2197 = function(arg0, arg1, arg2) { 1049 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_56); 1050 - return ret; 1051 - }; 1052 - imports.wbg.__wbindgen_closure_wrapper451 = function(arg0, arg1, arg2) { 1053 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1054 - return ret; 1055 - }; 1056 - imports.wbg.__wbindgen_closure_wrapper477 = function(arg0, arg1, arg2) { 1057 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1058 - return ret; 1059 - }; 1060 - imports.wbg.__wbindgen_closure_wrapper479 = function(arg0, arg1, arg2) { 1061 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1062 - return ret; 1063 - }; 1064 - imports.wbg.__wbindgen_closure_wrapper481 = function(arg0, arg1, arg2) { 1065 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1066 - return ret; 1067 - }; 1068 - imports.wbg.__wbindgen_closure_wrapper482 = function(arg0, arg1, arg2) { 1069 - const ret = makeMutClosure(arg0, arg1, 4, __wbg_adapter_30); 1070 - return ret; 1071 - }; 1072 - imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { 1073 - const ret = debugString(arg1); 1074 - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 1075 - const len1 = WASM_VECTOR_LEN; 1076 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 1077 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 1078 - }; 1079 - imports.wbg.__wbindgen_init_externref_table = function() { 1080 - const table = wasm.__wbindgen_export_1; 1081 - const offset = table.grow(4); 1082 - table.set(0, undefined); 1083 - table.set(offset + 0, undefined); 1084 - table.set(offset + 1, null); 1085 - table.set(offset + 2, true); 1086 - table.set(offset + 3, false); 1087 - ; 1088 - }; 1089 - imports.wbg.__wbindgen_is_function = function(arg0) { 1090 - const ret = typeof(arg0) === 'function'; 1091 - return ret; 1092 - }; 1093 - imports.wbg.__wbindgen_is_object = function(arg0) { 1094 - const val = arg0; 1095 - const ret = typeof(val) === 'object' && val !== null; 1096 - return ret; 1097 - }; 1098 - imports.wbg.__wbindgen_is_string = function(arg0) { 1099 - const ret = typeof(arg0) === 'string'; 1100 - return ret; 1101 - }; 1102 - imports.wbg.__wbindgen_is_undefined = function(arg0) { 1103 - const ret = arg0 === undefined; 1104 - return ret; 1105 - }; 1106 - imports.wbg.__wbindgen_memory = function() { 1107 - const ret = wasm.memory; 1108 - return ret; 1109 - }; 1110 - imports.wbg.__wbindgen_number_new = function(arg0) { 1111 - const ret = arg0; 1112 - return ret; 1113 - }; 1114 - imports.wbg.__wbindgen_string_get = function(arg0, arg1) { 1115 - const obj = arg1; 1116 - const ret = typeof(obj) === 'string' ? obj : undefined; 1117 - var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 1118 - var len1 = WASM_VECTOR_LEN; 1119 - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); 1120 - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); 1121 - }; 1122 - imports.wbg.__wbindgen_string_new = function(arg0, arg1) { 1123 - const ret = getStringFromWasm0(arg0, arg1); 1124 - return ret; 1125 - }; 1126 - imports.wbg.__wbindgen_throw = function(arg0, arg1) { 1127 - throw new Error(getStringFromWasm0(arg0, arg1)); 1128 - }; 1129 - 1130 - return imports; 1131 - } 1132 - 1133 - function __wbg_init_memory(imports, memory) { 1134 - 1135 - } 1136 - 1137 - function __wbg_finalize_init(instance, module) { 1138 - wasm = instance.exports; 1139 - __wbg_init.__wbindgen_wasm_module = module; 1140 - cachedDataViewMemory0 = null; 1141 - cachedUint8ArrayMemory0 = null; 1142 - cachedUint8ClampedArrayMemory0 = null; 1143 - 1144 - 1145 - wasm.__wbindgen_start(); 1146 - return wasm; 1147 - } 1148 - 1149 - function initSync(module) { 1150 - if (wasm !== undefined) return wasm; 1151 - 1152 - 1153 - if (typeof module !== 'undefined') { 1154 - if (Object.getPrototypeOf(module) === Object.prototype) { 1155 - ({module} = module) 1156 - } else { 1157 - console.warn('using deprecated parameters for `initSync()`; pass a single object instead') 1158 - } 1159 - } 1160 - 1161 - const imports = __wbg_get_imports(); 1162 - 1163 - __wbg_init_memory(imports); 1164 - 1165 - if (!(module instanceof WebAssembly.Module)) { 1166 - module = new WebAssembly.Module(module); 1167 - } 1168 - 1169 - const instance = new WebAssembly.Instance(module, imports); 1170 - 1171 - return __wbg_finalize_init(instance, module); 1172 - } 1173 - 1174 - async function __wbg_init(module_or_path) { 1175 - if (wasm !== undefined) return wasm; 1176 - 1177 - 1178 - if (typeof module_or_path !== 'undefined') { 1179 - if (Object.getPrototypeOf(module_or_path) === Object.prototype) { 1180 - ({module_or_path} = module_or_path) 1181 - } else { 1182 - console.warn('using deprecated parameters for the initialization function; pass a single object instead') 1183 - } 1184 - } 1185 - 1186 - if (typeof module_or_path === 'undefined') { 1187 - module_or_path = new URL('annoyance-meow_bg.wasm', import.meta.url); 1188 - } 1189 - const imports = __wbg_get_imports(); 1190 - 1191 - if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { 1192 - module_or_path = fetch(module_or_path); 1193 - } 1194 - 1195 - __wbg_init_memory(imports); 1196 - 1197 - const { instance, module } = await __wbg_load(await module_or_path, imports); 1198 - 1199 - return __wbg_finalize_init(instance, module); 1200 - } 1201 - 1202 - export { initSync }; 1203 - export default __wbg_init;
static/annoy/annoyance-meow_bg.wasm

This is a binary file and will not be displayed.

static/banners/1.gif

This is a binary file and will not be displayed.

static/banners/10.gif

This is a binary file and will not be displayed.

static/banners/11.gif

This is a binary file and will not be displayed.

static/banners/12.gif

This is a binary file and will not be displayed.

static/banners/13.gif

This is a binary file and will not be displayed.

static/banners/14.gif

This is a binary file and will not be displayed.

static/banners/15.gif

This is a binary file and will not be displayed.

static/banners/16.gif

This is a binary file and will not be displayed.

static/banners/17.gif

This is a binary file and will not be displayed.

static/banners/18.gif

This is a binary file and will not be displayed.

static/banners/19.gif

This is a binary file and will not be displayed.

static/banners/2.gif

This is a binary file and will not be displayed.

static/banners/20.gif

This is a binary file and will not be displayed.

static/banners/3.gif

This is a binary file and will not be displayed.

static/banners/4.gif

This is a binary file and will not be displayed.

static/banners/5.gif

This is a binary file and will not be displayed.

static/banners/6.gif

This is a binary file and will not be displayed.

static/banners/7.gif

This is a binary file and will not be displayed.

static/banners/8.gif

This is a binary file and will not be displayed.

static/banners/9.gif

This is a binary file and will not be displayed.

static/eyes/closed.webp

This is a binary file and will not be displayed.

static/eyes/normal_forward.webp

This is a binary file and will not be displayed.

static/eyes/normal_left.webp

This is a binary file and will not be displayed.

static/eyes/normal_right.webp

This is a binary file and will not be displayed.

static/fonts/dollmonoopt.woff2

This is a binary file and will not be displayed.

static/fonts/fusion-pixel-sc-monospaced.woff2

This is a binary file and will not be displayed.

static/fonts/fusion-pixel-sc-proportional.woff2

This is a binary file and will not be displayed.

static/icons/about.webp

This is a binary file and will not be displayed.

static/icons/cd_audio.webp

This is a binary file and will not be displayed.

static/icons/contact.webp

This is a binary file and will not be displayed.

static/icons/entries.webp

This is a binary file and will not be displayed.

static/icons/entry.webp

This is a binary file and will not be displayed.

static/icons/gaze.webp

This is a binary file and will not be displayed.

static/icons/gaze_closed.webp

This is a binary file and will not be displayed.

static/icons/gaze_site.webp

This is a binary file and will not be displayed.

static/icons/guestbook.webp

This is a binary file and will not be displayed.

static/icons/home.webp

This is a binary file and will not be displayed.

static/icons/msg_information.webp

This is a binary file and will not be displayed.

static/icons/msn.webp

This is a binary file and will not be displayed.

static/icons/question.webp

This is a binary file and will not be displayed.

static/icons/warning.webp

This is a binary file and will not be displayed.

static/others/250kb.webp

This is a binary file and will not be displayed.

static/others/aph.gif

This is a binary file and will not be displayed.

static/others/dbd.gif

This is a binary file and will not be displayed.

static/others/dd86k.gif

This is a binary file and will not be displayed.

static/others/desktopwebp.webp

This is a binary file and will not be displayed.

static/others/drewsh.gif

This is a binary file and will not be displayed.

static/others/godot.gif

This is a binary file and will not be displayed.

static/others/it.webp

This is a binary file and will not be displayed.

static/others/killfascists.webp

This is a binary file and will not be displayed.

static/others/moonlightnow.gif

This is a binary file and will not be displayed.

static/others/notaperson.webp

This is a binary file and will not be displayed.

static/others/poweredbynixos.webp

This is a binary file and will not be displayed.

static/others/skylar.gif

This is a binary file and will not be displayed.

static/others/slonk.gif

This is a binary file and will not be displayed.

static/pet/idle.webp

This is a binary file and will not be displayed.

static/pet/pick.webp

This is a binary file and will not be displayed.

static/pet/walk1.webp

This is a binary file and will not be displayed.

static/pet/walk2.webp

This is a binary file and will not be displayed.

static/pfp-iojkqpwerojnasduijf.webp

This is a binary file and will not be displayed.

static/resume.pdf

This is a binary file and will not be displayed.

static/wavey.gif

This is a binary file and will not be displayed.

-62
svelte.config.js
··· 1 - import adapter from '@sveltejs/adapter-node'; 2 - import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 - 4 - import { mdsvex } from 'mdsvex'; 5 - 6 - import * as toml from '@std/toml'; 7 - import { resolve } from 'node:path'; 8 - 9 - /** @type {import('@sveltejs/kit').Config} */ 10 - const config = { 11 - extensions: ['.svelte', '.md', '.svx'], 12 - 13 - preprocess: [ 14 - vitePreprocess(), 15 - mdsvex({ 16 - extensions: ['.md', '.svx'], 17 - frontmatter: { 18 - type: 'toml', 19 - marker: '+', 20 - parse(frontmatter, messages) { 21 - try { 22 - return toml.parse(frontmatter); 23 - } catch (e) { 24 - messages.push( 25 - 'Parsing error on line ' + e.line + ', column ' + e.column + ': ' + e.message 26 - ); 27 - } 28 - } 29 - }, 30 - smartypants: { 31 - dashes: 'oldschool', 32 - quotes: true, 33 - ellipses: true, 34 - backticks: false 35 - }, 36 - layout: { 37 - about: resolve('src/routes/(site)/about/_layout.svelte'), 38 - blogpost: resolve('src/routes/(site)/entries/_layout.svelte'), 39 - simple: resolve('src/components/_window_layout.svelte') 40 - } 41 - }) 42 - ], 43 - 44 - kit: { 45 - csrf: { 46 - checkOrigin: false, 47 - trustedOrigins: ["https://gaze.systems", "https://ptr.pet", "https://poor.dog"] 48 - }, 49 - prerender: { 50 - handleHttpError: 'warn' 51 - }, 52 - adapter: adapter({ 53 - precompress: true 54 - }), 55 - alias: { 56 - $components: 'src/components', 57 - $styles: 'src/styles' 58 - } 59 - } 60 - }; 61 - 62 - export default config;
-58
tailwind.config.js
··· 1 - import typography from '@tailwindcss/typography'; 2 - import forms from '@tailwindcss/forms'; 3 - 4 - /** @type {import('tailwindcss').Config} */ 5 - export default { 6 - content: ['./src/**/*.{html,js,svelte,ts,md}'], 7 - theme: { 8 - extend: { 9 - typography: ({ theme }) => ({ 10 - ralsei: { 11 - css: { 12 - '--tw-prose-body': theme('colors.ralsei.white'), 13 - '--tw-prose-headings': theme('colors.ralsei.pink.neon'), 14 - '--tw-prose-lead': theme('colors.ralsei.white'), 15 - '--tw-prose-links': theme('colors.ralsei.green.light'), 16 - '--tw-prose-bold': theme('colors.ralsei.white'), 17 - '--tw-prose-counters': theme('colors.ralsei.pink.regular'), 18 - '--tw-prose-bullets': theme('colors.ralsei.pink.regular'), 19 - '--tw-prose-hr': theme('colors.ralsei.white'), 20 - '--tw-prose-quotes': theme('colors.ralsei.white'), 21 - '--tw-prose-quote-borders': theme('colors.ralsei.white'), 22 - '--tw-prose-captions': theme('colors.ralsei.white'), 23 - '--tw-prose-code': theme('colors.ralsei.pink.regular'), 24 - '--tw-prose-pre-code': theme('colors.ralsei.white'), 25 - '--tw-prose-pre-bg': theme('colors.ralsei.green.dark'), 26 - '--tw-prose-th-borders': theme('colors.ralsei.white'), 27 - '--tw-prose-td-borders': theme('colors.ralsei.white') 28 - } 29 - } 30 - }), 31 - animation: { 32 - 'bounce-slow': 'bounce 3s infinite', 33 - 'bounce-fast': 'bounce 0.5s infinite', 34 - 'pulse-fast': 'pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite', 35 - blink: 'blink 1s step-start infinite' 36 - }, 37 - colors: { 38 - ralsei: { 39 - pink: { 40 - regular: '#fe96e0', 41 - neon: '#ff3eb7' 42 - }, 43 - white: '#fff9fe', 44 - black: '#000801', 45 - green: { 46 - light: '#4dcc8e', 47 - dark: '#162d26' 48 - } 49 - } 50 - } 51 - }, 52 - fontFamily: { 53 - 'sans-serif': ['"Fusion Pixel 10px Proportional zh_hans", sans-serif'], 54 - monospace: ['"Fusion Pixel 10px Monospaced zh_hans", monospace'] 55 - } 56 - }, 57 - plugins: [typography, forms] 58 - };
-24
tsconfig.json
··· 1 - { 2 - "extends": "./.svelte-kit/tsconfig.json", 3 - "compilerOptions": { 4 - "allowJs": true, 5 - "checkJs": true, 6 - "esModuleInterop": true, 7 - "forceConsistentCasingInFileNames": true, 8 - "resolveJsonModule": true, 9 - "skipLibCheck": true, 10 - "sourceMap": true, 11 - "strict": true, 12 - "plugins": [ 13 - { 14 - "name": "typescript-svelte-plugin", 15 - "enabled": true 16 - } 17 - ] 18 - } 19 - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 20 - // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files 21 - // 22 - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 23 - // from the referenced tsconfig.json - TypeScript does not merge them in 24 - }
-6
vite.config.ts
··· 1 - import { sveltekit } from '@sveltejs/kit/vite'; 2 - import { defineConfig } from 'vite'; 3 - 4 - export default defineConfig({ 5 - plugins: [sveltekit()] 6 - });