Compare changes

Choose any two refs to compare.

+1
.gitignore
··· 22 22 *.njsproj 23 23 *.sln 24 24 *.sw? 25 + .wrangler
+5 -5
.tangled/workflows/deploy.yml
··· 5 5 6 6 dependencies: 7 7 nixpkgs: 8 - - nodejs 9 - 8 + - bun 10 9 steps: 11 10 - name: build site 12 11 command: | 13 - nix develop --command bash -c 'vite build' 14 - - name: deploy 12 + bun i && bun run build 13 + 14 + - name: deployw 15 15 command: | 16 - npx --yes wrangler pages deploy --branch main --project-name plcbundle-watch ./dist/ 16 + bunx wrangler pages deploy --branch main --project-name plcbundle-watch ./dist/
+5 -1
README.md
··· 1 - # plcbundle-watch 1 + # plcbundle-watch 2 + 3 + More about plcbundle: https://tangled.org/atscan.net/plcbundle/ 4 + 5 + https://plcbundle-watch.pages.dev
+389
bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "workspaces": { 4 + "": { 5 + "name": "plcbundle-watch", 6 + "dependencies": { 7 + "@tailwindcss/vite": "^4.1.16", 8 + "date-fns": "^4.1.0", 9 + "filesize": "^11.0.13", 10 + "lodash": "^4.17.21", 11 + "numeral": "^2.0.6", 12 + "tailwindcss": "^4.1.16", 13 + }, 14 + "devDependencies": { 15 + "@skeletonlabs/skeleton": "^4.2.2", 16 + "@skeletonlabs/skeleton-svelte": "^4.2.2", 17 + "@sveltejs/vite-plugin-svelte": "^6.2.1", 18 + "@tsconfig/svelte": "^5.0.5", 19 + "@types/lodash": "^4.17.20", 20 + "@types/node": "^24.6.0", 21 + "svelte": "^5.39.6", 22 + "svelte-check": "^4.3.2", 23 + "typescript": "~5.9.3", 24 + "vite": "^7.1.7", 25 + }, 26 + }, 27 + }, 28 + "packages": { 29 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], 30 + 31 + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], 32 + 33 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], 34 + 35 + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], 36 + 37 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], 38 + 39 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], 40 + 41 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], 42 + 43 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], 44 + 45 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], 46 + 47 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], 48 + 49 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], 50 + 51 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], 52 + 53 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], 54 + 55 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], 56 + 57 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], 58 + 59 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], 60 + 61 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], 62 + 63 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], 64 + 65 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], 66 + 67 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], 68 + 69 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], 70 + 71 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], 72 + 73 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], 74 + 75 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], 76 + 77 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], 78 + 79 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], 80 + 81 + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], 82 + 83 + "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "1.7.3", "@floating-ui/utils": "0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], 84 + 85 + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], 86 + 87 + "@internationalized/date": ["@internationalized/date@3.10.0", "", { "dependencies": { "@swc/helpers": "0.5.17" } }, "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw=="], 88 + 89 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 90 + 91 + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "0.3.13", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 92 + 93 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 94 + 95 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 96 + 97 + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 98 + 99 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="], 100 + 101 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="], 102 + 103 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="], 104 + 105 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="], 106 + 107 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="], 108 + 109 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="], 110 + 111 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="], 112 + 113 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="], 114 + 115 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="], 116 + 117 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="], 118 + 119 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="], 120 + 121 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="], 122 + 123 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="], 124 + 125 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="], 126 + 127 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="], 128 + 129 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="], 130 + 131 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="], 132 + 133 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="], 134 + 135 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="], 136 + 137 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="], 138 + 139 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="], 140 + 141 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="], 142 + 143 + "@skeletonlabs/skeleton": ["@skeletonlabs/skeleton@4.2.2", "", { "peerDependencies": { "tailwindcss": "4.1.16" } }, "sha512-1sqP70RE0yfpKCEFU5mFNm7jH1Of3+C6J2SZWXiwr+wJvyCTyuJGE7yalFGv0ePXZWnFyF7t9u8NCoWc2aU+9Q=="], 144 + 145 + "@skeletonlabs/skeleton-common": ["@skeletonlabs/skeleton-common@4.2.2", "", {}, "sha512-zd/wHR4UJgxvkw4JIBHxnRC7xZw1WH1zAEjVKcx271dwpfLIcysN1cBH0h2NypnhwNrAqrlJlLnDoqyZvUwCKw=="], 146 + 147 + "@skeletonlabs/skeleton-svelte": ["@skeletonlabs/skeleton-svelte@4.2.2", "", { "dependencies": { "@internationalized/date": "3.10.0", "@skeletonlabs/skeleton-common": "4.2.2", "@zag-js/accordion": "1.26.4", "@zag-js/avatar": "1.26.4", "@zag-js/collapsible": "1.26.4", "@zag-js/collection": "1.26.4", "@zag-js/combobox": "1.26.4", "@zag-js/date-picker": "1.26.4", "@zag-js/dialog": "1.26.4", "@zag-js/file-upload": "1.26.4", "@zag-js/listbox": "1.26.4", "@zag-js/pagination": "1.26.4", "@zag-js/popover": "1.26.4", "@zag-js/progress": "1.26.4", "@zag-js/radio-group": "1.26.4", "@zag-js/rating-group": "1.26.4", "@zag-js/slider": "1.26.4", "@zag-js/svelte": "1.26.4", "@zag-js/switch": "1.26.4", "@zag-js/tabs": "1.26.4", "@zag-js/tags-input": "1.26.4", "@zag-js/toast": "1.26.4", "@zag-js/toggle-group": "1.26.4", "@zag-js/tooltip": "1.26.4", "@zag-js/tree-view": "1.26.4" }, "peerDependencies": { "svelte": "5.43.2" } }, "sha512-9lsV+MzUK6B5ZBkoek6QSm/JCrWq1VYtnVWrQNqsBEmKwPP+9E7x13YydOIMeeKKUWfrXXg5QTcdqQ4p5Dc2SA=="], 148 + 149 + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "8.15.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], 150 + 151 + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "5.0.1", "debug": "4.4.3", "deepmerge": "4.3.1", "magic-string": "0.30.21", "vitefu": "1.1.1" }, "peerDependencies": { "svelte": "5.43.2", "vite": "7.1.12" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], 152 + 153 + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "4.4.3" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "6.2.1", "svelte": "5.43.2", "vite": "7.1.12" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], 154 + 155 + "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], 156 + 157 + "@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "2.3.5", "enhanced-resolve": "5.18.3", "jiti": "2.6.1", "lightningcss": "1.30.2", "magic-string": "0.30.21", "source-map-js": "1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="], 158 + 159 + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="], 160 + 161 + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="], 162 + 163 + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="], 164 + 165 + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="], 166 + 167 + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="], 168 + 169 + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="], 170 + 171 + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="], 172 + 173 + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="], 174 + 175 + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="], 176 + 177 + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="], 178 + 179 + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="], 180 + 181 + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="], 182 + 183 + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="], 184 + 185 + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.16", "", { "dependencies": { "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "tailwindcss": "4.1.16" }, "peerDependencies": { "vite": "7.1.12" } }, "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg=="], 186 + 187 + "@tsconfig/svelte": ["@tsconfig/svelte@5.0.5", "", {}, "sha512-48fAnUjKye38FvMiNOj0J9I/4XlQQiZlpe9xaNPfe8vy2Y1hFBt8g1yqf2EGjVvHavo4jf2lC+TQyENCr4BJBQ=="], 188 + 189 + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 190 + 191 + "@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="], 192 + 193 + "@types/node": ["@types/node@24.9.2", "", { "dependencies": { "undici-types": "7.16.0" } }, "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA=="], 194 + 195 + "@zag-js/accordion": ["@zag-js/accordion@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-gjKu1i3Z3fGzrGazMDHskbgdqj/DtHw9ZOW8L9X/G37CNkIe4EQyy1GbzyM91gYncSFNShquBawxuaGK3VvSdw=="], 196 + 197 + "@zag-js/anatomy": ["@zag-js/anatomy@1.26.4", "", {}, "sha512-O7kedQsC/aQ+gkNo6y8ZcB0VFmQNihOMzXeglEWJdashGn5CO0xwYkLioFwfF5+u+QQtieVjof8VAc8QT41QfQ=="], 198 + 199 + "@zag-js/aria-hidden": ["@zag-js/aria-hidden@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4" } }, "sha512-B2nPb45uc7DpPTrR415MPEVyIgGKf76bEt9ongS0bdbfUwJ8vnaIYeWK5wNh6jkbwHqca0e4vIwGzzE2S7cF6g=="], 200 + 201 + "@zag-js/auto-resize": ["@zag-js/auto-resize@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4" } }, "sha512-zDkcfi18XWCOEWUTcONZWWZgsqEkm2nzjGFQ5uWf4UeF97ofsZ5pTc60Vzk7raB787vh4j3YxCPjVaeq3iedvg=="], 202 + 203 + "@zag-js/avatar": ["@zag-js/avatar@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-ZTv5uJGdLkQDm5UB8vz8NLPtFW9DbOES36vIWuA2+/KuMnwISIX3JeW3fEhn4cFF8e/o7B9/NR7QXTFjjKwCoA=="], 204 + 205 + "@zag-js/collapsible": ["@zag-js/collapsible@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-Y/SnLO3Tw4z/pWgvrO3j4eOPGN5YQxT173kYqE5c721dfK8TFbBsSH5oqjt4JE6VNCPnY9vFNc3zuXuvTLqLkg=="], 206 + 207 + "@zag-js/collection": ["@zag-js/collection@1.26.4", "", { "dependencies": { "@zag-js/utils": "1.26.4" } }, "sha512-cpDg0TnK49xD/qnhR6pzbYZ73b1+i22eE7xlVX5nwIZdaS3pRerx3YvD7epgYYjbcm/zeKIiFQcFj+CsWjtiLg=="], 208 + 209 + "@zag-js/combobox": ["@zag-js/combobox@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/aria-hidden": "1.26.4", "@zag-js/collection": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dismissable": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/popper": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-4Yq8nULxT1qEq+AYixZyaryQZrLjM1hL1C/QpPtUDuX0pN7jXKZTA18M3i0TtVRF+auQE3zI+1l5YTWrFQGPqw=="], 210 + 211 + "@zag-js/core": ["@zag-js/core@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-6pqLD/RlPLV0U+Zvw1JLp1aRMfujt5c4+K5FqDNEsGDlDkP8b/n89TADuhgloFeUlfYgHzXCHTO7UHDrNKY5dQ=="], 212 + 213 + "@zag-js/date-picker": ["@zag-js/date-picker@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/date-utils": "1.26.4", "@zag-js/dismissable": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/live-region": "1.26.4", "@zag-js/popper": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" }, "peerDependencies": { "@internationalized/date": "3.10.0" } }, "sha512-qyRqBxuF/sqSuGiio13958i+AoEvTfZDbzpZYj+2d/O7ubyTLD3TJMeTHzN97lmOa9mbQSuaL+aOOW3G7d+fWQ=="], 214 + 215 + "@zag-js/date-utils": ["@zag-js/date-utils@1.26.4", "", { "peerDependencies": { "@internationalized/date": "3.10.0" } }, "sha512-PdOfWlzGRcQK7kefGVBOXJzmQqQXI+tHUutLWqWCa/WIq81wLXO0U4UDRPQgzL4f9JQ2UTVIdQsa9wlx4i5EXQ=="], 216 + 217 + "@zag-js/dialog": ["@zag-js/dialog@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/aria-hidden": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dismissable": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/focus-trap": "1.26.4", "@zag-js/remove-scroll": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-glXvVGgh1ZnMtFlG0s3cub7d+C4jCkzsniuD0sNU1pWrziJFqSFAeqGAl+uTpMTs0C/jo8DpZzcGEsPmgaIhjg=="], 218 + 219 + "@zag-js/dismissable": ["@zag-js/dismissable@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4", "@zag-js/interact-outside": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-aY1W3YRh7bP3izaW1pV2V6quZHCTUrEp0Cr/345p6j8PJefsesI75+ln6xHO+wMXJFiNPDAbWImI+4Zv2qrvLQ=="], 220 + 221 + "@zag-js/dom-query": ["@zag-js/dom-query@1.26.4", "", { "dependencies": { "@zag-js/types": "1.26.4" } }, "sha512-Yxovh/S/9V/SLZvDwXxLXjsjRjLDHFHSgwsZh7dk7WFqDbnrQPY96rK8+Khd4EI2dMLdhmZM3AEU2IuKWKe2yQ=="], 222 + 223 + "@zag-js/file-upload": ["@zag-js/file-upload@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/file-utils": "1.26.4", "@zag-js/i18n-utils": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-j6BqWFQ1Tgk8nFcZyYLhZ/PE0r0lbEMNsZ4JhMMlk1gI4axBLNttj51LcFmJGOlGz2q0Kh1ULun6MAO6W54zmQ=="], 224 + 225 + "@zag-js/file-utils": ["@zag-js/file-utils@1.26.4", "", { "dependencies": { "@zag-js/i18n-utils": "1.26.4" } }, "sha512-C+5+y0exftrc3a2+HA+lxf8CSBBlF77tyuHr2noGB6TXWeUHjgDkUSn4j556d8lmbdWi0YSMqESI3eDdeFwBAw=="], 226 + 227 + "@zag-js/focus-trap": ["@zag-js/focus-trap@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4" } }, "sha512-/75t0MroZdtyXntQ4PclmD6fQD/xvV5tiNJPc7lY88Xk0TpxaWtJZ/beS98iR47jm1i7BbhlpbL0EyBTi6RJ7w=="], 228 + 229 + "@zag-js/focus-visible": ["@zag-js/focus-visible@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4" } }, "sha512-wKpnO/g99y9KYXZlszrHVxdI0ZEAEO/RL+5AcWqBiCuQhagm+rB9z4VeNK6K+BcOs+5PMJP2DY0UnNgmC2WbdQ=="], 230 + 231 + "@zag-js/i18n-utils": ["@zag-js/i18n-utils@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4" } }, "sha512-3ckQ4MMlZSu8TV6VMukpdZF/IjhiOwXEE9a4WqAMKCS16bqkDb574kKXGr2ZpXCVoWXYR+8OxbxeZfWZhUGKiA=="], 232 + 233 + "@zag-js/interact-outside": ["@zag-js/interact-outside@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-2EMqsO2/7zzIytADlfIRsHX/INkSoxnuVGOecjGY2ZUfOlOnbNngIkRUqLNDH9X814Xx5KEHeiGLvXe/xe9hOA=="], 234 + 235 + "@zag-js/listbox": ["@zag-js/listbox@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/collection": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/focus-visible": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-LWd9U9IJuI0NPPWvxbpOXxpTRSE7fGhzSEp8LFHWpa5gDKKDdEI/jSc8MsuaNGAYQCIqINKWWjakyWzioSrzOA=="], 236 + 237 + "@zag-js/live-region": ["@zag-js/live-region@1.26.4", "", {}, "sha512-4coDig5/SU9wkMrlDGVMYD8I314TTMTnJcvudQGZ66VvGAUn5UCRg2/vCvMn+ia7ZOB0eEPc8Jl5Gf4whAiXgg=="], 238 + 239 + "@zag-js/pagination": ["@zag-js/pagination@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-igDpqg3YC9RRz0+aW+sS01WGBhOxCYRH5LtB+RoQoX2qjg18HwCD/gxusogrtX+2yJUj6oXFtUjId+ftetkrxA=="], 240 + 241 + "@zag-js/popover": ["@zag-js/popover@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/aria-hidden": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dismissable": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/focus-trap": "1.26.4", "@zag-js/popper": "1.26.4", "@zag-js/remove-scroll": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-mKDtqTPEJEKVDF5MOWVRsRTswv+DxoYoOhZg5nSiLiRBrG58h9d/28UKh92lP8wtYkpelyfD4AQPCQIiLcG2Fw=="], 242 + 243 + "@zag-js/popper": ["@zag-js/popper@1.26.4", "", { "dependencies": { "@floating-ui/dom": "1.7.4", "@zag-js/dom-query": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-CnJb30EVR+xXeO3dRhiPkUghi2B1Z0OFMRPAi9NMBnAFD++jOzsGZe71CvthQbYqP9hU85uzBzVh6qMXXpTYhg=="], 244 + 245 + "@zag-js/progress": ["@zag-js/progress@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-w4uGApXLr0ZBo1biK9payfLWFwKhxy0iHpE2fP3uEjLctdV1jg7z1zAJ3/5qwEv5lMGKLhX/cXHaIYUL0DCf4w=="], 246 + 247 + "@zag-js/radio-group": ["@zag-js/radio-group@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/focus-visible": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-yTYtL7dBtdU9COQyWoafNo082/1EoJvnaaO+jE7sss6t5YSRkT0PUFZhwZObsJB1hivvojXdhw65S1P/iegB7A=="], 248 + 249 + "@zag-js/rating-group": ["@zag-js/rating-group@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-ZUh7cok2YhIp9lHxScD8LRuGpTlT38o9v9h5l3/Tm2ptb3zq8ils2L/w7yT87DKXnO7XLsdBpoXVS3Jd3JDxtw=="], 250 + 251 + "@zag-js/remove-scroll": ["@zag-js/remove-scroll@1.26.4", "", { "dependencies": { "@zag-js/dom-query": "1.26.4" } }, "sha512-I1vHZBo2EHKLs7pQ57B/w1U+GhdpBl5CYP6t3XdamPfZURDnqjJ3WqyIlTX2w0gGgQH3Ty61lip/4cvaVU0G/A=="], 252 + 253 + "@zag-js/slider": ["@zag-js/slider@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-8TJF6yWyAP2919u+0ldqqDnitqcmEHzmk/NioRgNtycKmsOqRw6wuf9zeiBagCQmgn/8i02dmjrYtkIAMNFpfA=="], 254 + 255 + "@zag-js/svelte": ["@zag-js/svelte@1.26.4", "", { "dependencies": { "@zag-js/core": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" }, "peerDependencies": { "svelte": "5.43.2" } }, "sha512-V4DzemgLvnyNQqdtVfrzJ3mxA84Rakg5i+4XXUzgPA8sL1gGclNISFpOAaiyBIh0KjbKNU38t/PPwlmR+5xV+Q=="], 256 + 257 + "@zag-js/switch": ["@zag-js/switch@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/focus-visible": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-qka+TZBwwNTuDkcCT4I/RqeG300bRDqg3eOjPIwm5fX9zy4gFfQ6dNPkycAyk/6yFwmY6ao40GYkCQlaMnD7Pw=="], 258 + 259 + "@zag-js/tabs": ["@zag-js/tabs@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-rLFTW2R3pNlauscsSJZIlocFWU3TsQ8Bhfjc+TJQHcUBsbOPtYmuYCMuwxJBrpuKjyIt8U5DEGuzDumsVO0t5g=="], 260 + 261 + "@zag-js/tags-input": ["@zag-js/tags-input@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/auto-resize": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/interact-outside": "1.26.4", "@zag-js/live-region": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-9SN9bsduoSjkXdzVxU/f3VYDKufa1d5IGQSsg2fa+f2UXeJ8NDOo/XVIZjAN6VO/vkZf3JuPr4H/yrsb4BUETw=="], 262 + 263 + "@zag-js/toast": ["@zag-js/toast@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dismissable": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-Bgp3T+cXDbEollDc1woqmv6HDUydGx4MeAJcK4wh8MNwNnxb/42mTBvmffn7wCU07/N6AYroP+m1BVo58c1NPg=="], 264 + 265 + "@zag-js/toggle-group": ["@zag-js/toggle-group@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-idOp4XkR5IAZAa7LNT74I7RBl78AUX9Ih0uthpUEBGndpjkViiZXzaU8X5a8FHBuNp5eUdNUVRM3P9zX+b5gdw=="], 266 + 267 + "@zag-js/tooltip": ["@zag-js/tooltip@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/focus-visible": "1.26.4", "@zag-js/popper": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-a+9QPbDib/XmGxp4pp+5UoNXp7d45uAwBfmQ9VAY0+vrluDcu83lOWmhiFHOui85nY7BRCuW5iaT9TIFrewV7Q=="], 268 + 269 + "@zag-js/tree-view": ["@zag-js/tree-view@1.26.4", "", { "dependencies": { "@zag-js/anatomy": "1.26.4", "@zag-js/collection": "1.26.4", "@zag-js/core": "1.26.4", "@zag-js/dom-query": "1.26.4", "@zag-js/types": "1.26.4", "@zag-js/utils": "1.26.4" } }, "sha512-mK56WITHqz3AIfKnZZm0P5zZ4dDa6FtE/v0LdWfvHfASjDQibtpgTwcdkQd3a6qMqHxWiq1lchTBhc21Xl3/MQ=="], 270 + 271 + "@zag-js/types": ["@zag-js/types@1.26.4", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-vmuC9fYJeM8dbwk+dxfdX6cFPnJsPVgQGIUJATbbp+S46JyOuJV4T3ERYX3TxBEdsgoKjWD1i1OQWWc3Nywtjg=="], 272 + 273 + "@zag-js/utils": ["@zag-js/utils@1.26.4", "", {}, "sha512-IwfMUrTZA31I2c/Oomqwqo0HoSZUVvwIejCpPvITiqtB+HZ4rmNHhSbRgz5+llsOiuBfnQ5SJvQCD4MKOHMA3w=="], 274 + 275 + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 276 + 277 + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 278 + 279 + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 280 + 281 + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "4.1.2" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 282 + 283 + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 284 + 285 + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], 286 + 287 + "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], 288 + 289 + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 290 + 291 + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], 292 + 293 + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 294 + 295 + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "4.2.11", "tapable": "2.3.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], 296 + 297 + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], 298 + 299 + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 300 + 301 + "esrap": ["esrap@2.1.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg=="], 302 + 303 + "fdir": ["fdir@6.5.0", "", { "optionalDependencies": { "picomatch": "4.0.3" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 304 + 305 + "filesize": ["filesize@11.0.13", "", {}, "sha512-mYJ/qXKvREuO0uH8LTQJ6v7GsUvVOguqxg2VTwQUkyTPXXRRWPdjuUPVqdBrJQhvci48OHlNGRnux+Slr2Rnvw=="], 306 + 307 + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 308 + 309 + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 310 + 311 + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "1.0.8" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 312 + 313 + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 314 + 315 + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], 316 + 317 + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], 318 + 319 + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], 320 + 321 + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], 322 + 323 + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], 324 + 325 + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], 326 + 327 + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], 328 + 329 + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], 330 + 331 + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], 332 + 333 + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], 334 + 335 + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], 336 + 337 + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], 338 + 339 + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 340 + 341 + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], 342 + 343 + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 344 + 345 + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 346 + 347 + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 348 + 349 + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 350 + 351 + "numeral": ["numeral@2.0.6", "", {}, "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA=="], 352 + 353 + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 354 + 355 + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 356 + 357 + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 358 + 359 + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 360 + 361 + "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "2.3.3" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="], 362 + 363 + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "1.2.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], 364 + 365 + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 366 + 367 + "svelte": ["svelte@5.43.2", "", { "dependencies": { "@jridgewell/remapping": "2.3.5", "@jridgewell/sourcemap-codec": "1.5.5", "@sveltejs/acorn-typescript": "1.0.6", "@types/estree": "1.0.8", "acorn": "8.15.0", "aria-query": "5.3.2", "axobject-query": "4.1.0", "clsx": "2.1.1", "esm-env": "1.2.2", "esrap": "2.1.2", "is-reference": "3.0.3", "locate-character": "3.0.0", "magic-string": "0.30.21", "zimmerframe": "1.1.4" } }, "sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA=="], 368 + 369 + "svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "chokidar": "4.0.3", "fdir": "6.5.0", "picocolors": "1.1.1", "sade": "1.8.1" }, "peerDependencies": { "svelte": "5.43.2", "typescript": "5.9.3" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="], 370 + 371 + "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="], 372 + 373 + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], 374 + 375 + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 376 + 377 + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 378 + 379 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 380 + 381 + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 382 + 383 + "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "0.25.12", "fdir": "6.5.0", "picomatch": "4.0.3", "postcss": "8.5.6", "rollup": "4.52.5", "tinyglobby": "0.2.15" }, "optionalDependencies": { "@types/node": "24.9.2", "fsevents": "2.3.3", "jiti": "2.6.1", "lightningcss": "1.30.2" }, "bin": { "vite": "bin/vite.js" } }, "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], 384 + 385 + "vitefu": ["vitefu@1.1.1", "", { "optionalDependencies": { "vite": "7.1.12" } }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], 386 + 387 + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 388 + } 389 + }
+7 -1
package.json
··· 7 7 "dev": "vite", 8 8 "build": "vite build", 9 9 "preview": "vite preview", 10 - "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json" 10 + "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json", 11 + "deploy": "bun run build && bunx wrangler pages deploy --branch main --project-name plcbundle-watch ./dist" 11 12 }, 12 13 "devDependencies": { 13 14 "@skeletonlabs/skeleton": "^4.2.2", ··· 15 16 "@sveltejs/vite-plugin-svelte": "^6.2.1", 16 17 "@tsconfig/svelte": "^5.0.5", 17 18 "@types/node": "^24.6.0", 19 + "@types/lodash": "^4.17.20", 18 20 "svelte": "^5.39.6", 19 21 "svelte-check": "^4.3.2", 20 22 "typescript": "~5.9.3", ··· 22 24 }, 23 25 "dependencies": { 24 26 "@tailwindcss/vite": "^4.1.16", 27 + "date-fns": "^4.1.0", 28 + "filesize": "^11.0.13", 29 + "lodash": "^4.17.21", 30 + "numeral": "^2.0.6", 25 31 "tailwindcss": "^4.1.16" 26 32 } 27 33 }
+368 -65
src/App.svelte
··· 1 1 <script lang="ts"> 2 + 2 3 import { onMount } from 'svelte'; 4 + import { filesize } from 'filesize'; 5 + import { formatDistanceToNow, addSeconds, subSeconds, formatDate, formatISO9075, differenceInSeconds } from 'date-fns'; 6 + import { Progress, Switch } from '@skeletonlabs/skeleton-svelte'; 7 + import orderBy from 'lodash/orderBy'; 8 + import BundleDownloader from './BundleDownloader.svelte'; 9 + import { formatNumber, formatUptime } from './lib/utils'; 10 + import instancesData from './instances.json'; 11 + 12 + const APP_TITLE = 'plcbundle instances' 13 + const PLC_DIRECTORY = 'plc.directory' 14 + const ROOT = 'f743c3ae1e3f6023e89e492bce63b52a9ed03ee46a163c2f4a3b997eaf2aaf85' 15 + const AUTO_REFRESH_INTERVAL = 10 // in seconds 16 + const BUNDLE_OPS = 10_000 17 + const PAST_ROOTS = [ 18 + // November 2025: https://bsky.app/profile/atproto.com/post/3m4e3mnxb7s2p 19 + "cbab6809a136d6a621906ee11199d3b0faf85b422fe0d0d2c346ce8e9dcd7485" 20 + ] 21 + 22 + type StatusResponse = { 23 + ok: boolean; 24 + bundles: { 25 + last_bundle: number; 26 + root_hash: string; 27 + head_hash: string; 28 + end_time?: string; 29 + total_size?: number; 30 + uncompressed_size?: number; 31 + updated_at?: string; 32 + }; 33 + server: { 34 + uptime: number; 35 + }; 36 + mempool?: { 37 + count: number; 38 + eta_next_bundle_seconds: number; 39 + last_time: Date; 40 + }; 41 + latency?: number; 42 + } 43 + 44 + type StatusResponseError = { 45 + error: string; 46 + } 3 47 4 48 type Instance = { 5 - url: string, 6 - cors?: boolean, 7 - status?: object, 8 - modern?: boolean, 49 + url: string; 50 + cors?: boolean; 51 + status?: StatusResponse | StatusResponseError; 52 + modern?: boolean; 53 + _head?: boolean; 54 + _oldRoot?: boolean; 55 + _conflict?: boolean; 56 + } 57 + 58 + type LastKnownBundle = { 59 + number: number; 60 + hash: string | null; 61 + mempool: number | null; 62 + mempoolPercent: number; 63 + mempoolBundle: number; 64 + lastTime?: Date; 65 + time?: string; 66 + etaNext?: Date | null; 67 + totalSize?: number | null; 68 + totalSizeUncompressed?: number | null; 9 69 } 10 70 11 - let lastKnownBundle = $state({ 71 + let lastKnownBundle = $state<LastKnownBundle>({ 12 72 number: 0, 13 73 hash: null, 74 + mempool: null, 75 + mempoolBundle: 0, 76 + mempoolPercent: 0, 14 77 }) 15 78 16 - let instances = $state([ 17 - { url: "https://plcbundle.atscan.net", modern: true }, 18 - { url: "https://plc.j4ck.xyz", modern: false }, 19 - { url: "https://plc.indexx.dev", modern: false }, 20 - ]) 79 + let isUpdating = $state(false) 80 + let canRefresh = $state(true) 81 + let consensus = $state({}) 82 + let isConflict = $state(consensus) 83 + let instancesInConflict = $state<string[]>([]) 84 + let lastUpdated = $state(new Date()) 85 + let autoRefreshEnabled = $state(true) 86 + let instances = $state<Instance[]>(instancesData.sort(() => Math.random() - 0.5)) 21 87 22 - //instancesSorted = $derived(instances.sort()) 88 + const instanceOrderBy = [ 89 + ['status.error', '_head', '_oldRoot' , 'status.bundles.last_bundle', 'status.latency'], 90 + ['desc', 'desc', 'asc', 'desc', 'asc'] 91 + ] 23 92 24 - async function getStatus(instance: Instance) { 25 - let statusResp: object | undefined; 93 + async function getStatus(instance: Instance): Promise<StatusResponse | StatusResponseError> { 94 + let statusResp: StatusResponse | undefined; 26 95 let url: string = instance.url; 96 + let lastError: string | undefined; 27 97 const start = performance.now(); 28 - if (instance.modern === false) { 98 + try { 99 + statusResp = await (await fetch(`${url}/status?${Number(new Date())}`)).json() 100 + } catch (e: any) { 101 + lastError = e.message; 102 + } 103 + if (!statusResp) { 29 104 url = `https://keyoxide.org/api/3/get/http?url=${encodeURIComponent(url)}&format=text&time=${Date.now()}` 105 + 106 + let indexResp: string | undefined; 107 + try { 108 + indexResp = await (await fetch(url)).text() 109 + } catch(e: any) { 110 + lastError = e.message; 111 + } 112 + const match = indexResp?.match(/Range:\s+(\d{6}) - (\d{6})/) 113 + if (match) { 114 + const [, from, to] = match 115 + const rootMatch = indexResp?.match(/Root: ([a-f0-9]{64})/) 116 + const headMatch = indexResp?.match(/Head: ([a-f0-9]{64})/) 117 + 118 + statusResp = { 119 + ok: true, 120 + bundles: { 121 + last_bundle: Number(to), 122 + root_hash: rootMatch ? rootMatch[1] : '', 123 + head_hash: headMatch ? headMatch[1] : '', 124 + }, 125 + server: { 126 + uptime: 1, 127 + } 128 + } 129 + } 30 130 } 31 - if (instance.modern) { 32 - try { 33 - statusResp = await (await fetch(`${url}/status`)).json() 34 - } catch (e) {} 131 + if (statusResp) { 132 + statusResp.ok = true 133 + statusResp.latency = performance.now() - start; 35 134 } 36 - if (!statusResp) { 37 - const indexResp = await (await fetch(url)).text() 38 - const [ _, from, to ] = indexResp?.match(/Range:\s+(\d{6}) - (\d{6})/) 39 - statusResp = { 40 - bundles: { 41 - last_bundle: Number(to), 42 - root_hash: indexResp?.match(/Root: ([a-f0-9]{64})/)[1], 43 - head_hash: indexResp?.match(/Head: ([a-f0-9]{64})/)[1], 44 - }, 45 - server: { 46 - uptime: 1, 135 + //if (instance.url === 'https://plc.j4ck.xyz') { statusResp.bundles.head_hash = 'f3ad3544452b2c078cba24990486bb9c277a1155'; } 136 + return statusResp ?? { error: lastError || 'unknown error' } 137 + } 138 + 139 + function recalculateHead() { 140 + isConflict = false 141 + instancesInConflict = [] 142 + const headHashes: any = {} 143 + for (const instance of instances) { 144 + if ((instance.status && 'error' in instance.status) || !instance.status) { 145 + continue 146 + } 147 + const { head_hash, root_hash, last_bundle } = instance.status.bundles 148 + instance._oldRoot = PAST_ROOTS.includes(root_hash) 149 + instance._head = !instance._oldRoot && (last_bundle === lastKnownBundle.number) 150 + 151 + if (instance._head && head_hash) { 152 + if (!headHashes[head_hash]) { 153 + headHashes[head_hash] = [] 47 154 } 155 + headHashes[head_hash].push(instance.url) 48 156 } 49 157 } 50 - if (statusResp) { 51 - statusResp.responseTime = performance.now() - start; 158 + console.log(headHashes) 159 + // second pass 160 + const sorted: any = Object.fromEntries( 161 + Object.entries(headHashes).sort(([, a]: any, [, b]: any) => b.length - a.length) 162 + ) 163 + for (const instance of instances) { 164 + if (Object.keys(sorted).length > 1 && Object.keys(sorted)[0] && !sorted[Object.keys(sorted)[0]].includes(instance.url)) { 165 + instance._conflict = true 166 + instancesInConflict.push(instance.url) 167 + } 52 168 } 53 - return statusResp 169 + //const uniq = [...new Set(headHashes)] 170 + isConflict = instancesInConflict.length > Math.ceil(instances.length/2) 54 171 } 55 172 56 173 async function doCheck() { 174 + isUpdating = true 175 + canRefresh = false 57 176 for (const i of instances) { 58 - i.status = undefined 177 + if (i.status && 'ok' in i.status) { 178 + i.status.ok = false 179 + } 59 180 } 60 181 61 182 await Promise.all(instances.map(async (instance) => { 62 183 const status = await getStatus(instance) 63 - 64 - if (status?.bundles?.last_bundle > lastKnownBundle.number) { 65 - lastKnownBundle.number = status?.bundles?.last_bundle 66 - lastKnownBundle.hash = status?.bundles?.head_hash 184 + if (!status) { 185 + return false 67 186 } 187 + 68 188 instance.status = status 189 + if ('ok' in status && status.ok) { 190 + 191 + if (status?.bundles?.last_bundle && status.bundles.last_bundle >= lastKnownBundle.number && !PAST_ROOTS.includes(status.bundles.root_hash)) { 192 + lastKnownBundle.number = status.bundles.last_bundle 193 + lastKnownBundle.hash = status.bundles.head_hash 194 + lastKnownBundle.time = status.bundles.end_time 195 + 196 + if (status?.mempool?.count && (!lastKnownBundle.mempool || status.mempool.count > lastKnownBundle.mempool || status.bundles.last_bundle > lastKnownBundle.mempoolBundle)) { 197 + lastKnownBundle.mempoolBundle = status.bundles.last_bundle 198 + lastKnownBundle.mempool = status.mempool.count 199 + lastKnownBundle.lastTime = status.mempool.last_time 200 + lastKnownBundle.mempoolPercent = Math.round((lastKnownBundle.mempool/100)*100)/100 201 + lastKnownBundle.etaNext = status.mempool.eta_next_bundle_seconds ? addSeconds(new Date(), status.mempool.eta_next_bundle_seconds) : null 202 + lastKnownBundle.totalSize = status.bundles.total_size 203 + lastKnownBundle.totalSizeUncompressed = status.bundles.uncompressed_size 204 + } 205 + } 206 + } 207 + 208 + lastUpdated = new Date() 209 + 210 + recalculateHead() 69 211 })) 212 + isUpdating = false 213 + updateTitle() 214 + setTimeout(() => { canRefresh = true }, 500) 215 + } 70 216 71 - instances = instances.sort((a, b) => a.status?.responseTime > b.status?.responseTime ? 1 : -1) 217 + function normalizedVersion(version: string) { 218 + const m = version.trim().match(/^([^\s]+)\.\d+\.\d+\-[0-9a-f]+(\+dirty|)$/) 219 + if (m) { 220 + return `${m[1]}+dirty` 221 + } 222 + return version 223 + } 72 224 225 + function updateTitle() { 226 + const arr: string[] = [] 227 + if (lastUpdated) { 228 + const upCount = instances.filter(i => i._head && !i._conflict) 229 + arr.push(`${isConflict ? 'โš ๏ธ' : 'โœ…'} [${upCount.length}/${instances.length}]`) 230 + } 231 + document.title = [...arr, APP_TITLE].join(' ') 232 + return true 73 233 } 74 234 75 - onMount(() => { 76 - doCheck() 77 - }) 235 + function bundleAge(instance: Instance) { 236 + if (!instance.status || !('bundles' in instance.status) || !instance.status.bundles?.updated_at) { 237 + return '-' 238 + } 239 + const str = instance.status?.bundles.updated_at ? instance.status?.bundles.updated_at : instance.status?.bundles.end_time || '' 240 + const diff = differenceInSeconds(new Date(), new Date(str)) 241 + return diff > 300 ? '>5m' : diff + 's' 242 + } 78 243 79 - <script> 244 + function secondsAge(seconds: number) { 245 + return seconds > 300 ? '>5m' : seconds + 's' 246 + } 247 + 248 + let autoRefreshTimer: ReturnType<typeof setTimeout> | null = null; 249 + 250 + onMount(() => { 251 + doCheck().then(() => { 252 + const scheduleRefresh = () => { 253 + autoRefreshTimer = setTimeout(() => { 254 + if (autoRefreshEnabled) { 255 + doCheck() 256 + } 257 + scheduleRefresh() 258 + }, AUTO_REFRESH_INTERVAL * 1000) 259 + } 260 + 261 + scheduleRefresh() 262 + 263 + }) 264 + 265 + return () => { 266 + if (autoRefreshTimer) { 267 + clearTimeout(autoRefreshTimer) 268 + } 269 + } 270 + }) 80 271 </script> 81 272 82 - <main class="w-full mt-10"> 83 - <div class="max-w-4xl mx-auto px-3"> 273 + <main class="w-full mt-10 mb-16"> 274 + <div class="max-w-5xl mx-auto px-3"> 84 275 85 - <header> 86 - <h1 class="text-3xl">plcbundle instances</h1> 276 + <header class="flex items-center gap-10 flex-wrap"> 277 + <div class="grow"> 278 + <h1 class="text-3xl linear-text-gradient"><a href="https://plcbundle-watch.pages.dev/" class="no-style">plcbundle instances</a></h1> 279 + </div> 280 + <div class="flex items-center gap-6"> 281 + <Switch class="opacity-75" checked={autoRefreshEnabled} onCheckedChange={(x) => autoRefreshEnabled = x.checked} disabled={isUpdating}> 282 + <Switch.Control class="data-[state=checked]:preset-filled-success-500"> 283 + <Switch.Thumb /> 284 + </Switch.Control> 285 + <Switch.Label>Auto-refresh ({AUTO_REFRESH_INTERVAL}s)</Switch.Label> 286 + <Switch.HiddenInput /> 287 + </Switch> 288 + <button type="button" class="btn btn-sm preset-tonal-primary" onclick={() => doCheck()} disabled={isUpdating || canRefresh === false}>Refresh</button> 289 + </div> 87 290 </header> 88 291 89 - <div class="flex items-center gap-2 mt-10 flex-wrap"> 90 - <div class="grow flex items-center text-lg"> 91 - <div><span class="opacity-50">Last known bundle:</span> <span class="font-semibold">{lastKnownBundle.number}</span> [<span class="font-mono text-base">{lastKnownBundle?.hash?.slice(0, 7)}</span>]</div> 292 + <div class="gap-10 mt-6 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"> 293 + <div> 294 + <h2 class="opacity-75 text-sm">Last known bundle</h2> 295 + <div> 296 + <div class="flex items-center gap-5"> 297 + <div class="font-semibold text-3xl">{lastKnownBundle.number}</div> 298 + {#if !isConflict} 299 + <div class="mt-1 font-mono badge preset-outlined-primary-500 text-xs">{lastKnownBundle?.hash?.slice(0, 7)}</div> 300 + {:else} 301 + <div class="mt-1 badge preset-filled-error-500">โš ๏ธ conflict!</div> 302 + {/if} 303 + </div> 304 + <div> 305 + <span class="opacity-50">{#if lastKnownBundle?.time} {formatDistanceToNow(lastKnownBundle.time, { addSuffix: true })}{/if}</span> 306 + </div> 307 + <div class="mt-1"> 308 + {#if instancesInConflict.length > 0} 309 + โš ๏ธ Fork alert on {instancesInConflict.length} instances! 310 + {:else if !isConflict} 311 + โœ… Everything fine! 312 + {/if} 313 + </div> 314 + </div> 92 315 </div> 93 - <div class=""> 94 - <button type="button" class="btn btn-sm preset-tonal-primary" onclick={() => doCheck()}>Refresh</button> 316 + <div> 317 + <div> 318 + <h2 class="opacity-75 text-sm">Next bundle</h2> 319 + </div> 320 + <div class="flex gap-4"> 321 + <div class="mt-4"> 322 + <Progress value={lastKnownBundle.mempoolPercent} class="items-center"> 323 + <Progress.Circle style="--size: 64px; --thickness: 10px;" class={lastKnownBundle.mempoolPercent > 95 ? 'animate-pulse' : ''}> 324 + <Progress.CircleTrack /> 325 + <Progress.CircleRange /> 326 + </Progress.Circle> 327 + <!--Progress.ValueText class="text-xs opacity-50" /--> 328 + </Progress> 329 + </div> 330 + {#if lastKnownBundle.number > 0} 331 + <div> 332 + <div class="font-semibold text-2xl animate-pulse">{lastKnownBundle.number + 1}</div> 333 + <div>{formatNumber(lastKnownBundle.mempool || 0)} / {formatNumber(BUNDLE_OPS)} <span class="opacity-50">({lastKnownBundle.mempoolPercent}%)</span></div> 334 + {#if lastKnownBundle.etaNext} 335 + <div class="mt-1 opacity-50">ETA: {formatDistanceToNow(lastKnownBundle.etaNext)}</div> 336 + {/if} 337 + </div> 338 + {/if} 339 + </div> 95 340 </div> 341 + {#if lastKnownBundle.number > 0} 342 + <div class=""> 343 + <div> 344 + <h2 class="opacity-75 text-sm">Statistics</h2> 345 + </div> 346 + <div class="mt-2 grid grid-cols-1 gap-1"> 347 + <div><span class="opacity-50">Instances:</span> {instances.filter(i => i._head && !i._conflict).length} latest / {instances.length} total</div> 348 + <div><span class="opacity-50">PLC Operations:</span> {formatNumber((lastKnownBundle.number * BUNDLE_OPS) + (lastKnownBundle.mempool || 0))}</div> 349 + <div><span class="opacity-50">Bundles Size:</span> {#if lastKnownBundle.totalSize}{filesize(lastKnownBundle.totalSize)}{/if}</div> 350 + <div><span class="opacity-50">Uncompressed:</span> {#if lastKnownBundle.totalSizeUncompressed}{filesize(lastKnownBundle.totalSizeUncompressed)}{/if}</div> 351 + </div> 352 + </div> 353 + {/if} 96 354 </div> 97 355 98 - <table class="table mt-4"> 356 + <table class="table mt-10"> 99 357 <thead> 100 358 <tr> 101 359 <th>endpoint</th> 102 - <th>status</th> 103 - <th>last bundle</th> 360 + <th>ok?</th> 361 + <th>last</th> 362 + <th>mempool</th> 363 + <th>age</th> 104 364 <th>head</th> 105 365 <th>root</th> 106 366 <th>version</th> 107 - <th>rtt</th> 367 + <th>rsv?</th> 368 + <th>ws?</th> 369 + <th>uptime</th> 370 + <th>latency</th> 108 371 </tr> 109 372 </thead> 110 - <tbody> 111 - {#each instances as instance} 373 + <tbody class="[&>tr]:hover:bg-primary-500/10"> 374 + {#each orderBy(instances, ...instanceOrderBy) as instance} 112 375 <tr> 113 376 <td><a href={instance.url} target="_blank" class="font-semibold">{instance.url.replace("https://", "")}</a></td> 114 - <td>{#if instance.status?.bundles?.last_bundle === lastKnownBundle.number}โœ…{:else if instance.status}๐Ÿ”„{/if}</td> 115 - <td>{#if instance.status?.bundles?.last_bundle}{instance.status?.bundles?.last_bundle}{/if}</td> 116 - <td><span class="font-mono text-xs">{#if instance.status?.bundles?.head_hash}{instance.status?.bundles?.head_hash.slice(0, 7)}{/if}</span></td> 117 - <td><span class="font-mono text-xs">{#if instance.status?.bundles?.root_hash}{instance.status?.bundles?.root_hash.slice(0, 7)}{/if}</span></td> 118 - <td>{#if instance.status?.server?.version}{instance.status?.server?.version}{/if}</td> 119 - <td class="opacity-50">{#if instance.status?.responseTime}{instance.status?.responseTime}ms{/if}</td> 377 + <td>{#if instance._head && instance.status?.ok}{#if instance._conflict}โš ๏ธ{:else}โœ…{/if}{:else if instance.status?.ok && instance._oldRoot}๐Ÿฆ‡{:else if instance.status?.ok}๐Ÿ”„{:else if instance.status?.error}โŒ{:else}โŒ›{/if}</td> 378 + {#if instance.status?.error} 379 + <td colspan="8" class="opacity-50 text-xs">Error: {instance.status?.error}</td> 380 + {:else} 381 + <td>{#if instance.status?.bundles?.last_bundle}<span class="{instance._conflict ? 'text-error-600' : ''}">{instance.status?.bundles?.last_bundle}</span>{/if}</td> 382 + <td>{#if instance.status?.mempool && (instance._head || instance._oldRoot)}<span class="{instance._conflict ? 'text-error-600' : (instance._oldRoot ? 'text-warning-500 opacity-50' : '')}">{formatNumber(instance.status?.mempool.count)}</span>{:else if instance.status?.error}<span class="opacity-25 text-xs">error</span>{:else if instance.status}<span class="opacity-25 text-xs">syncing</span>{/if}</td> 383 + <td> 384 + {#if instance.status?.mempool} 385 + <span class="text-xs opacity-50 {instance._conflict ? 'text-error-600' : (instance._oldRoot ? 'text-warning-500' : '')}"> 386 + {#if instance._head || instance._oldRoot} 387 + {secondsAge(instance.status?.mempool.last_op_age_seconds || 0)} 388 + {:else} 389 + {bundleAge(instance)} 390 + {/if} 391 + </span> 392 + {/if} 393 + </td> 394 + <td><span class="font-mono text-xs {instance._head ? (instance._conflict ? 'text-error-600' : 'text-success-600') : (instance._oldRoot ? 'text-warning-500 opacity-50' : 'opacity-50')}">{#if instance.status?.bundles?.head_hash}{instance.status?.bundles?.head_hash.slice(0, 7)}{/if}</span></td> 395 + <td><span class="font-mono text-xs {instance.status ? (instance.status?.bundles?.root_hash === ROOT ? 'text-success-600' : (PAST_ROOTS.includes(instance.status?.bundles?.root_hash) ? 'text-warning-500' : 'text-error-600')) : ''}">{#if instance.status?.bundles?.root_hash}{instance.status?.bundles?.root_hash.slice(0, 7)}{/if}</span></td> 396 + <td class="text-xs">{#if instance.status?.server?.version}<span title={instance.status?.server?.version}>{normalizedVersion(instance.status?.server?.version)}</span>{/if}</td> 397 + <td class="text-xs">{#if instance.status?.server?.resolver_enabled}โœ”๏ธŽ{:else if instance.status}<span class="opacity-25">-</span>{/if}</td> 398 + <td class="text-xs">{#if instance.status?.server?.websocket_enabled}โœ”๏ธŽ{:else if instance.status}<span class="opacity-25">-</span>{/if}</td> 399 + {/if} 400 + <td class="text-xs">{#if instance.status?.server?.uptime_seconds}{formatUptime(instance.status?.server?.uptime_seconds)}{/if}</td> 401 + <td class="text-xs opacity-50">{#if instance.status?.latency}<a href="{instance.url}/status">{Math.round(instance.status?.latency)}ms</a>{/if}</td> 120 402 </tr> 121 403 {/each} 122 404 </tbody> 123 405 </table> 124 406 125 - <div class="mt-12 opacity-50"> 126 - Source: <a href="https://tangled.org/@tree.fail/plcbundle-watch">https://tangled.org/@tree.fail/plcbundle-watch</a> 407 + 408 + <div class="mt-12"> 409 + <div> 410 + <span class="opacity-75">PLC Directory:</span> <a href="https://{PLC_DIRECTORY}">{PLC_DIRECTORY}</a> <span class="opacity-50">(origin)</span> 411 + </div> 412 + <div class="mt-2"> 413 + <span class="opacity-75">First hash (root):</span> <span class="font-mono text-xs">{ROOT.slice(0)}</span> 414 + </div> 415 + 416 + <div class="mt-6 opacity-50"> 417 + Last updated: {formatISO9075(lastUpdated)} 418 + </div> 419 + </div> 420 + <!--hr class="hr my-10" /--> 421 + <!--BundleDownloader instances={instances} /--> 422 + 423 + <hr class="hr mb-6 mt-12" /> 424 + <div class="opacity-50"> 425 + <div class="mt-4 text-sm"> 426 + <a href="https://tangled.org/@tree.fail/plcbundle-watch">Source Code</a> | โค๏ธ Made with love for <a href="https://atproto.com/">#atproto</a> community by <a href="https://bsky.app/profile/tree.fail">@tree.fail</a> using <a href="https://vite.dev/">Vite</a> & <a href="https://svelte.dev/">Svelte</a> 427 + </div> 127 428 </div> 429 + 430 + 128 431 </div> 129 432 </main> 130 433
+508
src/BundleDownloader.svelte
··· 1 + <script lang="ts"> 2 + import { Progress } from '@skeletonlabs/skeleton-svelte'; 3 + import { tick } from 'svelte'; 4 + 5 + type Instance = { 6 + url: string; 7 + name?: string; 8 + } 9 + 10 + type InstanceStatus = { 11 + url: string; 12 + lastBundle: number; 13 + } 14 + 15 + type DownloadedBundle = { 16 + number: number; 17 + status: 'downloading' | 'success' | 'error' | 'cancelled'; 18 + size?: number; 19 + error?: string; 20 + source?: string; 21 + } 22 + 23 + let { instances = [] }: { instances: Instance[] } = $props(); 24 + 25 + let selectedInstance = $state('random'); 26 + let bundlesInput = $state(''); 27 + let isDownloading = $state(false); 28 + let downloadedBundles = $state<DownloadedBundle[]>([]); 29 + let progress = $state(0); 30 + let totalBundles = $state(0); 31 + let abortController: AbortController | null = null; 32 + let isStopping = $state(false); 33 + let instanceStatuses = $state<InstanceStatus[]>([]); 34 + let useDirectory = $state(true); 35 + let directoryHandle: FileSystemDirectoryHandle | null = null; 36 + let hasFileSystemAccess = $state(false); 37 + 38 + // Check if File System Access API is available 39 + $effect(() => { 40 + hasFileSystemAccess = 'showDirectoryPicker' in window; 41 + if (!hasFileSystemAccess) { 42 + useDirectory = false; 43 + } 44 + }); 45 + 46 + async function pickDirectory(): Promise<boolean> { 47 + if (!hasFileSystemAccess) { 48 + return false; 49 + } 50 + 51 + try { 52 + directoryHandle = await (window as any).showDirectoryPicker({ 53 + mode: 'readwrite' 54 + }); 55 + return true; 56 + } catch (e) { 57 + if ((e as Error).name !== 'AbortError') { 58 + console.error('Failed to pick directory:', e); 59 + } 60 + return false; 61 + } 62 + } 63 + 64 + async function fetchInstanceStatuses() { 65 + const statuses: InstanceStatus[] = []; 66 + 67 + await Promise.all(instances.map(async (instance) => { 68 + try { 69 + const response = await fetch(`${instance.url}/status`, { 70 + signal: abortController?.signal 71 + }); 72 + const data = await response.json(); 73 + statuses.push({ 74 + url: instance.url, 75 + lastBundle: data.bundles.last_bundle 76 + }); 77 + } catch (e) { 78 + console.warn(`Failed to fetch status from ${instance.url}`, e); 79 + } 80 + })); 81 + 82 + return statuses; 83 + } 84 + 85 + function getAvailableInstancesForBundle(bundleNumber: number): string[] { 86 + return instanceStatuses 87 + .filter(s => s.lastBundle >= bundleNumber) 88 + .map(s => s.url); 89 + } 90 + 91 + function getRandomInstance(bundleNumber?: number): Instance | null { 92 + let availableUrls: string[]; 93 + 94 + if (bundleNumber !== undefined && instanceStatuses.length > 0) { 95 + availableUrls = getAvailableInstancesForBundle(bundleNumber); 96 + if (availableUrls.length === 0) { 97 + return null; 98 + } 99 + } else { 100 + availableUrls = instances.map(i => i.url); 101 + } 102 + 103 + const randomUrl = availableUrls[Math.floor(Math.random() * availableUrls.length)]; 104 + return instances.find(i => i.url === randomUrl) || null; 105 + } 106 + 107 + function getInstanceUrl(bundleNumber?: number): string | null { 108 + if (selectedInstance === 'random') { 109 + const instance = getRandomInstance(bundleNumber); 110 + return instance?.url || null; 111 + } 112 + return selectedInstance; 113 + } 114 + 115 + function getInstanceName(url: string): string { 116 + const instance = instances.find(i => i.url === url); 117 + return instance?.name || new URL(url).hostname; 118 + } 119 + 120 + function parseBundlesInput(input: string): number[] | 'all' { 121 + const trimmed = input.trim(); 122 + 123 + if (!trimmed) { 124 + return 'all'; 125 + } 126 + 127 + if (trimmed.includes('-')) { 128 + const [start, end] = trimmed.split('-').map(s => parseInt(s.trim())); 129 + if (isNaN(start) || isNaN(end) || start > end) { 130 + throw new Error('Invalid range format'); 131 + } 132 + const bundles = []; 133 + for (let i = start; i <= end; i++) { 134 + bundles.push(i); 135 + } 136 + return bundles; 137 + } 138 + 139 + const num = parseInt(trimmed); 140 + if (isNaN(num)) { 141 + throw new Error('Invalid bundle number'); 142 + } 143 + return [num]; 144 + } 145 + 146 + async function getLastBundle(instanceUrl: string): Promise<number> { 147 + const response = await fetch(`${instanceUrl}/status`, { 148 + signal: abortController?.signal 149 + }); 150 + const data = await response.json(); 151 + return data.bundles.last_bundle; 152 + } 153 + 154 + async function downloadBundle(instanceUrl: string, bundleNumber: number): Promise<Blob> { 155 + const response = await fetch(`${instanceUrl}/data/${bundleNumber}`, { 156 + signal: abortController?.signal 157 + }); 158 + if (!response.ok) { 159 + throw new Error(`HTTP ${response.status}`); 160 + } 161 + return await response.blob(); 162 + } 163 + 164 + function padBundleNumber(num: number): string { 165 + return num.toString().padStart(6, '0'); 166 + } 167 + 168 + async function saveFileToDirectory(blob: Blob, bundleNumber: number) { 169 + if (!directoryHandle) { 170 + throw new Error('No directory selected'); 171 + } 172 + 173 + const fileName = `${padBundleNumber(bundleNumber)}.jsonl.zst`; 174 + const fileHandle = await directoryHandle.getFileHandle(fileName, { create: true }); 175 + const writable = await fileHandle.createWritable(); 176 + await writable.write(blob); 177 + await writable.close(); 178 + } 179 + 180 + function saveFileBrowser(blob: Blob, bundleNumber: number) { 181 + const url = URL.createObjectURL(blob); 182 + const link = document.createElement('a'); 183 + link.href = url; 184 + link.download = `${padBundleNumber(bundleNumber)}.jsonl.zst`; 185 + link.click(); 186 + URL.revokeObjectURL(url); 187 + } 188 + 189 + function stopDownload() { 190 + if (abortController) { 191 + isStopping = true; 192 + abortController.abort(); 193 + } 194 + } 195 + 196 + async function handleDownload() { 197 + if (!selectedInstance) { 198 + alert('Please select an instance'); 199 + return; 200 + } 201 + 202 + // If using directory mode, pick directory first 203 + if (useDirectory && hasFileSystemAccess) { 204 + const picked = await pickDirectory(); 205 + if (!picked) { 206 + return; // User cancelled 207 + } 208 + } 209 + 210 + let bundleNumbers: number[]; 211 + abortController = new AbortController(); 212 + isStopping = false; 213 + 214 + try { 215 + if (selectedInstance === 'random') { 216 + instanceStatuses = await fetchInstanceStatuses(); 217 + if (instanceStatuses.length === 0) { 218 + alert('No instances available'); 219 + return; 220 + } 221 + } 222 + 223 + const parsed = parseBundlesInput(bundlesInput); 224 + 225 + if (parsed === 'all') { 226 + let lastBundle: number; 227 + 228 + if (selectedInstance === 'random') { 229 + lastBundle = Math.max(...instanceStatuses.map(s => s.lastBundle)); 230 + } else { 231 + lastBundle = await getLastBundle(selectedInstance); 232 + } 233 + 234 + bundleNumbers = []; 235 + for (let i = 1; i <= lastBundle; i++) { 236 + bundleNumbers.push(i); 237 + } 238 + } else { 239 + bundleNumbers = parsed; 240 + } 241 + } catch (e) { 242 + if (e instanceof Error && e.name === 'AbortError') { 243 + return; 244 + } 245 + alert(e instanceof Error ? e.message : 'Invalid input'); 246 + return; 247 + } 248 + 249 + isDownloading = true; 250 + downloadedBundles = []; 251 + progress = 0; 252 + totalBundles = bundleNumbers.length; 253 + 254 + for (let i = 0; i < bundleNumbers.length; i++) { 255 + if (abortController?.signal.aborted) { 256 + break; 257 + } 258 + 259 + const bundleNum = bundleNumbers[i]; 260 + const instanceUrl = getInstanceUrl(bundleNum); 261 + 262 + if (!instanceUrl) { 263 + downloadedBundles = [...downloadedBundles, { 264 + number: bundleNum, 265 + status: 'error', 266 + error: 'No instance has this bundle', 267 + }]; 268 + progress = Math.round(((i + 1) / totalBundles) * 100); 269 + await tick(); 270 + continue; 271 + } 272 + 273 + downloadedBundles = [...downloadedBundles, { 274 + number: bundleNum, 275 + status: 'downloading', 276 + source: instanceUrl, 277 + }]; 278 + 279 + await tick(); 280 + 281 + try { 282 + const blob = await downloadBundle(instanceUrl, bundleNum); 283 + 284 + if (abortController?.signal.aborted) { 285 + downloadedBundles = downloadedBundles.map(b => 286 + b.number === bundleNum ? { ...b, status: 'cancelled' as const } : b 287 + ); 288 + break; 289 + } 290 + 291 + // Save file 292 + if (useDirectory && directoryHandle) { 293 + await saveFileToDirectory(blob, bundleNum); 294 + } else { 295 + saveFileBrowser(blob, bundleNum); 296 + } 297 + 298 + downloadedBundles = downloadedBundles.map(b => 299 + b.number === bundleNum 300 + ? { ...b, status: 'success' as const, size: blob.size } 301 + : b 302 + ); 303 + 304 + } catch (e) { 305 + if (e instanceof Error && e.name === 'AbortError') { 306 + downloadedBundles = downloadedBundles.map(b => 307 + b.number === bundleNum ? { ...b, status: 'cancelled' as const } : b 308 + ); 309 + break; 310 + } 311 + 312 + downloadedBundles = downloadedBundles.map(b => 313 + b.number === bundleNum 314 + ? { ...b, status: 'error' as const, error: e instanceof Error ? e.message : 'Unknown error' } 315 + : b 316 + ); 317 + } 318 + 319 + progress = Math.round(((i + 1) / totalBundles) * 100); 320 + await tick(); 321 + } 322 + 323 + isDownloading = false; 324 + isStopping = false; 325 + abortController = null; 326 + directoryHandle = null; 327 + } 328 + 329 + function formatBytes(bytes: number): string { 330 + if (bytes === 0) return '0 B'; 331 + const k = 1024; 332 + const sizes = ['B', 'KB', 'MB', 'GB']; 333 + const i = Math.floor(Math.log(bytes) / Math.log(k)); 334 + return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; 335 + } 336 + 337 + function clearResults() { 338 + downloadedBundles = []; 339 + progress = 0; 340 + totalBundles = 0; 341 + } 342 + 343 + let successCount = $derived(downloadedBundles.filter(b => b.status === 'success').length); 344 + let errorCount = $derived(downloadedBundles.filter(b => b.status === 'error').length); 345 + let cancelledCount = $derived(downloadedBundles.filter(b => b.status === 'cancelled').length); 346 + let totalSize = $derived(downloadedBundles.reduce((sum, b) => sum + (b.size || 0), 0)); 347 + </script> 348 + 349 + <div class="bundle-downloader card space-y-4"> 350 + <h2 class="text-2xl">Bundle Downloader</h2> 351 + 352 + <div class="space-y-3"> 353 + <label class="label"> 354 + <span>Instance</span> 355 + <select 356 + class="select p-3 text-sm" 357 + bind:value={selectedInstance} 358 + disabled={isDownloading} 359 + > 360 + <option value="random">๐ŸŽฒ Random (each bundle from different source)</option> 361 + {#each instances as instance} 362 + <option value={instance.url}> 363 + {instance.name || instance.url} 364 + </option> 365 + {/each} 366 + </select> 367 + </label> 368 + 369 + <label class="label"> 370 + <span>Bundles</span> 371 + <input 372 + class="input text-sm" 373 + type="text" 374 + bind:value={bundlesInput} 375 + disabled={isDownloading} 376 + placeholder="empty = all, 5 = single, 1-10 = range" 377 + /> 378 + <p class="text-xs opacity-75 mt-1"> 379 + Leave empty for all bundles, enter a number (e.g., <code>5</code>) or range (e.g., <code>1-10</code>) 380 + </p> 381 + </label> 382 + 383 + {#if hasFileSystemAccess} 384 + <label class="flex items-center space-x-2"> 385 + <input 386 + type="checkbox" 387 + class="checkbox" 388 + bind:checked={useDirectory} 389 + disabled={isDownloading} 390 + /> 391 + <span class="text-sm"> 392 + ๐Ÿ“ Save to directory (recommended for multiple files) 393 + </span> 394 + </label> 395 + {:else} 396 + <div class="alert variant-ghost-warning p-2 text-xs"> 397 + <span>โš ๏ธ Directory mode not available in this browser. Files will download individually.</span> 398 + </div> 399 + {/if} 400 + 401 + <div class="flex gap-2"> 402 + {#if !isDownloading} 403 + <button 404 + class="btn preset-tonal-primary flex-1" 405 + onclick={handleDownload} 406 + > 407 + {useDirectory && hasFileSystemAccess ? '๐Ÿ“ Choose Directory & Download' : '๐Ÿ“ฅ Download'} 408 + </button> 409 + {#if downloadedBundles.length > 0} 410 + <button 411 + class="btn preset-tonal-surface" 412 + onclick={clearResults} 413 + > 414 + ๐Ÿ—‘๏ธ Clear 415 + </button> 416 + {/if} 417 + {:else} 418 + <button 419 + class="btn preset-filled-error-500 flex-1" 420 + onclick={stopDownload} 421 + disabled={isStopping} 422 + > 423 + {isStopping ? 'โณ Stopping...' : 'โ›” Stop'} 424 + </button> 425 + {/if} 426 + </div> 427 + 428 + {#if isDownloading} 429 + <div class="space-y-2"> 430 + <Progress value={progress} max={100} /> 431 + <p class="text-sm text-center font-semibold"> 432 + {progress}% ({successCount}/{totalBundles}) 433 + </p> 434 + </div> 435 + {/if} 436 + </div> 437 + 438 + {#if downloadedBundles.length > 0} 439 + <div class="space-y-2"> 440 + <h3 class="text-2xl"> 441 + {isDownloading ? 'Downloading...' : 'Results'} 442 + ({successCount}/{downloadedBundles.length}) 443 + </h3> 444 + 445 + <div class="table-container max-h-64 overflow-y-auto"> 446 + <table class="table table-compact table-hover"> 447 + <thead> 448 + <tr> 449 + <th>File</th> 450 + <th>Source</th> 451 + <th>Status</th> 452 + <th class="text-right">Size</th> 453 + </tr> 454 + </thead> 455 + <tbody> 456 + {#each downloadedBundles as bundle (bundle.number)} 457 + <tr> 458 + <td class="font-mono text-xs">{padBundleNumber(bundle.number)}.jsonl.zst</td> 459 + <td class="text-xs" title={bundle.source}> 460 + {bundle.source ? getInstanceName(bundle.source) : '-'} 461 + </td> 462 + <td> 463 + {#if bundle.status === 'downloading'} 464 + <span class="badge variant-filled text-xs">โณ Downloading</span> 465 + {:else if bundle.status === 'success'} 466 + <span class="badge variant-filled-success text-xs">โœ… Success</span> 467 + {:else if bundle.status === 'cancelled'} 468 + <span class="badge variant-filled-warning text-xs">โš ๏ธ Cancelled</span> 469 + {:else} 470 + <span class="badge variant-filled-error text-xs" title={bundle.error}>โŒ Error</span> 471 + {/if} 472 + </td> 473 + <td class="text-sm text-right">{bundle.size ? formatBytes(bundle.size) : '-'}</td> 474 + </tr> 475 + {/each} 476 + </tbody> 477 + </table> 478 + </div> 479 + 480 + <div class="card p-3 variant-ghost-surface grid grid-cols-4 gap-2 text-sm"> 481 + <div> 482 + <div class="font-bold text-success-500"> 483 + {successCount} 484 + </div> 485 + <div class="opacity-75">Success</div> 486 + </div> 487 + <div> 488 + <div class="font-bold text-error-500"> 489 + {errorCount} 490 + </div> 491 + <div class="opacity-75">Failed</div> 492 + </div> 493 + <div> 494 + <div class="font-bold text-warning-500"> 495 + {cancelledCount} 496 + </div> 497 + <div class="opacity-75">Cancelled</div> 498 + </div> 499 + <div> 500 + <div class="font-bold"> 501 + {formatBytes(totalSize)} 502 + </div> 503 + <div class="opacity-75">Total</div> 504 + </div> 505 + </div> 506 + </div> 507 + {/if} 508 + </div>
+10 -3
src/app.css
··· 5 5 @import '@skeletonlabs/skeleton/themes/cerberus'; 6 6 7 7 .table th { 8 - @apply text-xs opacity-50 font-light px-3 text-left; 8 + @apply text-xs opacity-50 font-light px-2 text-left; 9 9 } 10 10 11 11 .table td { 12 - @apply py-1.5 px-3; 12 + @apply py-1.5 px-2; 13 13 } 14 14 15 - a { 15 + a:not(.no-style) { 16 16 @apply underline hover:no-underline; 17 17 } 18 18 ··· 22 22 23 23 [data-theme='cerberus'] { 24 24 --text-scaling: 1.3; 25 + } 26 + 27 + .linear-text-gradient { 28 + background: linear-gradient(325deg, var(--color-surface-50), var(--color-primary-500)); 29 + -webkit-background-clip: text; 30 + background-clip: text; 31 + color: transparent; 25 32 }
+51
src/instances.json
··· 1 + [ 2 + { 3 + "url": "https://plcbundle.atscan.net", 4 + "country": "AT", 5 + "maintainer": "@tree.fail" 6 + }, 7 + { 8 + "url": "https://plcbundle2.atscan.net", 9 + "country": "CZ", 10 + "maintainer": "@tree.fail" 11 + }, 12 + { 13 + "url": "https://plcbundle3.atscan.net", 14 + "country": "CZ", 15 + "maintainer": "@tree.fail" 16 + }, 17 + { 18 + "url": "https://plc.j4ck.xyz", 19 + "country": "UK", 20 + "maintainer": "@j4ck.xyz" 21 + }, 22 + { 23 + "url": "https://plc.indexx.dev", 24 + "country": "US", 25 + "maintainer": "@indexx.dev" 26 + }, 27 + { 28 + "url": "https://plc.nyxt.dev" 29 + }, 30 + { 31 + "url": "https://plc.madebydanny.uk", 32 + "country": "US", 33 + "maintainer": "@madebydanny.uk" 34 + }, 35 + { 36 + "url": "https://plc.tartarus.us" 37 + }, 38 + { 39 + "url": "https://plcbundle.snek.cc", 40 + "country": "US", 41 + "maintainer": "@jackvalinsky.com" 42 + }, 43 + { 44 + "url": "https://plc.dane.computer", 45 + "country": "CA", 46 + "maintainer": "@dane.is.extraordinarily.cool" 47 + }, 48 + { 49 + "url": "https://plc.witchcraft.systems" 50 + } 51 + ]
+53
src/lib/utils.ts
··· 1 + import numeral from 'numeral'; 2 + 3 + export function formatNumber(n: number) { 4 + return numeral(n).format() 5 + } 6 + 7 + export function formatUptime(totalSeconds: number): string { 8 + if (totalSeconds < 0) { 9 + return "Invalid input: seconds cannot be negative"; 10 + } 11 + if (totalSeconds === 0) { 12 + return "0s"; 13 + } 14 + 15 + let parts: string[] = []; 16 + let displayUnits: [string, number][]; 17 + 18 + const SECONDS_IN_MINUTE = 60; 19 + const SECONDS_IN_HOUR = 3600; // 60 minutes * 60 seconds/minute 20 + const SECONDS_IN_DAY = 24 * SECONDS_IN_HOUR; 21 + 22 + if (totalSeconds > SECONDS_IN_DAY) { // If uptime is bigger than 1 day 23 + displayUnits = [ 24 + ['d', SECONDS_IN_DAY], 25 + ['h', SECONDS_IN_HOUR], 26 + ]; 27 + } else if (totalSeconds > SECONDS_IN_MINUTE) { // If uptime is bigger than 60 seconds but not bigger than 60 minutes 28 + displayUnits = [ 29 + ['d', SECONDS_IN_DAY], 30 + ['h', SECONDS_IN_HOUR], 31 + ['m', SECONDS_IN_MINUTE], 32 + ]; 33 + } else { // If uptime is 60 seconds or less 34 + displayUnits = [ 35 + ['d', SECONDS_IN_DAY], 36 + ['h', SECONDS_IN_HOUR], 37 + ['m', SECONDS_IN_MINUTE], 38 + ['s', 1], 39 + ]; 40 + } 41 + 42 + let remainingSeconds = totalSeconds; 43 + 44 + for (const [unitChar, unitSeconds] of displayUnits) { 45 + if (remainingSeconds >= unitSeconds) { 46 + const value = Math.floor(remainingSeconds / unitSeconds); 47 + parts.push(`${value}${unitChar}`); 48 + remainingSeconds %= unitSeconds; 49 + } 50 + } 51 + 52 + return parts.join(""); 53 + }
+1
src/types/lodash.d.ts
··· 1 + declare module 'lodash/orderBy';