schoolbox web extension :)

Compare changes

Choose any two refs to compare.

+7
.env.example
···
··· 1 + # .env file is optional to automatically open an authenticated schoolbox session during development 2 + 3 + # base URL, excluding trailing slashes 4 + WXT_SCHOOLBOX_URL="https://help.schoolbox.com.au" 5 + 6 + # JSON web token, found at `base_url/user/token` 7 + WXT_SCHOOLBOX_JWT="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNjaG9vbGJveCJ9..."
+2 -2
.github/workflows/build.yml
··· 13 runs-on: ubuntu-latest 14 steps: 15 - name: Checkout 16 - uses: actions/checkout@v4 17 with: 18 fetch-depth: 0 19 submodules: "recursive" ··· 30 bun zip:firefox 31 32 - name: Upload artifacts 33 - uses: actions/upload-artifact@v4 34 with: 35 name: extension 36 path: dist/*.zip
··· 13 runs-on: ubuntu-latest 14 steps: 15 - name: Checkout 16 + uses: actions/checkout@v6 17 with: 18 fetch-depth: 0 19 submodules: "recursive" ··· 30 bun zip:firefox 31 32 - name: Upload artifacts 33 + uses: actions/upload-artifact@v5 34 with: 35 name: extension 36 path: dist/*.zip
+29
.github/workflows/check.yml
···
··· 1 + name: Check 2 + 3 + on: 4 + workflow_dispatch: 5 + push: 6 + pull_request: 7 + 8 + permissions: 9 + contents: read 10 + pull-requests: read 11 + 12 + jobs: 13 + build: 14 + runs-on: ubuntu-latest 15 + steps: 16 + - name: Checkout 17 + uses: actions/checkout@v6 18 + with: 19 + fetch-depth: 0 20 + submodules: "recursive" 21 + 22 + - name: Setup bun 23 + uses: oven-sh/setup-bun@v2 24 + 25 + - name: Install dependencies 26 + run: bun install --frozen-lockfile 27 + 28 + - name: Run checks 29 + run: bun run check
+29
.github/workflows/lint.yml
···
··· 1 + name: Lint 2 + 3 + on: 4 + workflow_dispatch: 5 + push: 6 + pull_request: 7 + 8 + permissions: 9 + contents: read 10 + pull-requests: read 11 + 12 + jobs: 13 + build: 14 + runs-on: ubuntu-latest 15 + steps: 16 + - name: Checkout 17 + uses: actions/checkout@v6 18 + with: 19 + fetch-depth: 0 20 + submodules: "recursive" 21 + 22 + - name: Setup bun 23 + uses: oven-sh/setup-bun@v2 24 + 25 + - name: Install dependencies 26 + run: bun install --frozen-lockfile 27 + 28 + - name: Run eslint 29 + run: bun run lint
+1 -1
.github/workflows/release-please.yml
··· 27 if: ${{ needs.release-please.outputs.release_created }} 28 steps: 29 - name: Checkout 30 - uses: actions/checkout@v4 31 with: 32 submodules: recursive 33 fetch-depth: 0
··· 27 if: ${{ needs.release-please.outputs.release_created }} 28 steps: 29 - name: Checkout 30 + uses: actions/checkout@v6 31 with: 32 submodules: recursive 33 fetch-depth: 0
+2 -3
.gitignore
··· 1 - secrets.md 2 - 3 # Logs 4 logs 5 *.log ··· 15 stats-*.json 16 .wxt 17 web-ext.config.ts 18 - dist/ 19 20 # Editor directories and files 21 .vscode/*
··· 1 # Logs 2 logs 3 *.log ··· 13 stats-*.json 14 .wxt 15 web-ext.config.ts 16 + dist 17 + .env 18 19 # Editor directories and files 20 .vscode/*
+1 -1
.prettierrc.json
··· 3 "bracketSameLine": true, 4 "plugins": ["prettier-plugin-svelte", "prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"], 5 "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], 6 - "tailwindStylesheet": "./src/entrypoints/app.css" 7 }
··· 3 "bracketSameLine": true, 4 "plugins": ["prettier-plugin-svelte", "prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"], 5 "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], 6 + "tailwindStylesheet": "./src/entrypoints/popup/app.css" 7 }
+68
CHANGELOG.md
··· 1 # Changelog 2 3 ## [4.2.0](https://github.com/schooltape/schooltape/compare/v4.1.4...v4.2.0) (2025-10-29) 4 5
··· 1 # Changelog 2 3 + ## [4.3.4](https://github.com/schooltape/schooltape/compare/v4.3.3...v4.3.4) (2025-12-15) 4 + 5 + 6 + ### Bug Fixes 7 + 8 + * **plugins/homepageSwitcher:** closing tabs ([3db3c02](https://github.com/schooltape/schooltape/commit/3db3c021a51f8e2401c2c6f0f838b3065a723876)) 9 + 10 + ## [4.3.3](https://github.com/schooltape/schooltape/compare/v4.3.2...v4.3.3) (2025-12-12) 11 + 12 + 13 + ### Bug Fixes 14 + 15 + * **plugins:** settings menus not resolving ([6378de9](https://github.com/schooltape/schooltape/commit/6378de946052ae2c927e8ed9dbfeb6d384f61ec3)) 16 + 17 + ## [4.3.2](https://github.com/schooltape/schooltape/compare/v4.3.1...v4.3.2) (2025-12-12) 18 + 19 + 20 + ### Bug Fixes 21 + 22 + * **hot reload:** injecting on external sites ([9bca599](https://github.com/schooltape/schooltape/commit/9bca5997e13289957abbd2b718a43ed97831d473)) 23 + * **popup:** make modal full width ([4737a36](https://github.com/schooltape/schooltape/commit/4737a368d757068b7fa66af9d54e574831891444)) 24 + * **subheader:** add missing description to toggle ([3ab0081](https://github.com/schooltape/schooltape/commit/3ab0081a5aad91e3c9322ef62e85ff8fdcdfedf8)) 25 + 26 + ## [4.3.1](https://github.com/schooltape/schooltape/compare/v4.3.0...v4.3.1) (2025-12-10) 27 + 28 + 29 + ### Bug Fixes 30 + 31 + * **hot reload:** misc. bugs and race conditions ([dd89b3c](https://github.com/schooltape/schooltape/commit/dd89b3cc2dce2d524ce0885e28fdf38c8c6903ad)) 32 + 33 + ## [4.3.0](https://github.com/schooltape/schooltape/compare/v4.2.0...v4.3.0) (2025-12-10) 34 + 35 + 36 + ### Features 37 + 38 + * hot reload ([830b724](https://github.com/schooltape/schooltape/commit/830b7247119a8e4c79b4fcd7c66d64e3ef305363)) 39 + * partial user snippet and theme hot reload ([c7ed1d7](https://github.com/schooltape/schooltape/commit/c7ed1d745326f66d86f0d7ed1b74400cd562fa71)) 40 + * **plugins/changeIcon:** init ([46001a2](https://github.com/schooltape/schooltape/commit/46001a2c3eacd73d596966e8fa7373fe00ef30fb)) 41 + * **plugins/homepageSwitcher:** hot reload ([134e092](https://github.com/schooltape/schooltape/commit/134e0920717b8d326f00ebf84e22402f9c44d012)) 42 + * **plugins/modernIcons:** hot reload ([1ca8ac9](https://github.com/schooltape/schooltape/commit/1ca8ac9c6be9b1a68852f79bc555245ff7ec1660)) 43 + * **plugins/progressBar:** hot reload ([7528f35](https://github.com/schooltape/schooltape/commit/7528f353bc35332a6b737bc9bc2401d94def8e47)) 44 + * **plugins/scrollPeriod:** hot reload ([094ff68](https://github.com/schooltape/schooltape/commit/094ff682b4cc1995e4b68c582539f84961c96fc4)) 45 + * **plugins/scrollSegments:** hot reload ([5881f37](https://github.com/schooltape/schooltape/commit/5881f37931ce3ac56f1bd56f29ddecd8e6582cd6)) 46 + * **plugins/subheader:** hot reload ([d0a8e39](https://github.com/schooltape/schooltape/commit/d0a8e392c12a899afe34c3a3f7d54bc92934941f)) 47 + * **plugins/tabTitle:** hot reload ([0cc6515](https://github.com/schooltape/schooltape/commit/0cc6515f1c3ac6b56ab6714e72a88878c77b85c7)) 48 + * **plugins:** partial hot reload ([2853012](https://github.com/schooltape/schooltape/commit/28530125676550606659eebf29f68eff2091aadd)) 49 + * remove reload banner ([e209046](https://github.com/schooltape/schooltape/commit/e20904617de8252905de636036758185d2776110)) 50 + * remove schooltape from footer ([9187293](https://github.com/schooltape/schooltape/commit/918729375f7d0a5d117ffd29a5821324d44697d4)) 51 + * **themes:** hot reload ([bd13a9f](https://github.com/schooltape/schooltape/commit/bd13a9fb2a3d41a46dc1c71e3c62095e18858641)) 52 + 53 + 54 + ### Bug Fixes 55 + 56 + * allElementsPresent fn call and watchers ([dcef77c](https://github.com/schooltape/schooltape/commit/dcef77c711c706e9799348f143f40e8be6b4186e)) 57 + * **hot reload:** don't inject plugins if required elements are not ([9036ba9](https://github.com/schooltape/schooltape/commit/9036ba9298dc0e481d48f048ebeec34734ff83c1)) 58 + * **plugins/progressBar:** hot reload ([c745024](https://github.com/schooltape/schooltape/commit/c745024c63e9e5ba31e4afaf57b851ec8088509a)) 59 + * **plugins/scrollPeriod:** updateScrollbar fn call ([066654d](https://github.com/schooltape/schooltape/commit/066654d5e91e4050ebaeb0f63417f012f894d2b0)) 60 + * **popup:** grammar of plugins and snippets ([5f2720f](https://github.com/schooltape/schooltape/commit/5f2720f107312ffec289e55f67d255dfe060cc29)) 61 + * remove unused ignores and error handling ([687950a](https://github.com/schooltape/schooltape/commit/687950a40518dab92c9682d9f3bd70e1d9103311)) 62 + * **storage:** closeCurrentTab fallback regression ([0f55e81](https://github.com/schooltape/schooltape/commit/0f55e81e66d23f1d73ccadbc982ce13130b38e0d)) 63 + * **themes:** hot reload not working when disabled -> enabled ([73dc66d](https://github.com/schooltape/schooltape/commit/73dc66df365ebe993d7d85de327981419a5a7e0e)) 64 + * **utils:** add `dataAttr` in query selectors ([267ce51](https://github.com/schooltape/schooltape/commit/267ce511e5bf23dd81fd53e966ee22f9940b07b7)) 65 + 66 + 67 + ### Reverts 68 + 69 + * "feat(plugins/changeIcon): init" ([bf5d50a](https://github.com/schooltape/schooltape/commit/bf5d50a218e8da7db78c9849ab88506c9fadfe26)) 70 + 71 ## [4.2.0](https://github.com/schooltape/schooltape/compare/v4.1.4...v4.2.0) (2025-10-29) 72 73
+230 -102
bun.lock
··· 6 "devDependencies": { 7 "@catppuccin/palette": "^1.7.1", 8 "@catppuccin/tailwindcss": "1.0.0", 9 - "@eslint/compat": "^1.4.1", 10 "@eslint/js": "^9.39.1", 11 - "@lucide/svelte": "^0.544.0", 12 - "@sveltejs/vite-plugin-svelte": "^5.1.1", 13 "@tailwindcss/vite": "^4.1.17", 14 "@tsconfig/svelte": "^5.0.6", 15 - "@types/node": "^22.19.1", 16 "@types/semver": "^7.7.1", 17 "@wxt-dev/module-svelte": "^2.0.4", 18 "eslint": "^9.39.1", 19 "eslint-config-prettier": "^10.1.8", 20 "eslint-plugin-svelte": "^3.13.0", 21 "globals": "^16.5.0", 22 - "pino": "^9.14.0", 23 - "postcss": "^8.5.6", 24 "prettier": "^3.6.2", 25 "prettier-plugin-organize-imports": "^4.3.0", 26 "prettier-plugin-svelte": "^3.4.0", 27 - "prettier-plugin-tailwindcss": "^0.6.14", 28 "semver": "^7.7.3", 29 - "svelte": "^5.43.6", 30 "svelte-check": "^4.3.4", 31 "svelte-spa-router": "^4.0.1", 32 "tailwindcss": "^4.1.17", 33 "typescript": "^5.9.3", 34 - "typescript-eslint": "^8.46.4", 35 "wxt": "^0.20.11", 36 }, 37 }, ··· 63 64 "@devicefarmer/adbkit-monkey": ["@devicefarmer/adbkit-monkey@1.2.1", "", {}, "sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg=="], 65 66 - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], 67 68 - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="], 69 70 - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="], 71 72 - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="], 73 74 - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="], 75 76 - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="], 77 78 - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="], 79 80 - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="], 81 82 - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="], 83 84 - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="], 85 86 - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="], 87 88 - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="], 89 90 - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="], 91 92 - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="], 93 94 - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="], 95 96 - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="], 97 98 - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="], 99 100 - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="], 101 102 - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="], 103 104 - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="], 105 106 - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="], 107 108 - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="], 109 110 - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="], 111 112 - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="], 113 114 - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="], 115 116 "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], 117 118 "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], 119 120 - "@eslint/compat": ["@eslint/compat@1.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0" }, "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w=="], 121 122 "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], 123 124 "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], 125 126 - "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 127 128 "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], 129 130 - "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], 131 132 "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], 133 ··· 153 154 "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 155 156 - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 157 158 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 159 160 - "@lucide/svelte": ["@lucide/svelte@0.544.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-9f9O6uxng2pLB01sxNySHduJN3HTl5p0HDu4H26VR51vhZfiMzyOMe9Mhof3XAk4l813eTtl+/DYRvGyoRR+yw=="], 161 162 "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 163 ··· 165 166 "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 167 168 - "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], 169 - 170 "@pnpm/config.env-replace": ["@pnpm/config.env-replace@1.1.0", "", {}, "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="], 171 172 "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="], ··· 193 194 "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="], 195 196 "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="], 197 198 "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="], 199 200 "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="], 201 202 "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="], ··· 206 "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="], 207 208 "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="], 209 210 "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="], 211 212 "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="], 213 214 "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="], 215 216 "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], 217 218 - "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="], 219 220 - "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="], 221 222 - "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "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.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="], 223 224 - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], 225 226 - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="], 227 228 - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="], 229 230 - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="], 231 232 - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="], 233 234 - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="], 235 236 - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="], 237 238 - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="], 239 240 - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="], 241 242 - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="], 243 244 - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="], 245 246 - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="], 247 248 - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], 249 250 - "@tailwindcss/vite": ["@tailwindcss/vite@4.1.17", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "tailwindcss": "4.1.17" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA=="], 251 252 "@tsconfig/svelte": ["@tsconfig/svelte@5.0.6", "", {}, "sha512-yGxYL0I9eETH1/DR9qVJey4DAsCdeau4a9wYPKuXfEhm8lFO8wg+LLYJjIpAm6Fw7HSlhepPhYPDop75485yWQ=="], 253 ··· 263 264 "@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="], 265 266 - "@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], 267 268 "@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="], 269 270 - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.4", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/type-utils": "8.46.4", "@typescript-eslint/utils": "8.46.4", "@typescript-eslint/visitor-keys": "8.46.4", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.4", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg=="], 271 272 - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.4", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/types": "8.46.4", "@typescript-eslint/typescript-estree": "8.46.4", "@typescript-eslint/visitor-keys": "8.46.4", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w=="], 273 274 - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.4", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.4", "@typescript-eslint/types": "^8.46.4", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ=="], 275 276 - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.4", "", { "dependencies": { "@typescript-eslint/types": "8.46.4", "@typescript-eslint/visitor-keys": "8.46.4" } }, "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA=="], 277 278 - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.4", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A=="], 279 280 - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.4", "", { "dependencies": { "@typescript-eslint/types": "8.46.4", "@typescript-eslint/typescript-estree": "8.46.4", "@typescript-eslint/utils": "8.46.4", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ=="], 281 282 - "@typescript-eslint/types": ["@typescript-eslint/types@8.46.4", "", {}, "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w=="], 283 284 - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.4", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.4", "@typescript-eslint/tsconfig-utils": "8.46.4", "@typescript-eslint/types": "8.46.4", "@typescript-eslint/visitor-keys": "8.46.4", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA=="], 285 286 - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/types": "8.46.4", "@typescript-eslint/typescript-estree": "8.46.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg=="], 287 288 - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.4", "", { "dependencies": { "@typescript-eslint/types": "8.46.4", "eslint-visitor-keys": "^4.2.1" } }, "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw=="], 289 290 "@webext-core/fake-browser": ["@webext-core/fake-browser@1.3.2", "", { "dependencies": { "lodash.merge": "^4.6.2" } }, "sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A=="], 291 ··· 293 294 "@webext-core/match-patterns": ["@webext-core/match-patterns@1.0.3", "", {}, "sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A=="], 295 296 - "@wxt-dev/browser": ["@wxt-dev/browser@0.1.4", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, "sha512-9x03I15i79XU8qYwjv4le0K2HdMl/Yga2wUBSoUbcrCnamv8P3nvuYxREQ9C5QY/qPAfeEVdAtaTrS3KWak71g=="], 297 298 "@wxt-dev/module-svelte": ["@wxt-dev/module-svelte@2.0.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0" }, "peerDependencies": { "svelte": ">=5", "wxt": ">=0.18.6" } }, "sha512-zYrzhoaRZsudPDQE2Yb4AOLwZXD4fXCOf+/ud7tvFnAJJ71aFtvZ5OuW/U2oBBYXdAtm+S2erFN2L0Re79x6ZA=="], 299 ··· 435 436 "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 437 438 "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], 439 440 "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], ··· 463 464 "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], 465 466 - "esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], 467 468 "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 469 ··· 471 472 "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 473 474 - "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], 475 476 "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], 477 478 - "eslint-plugin-svelte": ["eslint-plugin-svelte@3.13.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg=="], 479 480 "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], 481 ··· 487 488 "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], 489 490 - "esrap": ["esrap@2.1.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg=="], 491 492 "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], 493 ··· 559 560 "graceful-readlink": ["graceful-readlink@1.0.1", "", {}, "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w=="], 561 562 - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], 563 - 564 "growly": ["growly@1.3.0", "", {}, "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw=="], 565 566 "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], ··· 711 712 "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], 713 714 - "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 715 716 "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], 717 ··· 756 "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], 757 758 "nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="], 759 760 "ofetch": ["ofetch@1.4.1", "", { "dependencies": { "destr": "^2.0.3", "node-fetch-native": "^1.6.4", "ufo": "^1.5.4" } }, "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw=="], 761 ··· 797 798 "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], 799 800 - "pino": ["pino@9.14.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w=="], 801 802 "pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="], 803 ··· 805 806 "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], 807 808 - "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=="], 809 810 "postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], 811 ··· 817 818 "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 819 820 - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], 821 822 "prettier-plugin-organize-imports": ["prettier-plugin-organize-imports@4.3.0", "", { "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", "vue-tsc": "^2.1.0 || 3" }, "optionalPeers": ["vue-tsc"] }, "sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw=="], 823 824 - "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="], 825 826 - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg=="], 827 828 "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], 829 ··· 941 942 "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 943 944 - "svelte": ["svelte@5.43.6", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-RnyO9VXI85Bmsf4b8AuQFBKFYL3LKUl+ZrifOjvlrQoboAROj5IITVLK1yOXBjwUWUn2BI5cfmurktgCzuZ5QA=="], 945 946 "svelte-check": ["svelte-check@4.3.4", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw=="], 947 ··· 949 950 "svelte-spa-router": ["svelte-spa-router@4.0.1", "", { "dependencies": { "regexparam": "2.0.2" } }, "sha512-2JkmUQ2f9jRluijL58LtdQBIpynSbem2eBGp4zXdi7aDY1znbR6yjw0KsonD0aq2QLwf4Yx4tBJQjxIjgjXHKg=="], 951 952 - "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], 953 954 "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], 955 ··· 977 978 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 979 980 - "typescript-eslint": ["typescript-eslint@8.46.4", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.4", "@typescript-eslint/parser": "8.46.4", "@typescript-eslint/typescript-estree": "8.46.4", "@typescript-eslint/utils": "8.46.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg=="], 981 982 "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], 983 984 "uhyphen": ["uhyphen@0.2.0", "", {}, "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="], 985 986 - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 987 988 "unimport": ["unimport@5.0.1", "", { "dependencies": { "acorn": "^8.14.1", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.1", "magic-string": "^0.30.17", "mlly": "^1.7.4", "pathe": "^2.0.3", "picomatch": "^4.0.2", "pkg-types": "^2.1.0", "scule": "^1.3.0", "strip-literal": "^3.0.0", "tinyglobby": "^0.2.13", "unplugin": "^2.3.2", "unplugin-utils": "^0.2.4" } }, "sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ=="], 989 ··· 1003 1004 "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], 1005 1006 - "vite-node": ["vite-node@3.1.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA=="], 1007 1008 - "vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="], 1009 1010 "watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="], 1011 ··· 1029 1030 "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], 1031 1032 - "wxt": ["wxt@0.20.11", "", { "dependencies": { "@1natsu/wait-element": "^4.1.2", "@aklinker1/rollup-plugin-visualizer": "5.12.0", "@webext-core/fake-browser": "^1.3.2", "@webext-core/isolated-element": "^1.1.2", "@webext-core/match-patterns": "^1.0.3", "@wxt-dev/browser": "^0.1.4", "@wxt-dev/storage": "^1.0.0", "async-mutex": "^0.5.0", "c12": "^3.2.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "ci-info": "^4.3.0", "consola": "^3.4.2", "defu": "^6.1.4", "dotenv": "^17.2.2", "dotenv-expand": "^12.0.3", "esbuild": "^0.25.0", "fast-glob": "^3.3.3", "filesize": "^11.0.2", "fs-extra": "^11.3.1", "get-port-please": "^3.2.0", "giget": "^1.2.3 || ^2.0.0", "hookable": "^5.5.3", "import-meta-resolve": "^4.2.0", "is-wsl": "^3.1.0", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.12", "magicast": "^0.3.5", "minimatch": "^10.0.3", "nano-spawn": "^1.0.2", "normalize-path": "^3.0.0", "nypm": "^0.6.1", "ohash": "^2.0.11", "open": "^10.2.0", "ora": "^8.2.0", "perfect-debounce": "^2.0.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "publish-browser-extension": "^2.3.0 || ^3.0.2", "scule": "^1.3.0", "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0", "vite": "^5.4.19 || ^6.3.4 || ^7.0.0", "vite-node": "^2.1.4 || ^3.1.2", "web-ext-run": "^0.2.4" }, "bin": { "wxt": "bin/wxt.mjs", "wxt-publish-extension": "bin/wxt-publish-extension.cjs" } }, "sha512-DqqHc/5COs8GR21ii99bANXf/mu6zuDpiXFV1YKNsqO5/PvkrCx5arY0aVPL5IATsuacAnNzdj4eMc1qbzS53Q=="], 1033 1034 "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="], 1035 ··· 1063 1064 "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 1065 1066 "@eslint/eslintrc/espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="], 1067 1068 "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], 1069 1070 "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], 1071 1072 "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], 1073 1074 "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 1075 1076 "@tailwindcss/node/lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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=="], 1077 - 1078 - "@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 1079 1080 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], 1081 ··· 1083 1084 "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], 1085 1086 - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], 1087 1088 "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 1089 ··· 1093 1094 "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 1095 1096 - "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 1097 1098 "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 1099 ··· 1102 "boxen/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], 1103 1104 "c12/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 1105 1106 "chrome-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], 1107 ··· 1119 1120 "dotenv-expand/dotenv": ["dotenv@16.5.0", "", {}, "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="], 1121 1122 - "eslint-plugin-svelte/postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 1123 - 1124 - "eslint-plugin-svelte/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 1125 1126 "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1127 ··· 1173 1174 "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 1175 1176 - "svelte-eslint-parser/postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 1177 - 1178 "unimport/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], 1179 1180 "unimport/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 1181 1182 "unimport/pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], 1183 ··· 1187 1188 "update-notifier/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 1189 1190 - "vite/postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 1191 1192 - "web-ext-run/pino": ["pino@9.7.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg=="], 1193 1194 "web-ext-run/strip-json-comments": ["strip-json-comments@5.0.2", "", {}, "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g=="], 1195 ··· 1227 1228 "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], 1229 1230 - "@tailwindcss/node/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 1231 1232 - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], 1233 1234 "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1235 1236 "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1237 1238 "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1239 1240 "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], ··· 1255 1256 "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], 1257 1258 "unimport/pkg-types/exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], 1259 1260 "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1261 1262 "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1263 1264 "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1265 1266 "giget/nypm/pkg-types/exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], 1267 1268 "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1269 }
··· 6 "devDependencies": { 7 "@catppuccin/palette": "^1.7.1", 8 "@catppuccin/tailwindcss": "1.0.0", 9 + "@eslint/compat": "^2.0.0", 10 "@eslint/js": "^9.39.1", 11 + "@lucide/svelte": "^0.554.0", 12 + "@sveltejs/vite-plugin-svelte": "^6.2.1", 13 "@tailwindcss/vite": "^4.1.17", 14 "@tsconfig/svelte": "^5.0.6", 15 + "@types/node": "^24.10.1", 16 "@types/semver": "^7.7.1", 17 "@wxt-dev/module-svelte": "^2.0.4", 18 "eslint": "^9.39.1", 19 "eslint-config-prettier": "^10.1.8", 20 "eslint-plugin-svelte": "^3.13.0", 21 "globals": "^16.5.0", 22 "prettier": "^3.6.2", 23 "prettier-plugin-organize-imports": "^4.3.0", 24 "prettier-plugin-svelte": "^3.4.0", 25 + "prettier-plugin-tailwindcss": "^0.7.1", 26 "semver": "^7.7.3", 27 + "svelte": "^5.43.10", 28 "svelte-check": "^4.3.4", 29 "svelte-spa-router": "^4.0.1", 30 "tailwindcss": "^4.1.17", 31 "typescript": "^5.9.3", 32 + "typescript-eslint": "^8.47.0", 33 "wxt": "^0.20.11", 34 }, 35 }, ··· 61 62 "@devicefarmer/adbkit-monkey": ["@devicefarmer/adbkit-monkey@1.2.1", "", {}, "sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg=="], 63 64 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], 65 66 + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], 67 68 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], 69 70 + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], 71 72 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], 73 + 74 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], 75 76 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], 77 78 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], 79 80 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], 81 82 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], 83 84 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], 85 86 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], 87 88 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], 89 90 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], 91 92 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], 93 94 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], 95 96 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], 97 98 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], 99 100 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], 101 102 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], 103 104 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], 105 106 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], 107 108 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], 109 110 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], 111 112 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], 113 114 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], 115 116 "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], 117 118 "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], 119 120 + "@eslint/compat": ["@eslint/compat@2.0.0", "", { "dependencies": { "@eslint/core": "^1.0.0" }, "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA=="], 121 122 "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], 123 124 "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], 125 126 + "@eslint/core": ["@eslint/core@1.0.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw=="], 127 128 "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], 129 130 + "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], 131 132 "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], 133 ··· 153 154 "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 155 156 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 157 158 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 159 160 + "@lucide/svelte": ["@lucide/svelte@0.554.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-CM6wLEH8uk3WBpC42t8R0hF7SlQrsYEL6qGuXdB99xKZwKglpWmX5XgYu7FIYOCBYOyC1rm4dNhIe6uF9pOXqw=="], 161 162 "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 163 ··· 165 166 "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 167 168 "@pnpm/config.env-replace": ["@pnpm/config.env-replace@1.1.0", "", {}, "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="], 169 170 "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="], ··· 191 192 "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="], 193 194 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.5", "", { "os": "linux", "cpu": "none" }, "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA=="], 195 + 196 "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="], 197 198 "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="], 199 200 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q=="], 201 + 202 "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="], 203 204 "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="], ··· 208 "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="], 209 210 "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="], 211 + 212 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.5", "", { "os": "none", "cpu": "arm64" }, "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg=="], 213 214 "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="], 215 216 "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="], 217 218 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.5", "", { "os": "win32", "cpu": "x64" }, "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A=="], 219 + 220 "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="], 221 222 "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], 223 224 + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], 225 226 + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], 227 228 + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "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.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], 229 230 + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], 231 232 + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], 233 234 + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], 235 236 + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], 237 238 + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], 239 240 + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], 241 242 + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], 243 244 + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], 245 246 + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], 247 248 + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], 249 250 + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], 251 252 + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], 253 254 + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], 255 256 + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.18", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="], 257 258 "@tsconfig/svelte": ["@tsconfig/svelte@5.0.6", "", {}, "sha512-yGxYL0I9eETH1/DR9qVJey4DAsCdeau4a9wYPKuXfEhm8lFO8wg+LLYJjIpAm6Fw7HSlhepPhYPDop75485yWQ=="], 259 ··· 269 270 "@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="], 271 272 + "@types/node": ["@types/node@24.10.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg=="], 273 274 "@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="], 275 276 + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.50.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/type-utils": "8.50.0", "@typescript-eslint/utils": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.50.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg=="], 277 278 + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.50.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q=="], 279 280 + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.50.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.50.0", "@typescript-eslint/types": "^8.50.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ=="], 281 282 + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0" } }, "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A=="], 283 284 + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.50.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w=="], 285 286 + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/utils": "8.50.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw=="], 287 288 + "@typescript-eslint/types": ["@typescript-eslint/types@8.50.0", "", {}, "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w=="], 289 290 + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.50.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.50.0", "@typescript-eslint/tsconfig-utils": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ=="], 291 292 + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.50.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg=="], 293 294 + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q=="], 295 296 "@webext-core/fake-browser": ["@webext-core/fake-browser@1.3.2", "", { "dependencies": { "lodash.merge": "^4.6.2" } }, "sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A=="], 297 ··· 299 300 "@webext-core/match-patterns": ["@webext-core/match-patterns@1.0.3", "", {}, "sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A=="], 301 302 + "@wxt-dev/browser": ["@wxt-dev/browser@0.1.32", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, "sha512-jvfSppeLzlH4sOkIvMBJoA1pKoI+U5gTkjDwMKdkTWh0P/fj+KDyze3lzo3S6372viCm8tXUKNez+VKyVz2ZDw=="], 303 304 "@wxt-dev/module-svelte": ["@wxt-dev/module-svelte@2.0.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0" }, "peerDependencies": { "svelte": ">=5", "wxt": ">=0.18.6" } }, "sha512-zYrzhoaRZsudPDQE2Yb4AOLwZXD4fXCOf+/ud7tvFnAJJ71aFtvZ5OuW/U2oBBYXdAtm+S2erFN2L0Re79x6ZA=="], 305 ··· 441 442 "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 443 444 + "devalue": ["devalue@5.6.1", "", {}, "sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A=="], 445 + 446 "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], 447 448 "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], ··· 471 472 "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], 473 474 + "esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], 475 476 "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 477 ··· 479 480 "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 481 482 + "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], 483 484 "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], 485 486 + "eslint-plugin-svelte": ["eslint-plugin-svelte@3.13.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-Ng+kV/qGS8P/isbNYVE3sJORtubB+yLEcYICMkUWNaDTb0SwZni/JhAYXh/Dz/q2eThUwWY0VMPZ//KYD1n3eQ=="], 487 488 "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], 489 ··· 495 496 "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], 497 498 + "esrap": ["esrap@2.2.1", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg=="], 499 500 "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], 501 ··· 567 568 "graceful-readlink": ["graceful-readlink@1.0.1", "", {}, "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w=="], 569 570 "growly": ["growly@1.3.0", "", {}, "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw=="], 571 572 "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], ··· 717 718 "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], 719 720 + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 721 722 "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], 723 ··· 762 "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], 763 764 "nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="], 765 + 766 + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], 767 768 "ofetch": ["ofetch@1.4.1", "", { "dependencies": { "destr": "^2.0.3", "node-fetch-native": "^1.6.4", "ufo": "^1.5.4" } }, "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw=="], 769 ··· 805 806 "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], 807 808 + "pino": ["pino@9.7.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg=="], 809 810 "pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="], 811 ··· 813 814 "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], 815 816 + "postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 817 818 "postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], 819 ··· 825 826 "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 827 828 + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], 829 830 "prettier-plugin-organize-imports": ["prettier-plugin-organize-imports@4.3.0", "", { "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", "vue-tsc": "^2.1.0 || 3" }, "optionalPeers": ["vue-tsc"] }, "sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw=="], 831 832 + "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.1", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-xL49LCloMoZRvSwa6IEdN2GV6cq2IqpYGstYtMT+5wmml1/dClEoI0MZR78MiVPpu6BdQFfN0/y73yO6+br5Pg=="], 833 834 + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="], 835 836 "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], 837 ··· 949 950 "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 951 952 + "svelte": ["svelte@5.46.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.5.0", "esm-env": "^1.2.1", "esrap": "^2.2.1", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-ZhLtvroYxUxr+HQJfMZEDRsGsmU46x12RvAv/zi9584f5KOX7bUrEbhPJ7cKFmUvZTJXi/CFZUYwDC6M1FigPw=="], 953 954 "svelte-check": ["svelte-check@4.3.4", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw=="], 955 ··· 957 958 "svelte-spa-router": ["svelte-spa-router@4.0.1", "", { "dependencies": { "regexparam": "2.0.2" } }, "sha512-2JkmUQ2f9jRluijL58LtdQBIpynSbem2eBGp4zXdi7aDY1znbR6yjw0KsonD0aq2QLwf4Yx4tBJQjxIjgjXHKg=="], 959 960 + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], 961 962 "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], 963 ··· 985 986 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 987 988 + "typescript-eslint": ["typescript-eslint@8.50.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.50.0", "@typescript-eslint/parser": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/utils": "8.50.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A=="], 989 990 "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], 991 992 "uhyphen": ["uhyphen@0.2.0", "", {}, "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="], 993 994 + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 995 996 "unimport": ["unimport@5.0.1", "", { "dependencies": { "acorn": "^8.14.1", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.1", "magic-string": "^0.30.17", "mlly": "^1.7.4", "pathe": "^2.0.3", "picomatch": "^4.0.2", "pkg-types": "^2.1.0", "scule": "^1.3.0", "strip-literal": "^3.0.0", "tinyglobby": "^0.2.13", "unplugin": "^2.3.2", "unplugin-utils": "^0.2.4" } }, "sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ=="], 997 ··· 1011 1012 "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], 1013 1014 + "vite-node": ["vite-node@5.2.0", "", { "dependencies": { "cac": "^6.7.14", "es-module-lexer": "^1.7.0", "obug": "^2.0.0", "pathe": "^2.0.3", "vite": "^7.2.2" }, "bin": { "vite-node": "dist/cli.mjs" } }, "sha512-7UT39YxUukIA97zWPXUGb0SGSiLexEGlavMwU3HDE6+d/HJhKLjLqu4eX2qv6SQiocdhKLRcusroDwXHQ6CnRQ=="], 1015 1016 + "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], 1017 1018 "watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="], 1019 ··· 1037 1038 "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], 1039 1040 + "wxt": ["wxt@0.20.13", "", { "dependencies": { "@1natsu/wait-element": "^4.1.2", "@aklinker1/rollup-plugin-visualizer": "5.12.0", "@webext-core/fake-browser": "^1.3.2", "@webext-core/isolated-element": "^1.1.2", "@webext-core/match-patterns": "^1.0.3", "@wxt-dev/browser": "^0.1.32", "@wxt-dev/storage": "^1.0.0", "async-mutex": "^0.5.0", "c12": "^3.3.2", "cac": "^6.7.14", "chokidar": "^4.0.3", "ci-info": "^4.3.1", "consola": "^3.4.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "dotenv-expand": "^12.0.3", "esbuild": "^0.27.1", "fast-glob": "^3.3.3", "filesize": "^11.0.13", "fs-extra": "^11.3.2", "get-port-please": "^3.2.0", "giget": "^1.2.3 || ^2.0.0", "hookable": "^5.5.3", "import-meta-resolve": "^4.2.0", "is-wsl": "^3.1.0", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.12", "magicast": "^0.3.5", "minimatch": "^10.1.1", "nano-spawn": "^1.0.3", "normalize-path": "^3.0.0", "nypm": "^0.6.2", "ohash": "^2.0.11", "open": "^10.2.0", "ora": "^8.2.0", "perfect-debounce": "^2.0.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "publish-browser-extension": "^2.3.0 || ^3.0.2", "scule": "^1.3.0", "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0", "vite": "^5.4.19 || ^6.3.4 || ^7.0.0", "vite-node": "^3.2.4 || ^5.0.0", "web-ext-run": "^0.2.4" }, "bin": { "wxt": "bin/wxt.mjs", "wxt-publish-extension": "bin/wxt-publish-extension.cjs" } }, "sha512-FwQEk+0a4/pYha6rTKGl5iicU6kRYDBDiElJf55CFEfoJKqvGzBTZpphafurQfqU1X0hvAm9w5GEWC0thXI6wQ=="], 1041 1042 "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="], 1043 ··· 1071 1072 "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 1073 1074 + "@eslint/config-helpers/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 1075 + 1076 "@eslint/eslintrc/espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="], 1077 1078 "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], 1079 + 1080 + "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 1081 1082 "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], 1083 1084 + "@jridgewell/gen-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 1085 + 1086 + "@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 1087 + 1088 "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], 1089 1090 "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 1091 1092 "@tailwindcss/node/lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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=="], 1093 1094 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], 1095 ··· 1097 1098 "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], 1099 1100 + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="], 1101 1102 "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 1103 ··· 1107 1108 "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 1109 1110 + "@typescript-eslint/typescript-estree/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 1111 + 1112 + "@wxt-dev/module-svelte/@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="], 1113 1114 "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 1115 ··· 1118 "boxen/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], 1119 1120 "c12/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 1121 + 1122 + "chrome-launcher/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], 1123 1124 "chrome-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], 1125 ··· 1137 1138 "dotenv-expand/dotenv": ["dotenv@16.5.0", "", {}, "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="], 1139 1140 + "eslint/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 1141 1142 "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1143 ··· 1189 1190 "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 1191 1192 "unimport/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], 1193 1194 "unimport/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 1195 + 1196 + "unimport/magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 1197 1198 "unimport/pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], 1199 ··· 1203 1204 "update-notifier/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 1205 1206 + "vite/esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], 1207 1208 + "vite-node/vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="], 1209 1210 "web-ext-run/strip-json-comments": ["strip-json-comments@5.0.2", "", {}, "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g=="], 1211 ··· 1243 1244 "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], 1245 1246 + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], 1247 + 1248 + "@typescript-eslint/typescript-estree/tinyglobby/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 1249 + 1250 + "@typescript-eslint/typescript-estree/tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 1251 + 1252 + "@wxt-dev/module-svelte/@sveltejs/vite-plugin-svelte/@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="], 1253 + 1254 + "@wxt-dev/module-svelte/@sveltejs/vite-plugin-svelte/magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 1255 1256 + "@wxt-dev/module-svelte/@sveltejs/vite-plugin-svelte/vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="], 1257 1258 "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1259 1260 "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1261 1262 + "chrome-launcher/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 1263 + 1264 "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1265 1266 "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], ··· 1281 1282 "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], 1283 1284 + "unimport/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 1285 + 1286 "unimport/pkg-types/exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], 1287 1288 + "vite-node/vite/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 1289 + 1290 + "vite-node/vite/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 1291 + 1292 + "vite-node/vite/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=="], 1293 + 1294 + "vite-node/vite/rollup": ["rollup@4.53.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.5", "@rollup/rollup-android-arm64": "4.53.5", "@rollup/rollup-darwin-arm64": "4.53.5", "@rollup/rollup-darwin-x64": "4.53.5", "@rollup/rollup-freebsd-arm64": "4.53.5", "@rollup/rollup-freebsd-x64": "4.53.5", "@rollup/rollup-linux-arm-gnueabihf": "4.53.5", "@rollup/rollup-linux-arm-musleabihf": "4.53.5", "@rollup/rollup-linux-arm64-gnu": "4.53.5", "@rollup/rollup-linux-arm64-musl": "4.53.5", "@rollup/rollup-linux-loong64-gnu": "4.53.5", "@rollup/rollup-linux-ppc64-gnu": "4.53.5", "@rollup/rollup-linux-riscv64-gnu": "4.53.5", "@rollup/rollup-linux-riscv64-musl": "4.53.5", "@rollup/rollup-linux-s390x-gnu": "4.53.5", "@rollup/rollup-linux-x64-gnu": "4.53.5", "@rollup/rollup-linux-x64-musl": "4.53.5", "@rollup/rollup-openharmony-arm64": "4.53.5", "@rollup/rollup-win32-arm64-msvc": "4.53.5", "@rollup/rollup-win32-ia32-msvc": "4.53.5", "@rollup/rollup-win32-x64-gnu": "4.53.5", "@rollup/rollup-win32-x64-msvc": "4.53.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ=="], 1295 + 1296 + "vite-node/vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 1297 + 1298 + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], 1299 + 1300 + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="], 1301 + 1302 + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="], 1303 + 1304 + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="], 1305 + 1306 + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="], 1307 + 1308 + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="], 1309 + 1310 + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="], 1311 + 1312 + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="], 1313 + 1314 + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="], 1315 + 1316 + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="], 1317 + 1318 + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="], 1319 + 1320 + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="], 1321 + 1322 + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="], 1323 + 1324 + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="], 1325 + 1326 + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="], 1327 + 1328 + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="], 1329 + 1330 + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="], 1331 + 1332 + "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="], 1333 + 1334 + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="], 1335 + 1336 + "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="], 1337 + 1338 + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="], 1339 + 1340 + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="], 1341 + 1342 + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="], 1343 + 1344 + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="], 1345 + 1346 + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="], 1347 + 1348 "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1349 1350 "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1351 + 1352 + "@wxt-dev/module-svelte/@sveltejs/vite-plugin-svelte/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 1353 1354 "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1355 1356 "giget/nypm/pkg-types/exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], 1357 + 1358 + "vite-node/vite/rollup/@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.5", "", { "os": "android", "cpu": "arm" }, "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ=="], 1359 + 1360 + "vite-node/vite/rollup/@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.5", "", { "os": "android", "cpu": "arm64" }, "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw=="], 1361 + 1362 + "vite-node/vite/rollup/@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ=="], 1363 + 1364 + "vite-node/vite/rollup/@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA=="], 1365 + 1366 + "vite-node/vite/rollup/@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw=="], 1367 + 1368 + "vite-node/vite/rollup/@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ=="], 1369 + 1370 + "vite-node/vite/rollup/@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.5", "", { "os": "linux", "cpu": "arm" }, "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA=="], 1371 + 1372 + "vite-node/vite/rollup/@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.5", "", { "os": "linux", "cpu": "arm" }, "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ=="], 1373 + 1374 + "vite-node/vite/rollup/@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg=="], 1375 + 1376 + "vite-node/vite/rollup/@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g=="], 1377 + 1378 + "vite-node/vite/rollup/@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.5", "", { "os": "linux", "cpu": "none" }, "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ=="], 1379 + 1380 + "vite-node/vite/rollup/@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.5", "", { "os": "linux", "cpu": "none" }, "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w=="], 1381 + 1382 + "vite-node/vite/rollup/@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw=="], 1383 + 1384 + "vite-node/vite/rollup/@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw=="], 1385 + 1386 + "vite-node/vite/rollup/@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.5", "", { "os": "linux", "cpu": "x64" }, "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg=="], 1387 + 1388 + "vite-node/vite/rollup/@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA=="], 1389 + 1390 + "vite-node/vite/rollup/@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w=="], 1391 + 1392 + "vite-node/vite/rollup/@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.5", "", { "os": "win32", "cpu": "x64" }, "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ=="], 1393 + 1394 + "vite-node/vite/rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 1395 1396 "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1397 }
+16 -18
package.json
··· 1 { 2 "name": "schooltape", 3 - "version": "4.2.0", 4 "author": "42willow", 5 "devDependencies": { 6 "@catppuccin/palette": "^1.7.1", 7 "@catppuccin/tailwindcss": "1.0.0", 8 - "@eslint/compat": "^1.4.1", 9 - "@eslint/js": "^9.39.1", 10 - "@lucide/svelte": "^0.544.0", 11 - "@sveltejs/vite-plugin-svelte": "^5.1.1", 12 - "@tailwindcss/vite": "^4.1.17", 13 "@tsconfig/svelte": "^5.0.6", 14 - "@types/node": "^22.19.1", 15 "@types/semver": "^7.7.1", 16 "@wxt-dev/module-svelte": "^2.0.4", 17 - "eslint": "^9.39.1", 18 "eslint-config-prettier": "^10.1.8", 19 - "eslint-plugin-svelte": "^3.13.0", 20 "globals": "^16.5.0", 21 - "pino": "^9.14.0", 22 - "postcss": "^8.5.6", 23 - "prettier": "^3.6.2", 24 "prettier-plugin-organize-imports": "^4.3.0", 25 - "prettier-plugin-svelte": "^3.4.0", 26 - "prettier-plugin-tailwindcss": "^0.6.14", 27 "semver": "^7.7.3", 28 - "svelte": "^5.43.6", 29 "svelte-check": "^4.3.4", 30 "svelte-spa-router": "^4.0.1", 31 - "tailwindcss": "^4.1.17", 32 "typescript": "^5.9.3", 33 - "typescript-eslint": "^8.46.4", 34 - "wxt": "^0.20.11" 35 }, 36 "description": "Schooltape is a free, open-source, web extension that allows you to customise the look, feel, and functionality of Schoolbox!", 37 "homepage": "https://schooltape.github.io",
··· 1 { 2 "name": "schooltape", 3 + "version": "4.3.4", 4 "author": "42willow", 5 "devDependencies": { 6 "@catppuccin/palette": "^1.7.1", 7 "@catppuccin/tailwindcss": "1.0.0", 8 + "@eslint/compat": "^2.0.0", 9 + "@eslint/js": "^9.39.2", 10 + "@lucide/svelte": "^0.554.0", 11 + "@sveltejs/vite-plugin-svelte": "^6.2.1", 12 + "@tailwindcss/vite": "^4.1.18", 13 "@tsconfig/svelte": "^5.0.6", 14 + "@types/node": "^24.10.4", 15 "@types/semver": "^7.7.1", 16 "@wxt-dev/module-svelte": "^2.0.4", 17 + "eslint": "^9.39.2", 18 "eslint-config-prettier": "^10.1.8", 19 + "eslint-plugin-svelte": "^3.13.1", 20 "globals": "^16.5.0", 21 + "prettier": "^3.7.4", 22 "prettier-plugin-organize-imports": "^4.3.0", 23 + "prettier-plugin-svelte": "^3.4.1", 24 + "prettier-plugin-tailwindcss": "^0.7.2", 25 "semver": "^7.7.3", 26 + "svelte": "^5.46.0", 27 "svelte-check": "^4.3.4", 28 "svelte-spa-router": "^4.0.1", 29 + "tailwindcss": "^4.1.18", 30 "typescript": "^5.9.3", 31 + "typescript-eslint": "^8.50.0", 32 + "wxt": "^0.20.13" 33 }, 34 "description": "Schooltape is a free, open-source, web extension that allows you to customise the look, feel, and functionality of Schoolbox!", 35 "homepage": "https://schooltape.github.io",
+31 -29
src/entrypoints/background.ts
··· 1 import type { Browser } from "#imports"; 2 import { browser, defineBackground, storage } from "#imports"; 3 import { logger } from "@/utils/logger"; 4 import { globalSettings, updated } from "@/utils/storage"; 5 import semver from "semver"; 6 ··· 9 if (reason === "install") { 10 logger.info("[background] Opening wiki page after install"); 11 browser.tabs.create({ url: "https://schooltape.github.io/installed" }); 12 if (import.meta.env.DEV) { 13 logger.info("[background] Opening development URLs"); 14 browser.tabs.create({ url: "https://help.schoolbox.com.au/account/anonymous.php?" }); 15 browser.tabs.create({ url: browser.runtime.getURL("/popup.html") }); 16 } 17 } else if (reason === "update") { 18 logger.info("[background] Showing update badge"); ··· 38 }); 39 40 // update icon when toggle or update is changed 41 - globalSettings.storage.watch(() => { 42 - updateIcon(); 43 - }); 44 - 45 - // listen for messages 46 - interface Message { 47 - resetSettings?: boolean; 48 - inject?: string; 49 - toTab?: string; 50 - updateIcon?: boolean; 51 - } 52 53 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 54 - browser.runtime.onMessage.addListener(async (msg: any, sender: any) => { 55 - const message = msg as Message; 56 - logger.child({ message, sender }).info("[background] Received message"); 57 58 - if (message.resetSettings) { 59 - resetSettings(); 60 - } else if (message.toTab) { 61 - const tabs = await browser.tabs.query({ url: message.toTab }); 62 - if (tabs.length > 0) { 63 - // @ts-expect-error - tab will exist 64 - browser.tabs.update(tabs[0].id, { active: true }); 65 - } else if (sender.tab?.id) { 66 - browser.tabs.update(sender.tab.id, { url: message.toTab }); 67 - } 68 - } else if (message.updateIcon) { 69 - updateIcon(); 70 } 71 72 return true; // return success ··· 131 if (new Date().getMonth() === 5) { 132 iconSuffix += "-ctp"; 133 } 134 - if ((await globalSettings.storage.getValue()).global === false) { 135 iconSuffix += "-disabled"; 136 } 137 - if ((await updated.storage.getValue()).icon === true) { 138 iconSuffix += "-badge"; 139 } 140
··· 1 import type { Browser } from "#imports"; 2 import { browser, defineBackground, storage } from "#imports"; 3 import { logger } from "@/utils/logger"; 4 + import type { BackgroundMessage } from "@/utils/storage"; 5 import { globalSettings, updated } from "@/utils/storage"; 6 import semver from "semver"; 7 ··· 10 if (reason === "install") { 11 logger.info("[background] Opening wiki page after install"); 12 browser.tabs.create({ url: "https://schooltape.github.io/installed" }); 13 + 14 if (import.meta.env.DEV) { 15 logger.info("[background] Opening development URLs"); 16 browser.tabs.create({ url: "https://help.schoolbox.com.au/account/anonymous.php?" }); 17 browser.tabs.create({ url: browser.runtime.getURL("/popup.html") }); 18 + const schoolbox = { 19 + url: import.meta.env.WXT_SCHOOLBOX_URL, 20 + jwt: import.meta.env.WXT_SCHOOLBOX_JWT, 21 + }; 22 + if (schoolbox.url && schoolbox.jwt) { 23 + browser.tabs.create({ url: `${schoolbox.url}/api/session?jwt=${schoolbox.jwt}` }); 24 + } 25 } 26 } else if (reason === "update") { 27 logger.info("[background] Showing update badge"); ··· 47 }); 48 49 // update icon when toggle or update is changed 50 + globalSettings.watch(updateIcon); 51 52 + browser.runtime.onMessage.addListener(async (msg: BackgroundMessage, sender: Browser.runtime.MessageSender) => { 53 + logger.info("[background] received message", { message: msg, sender }); 54 55 + switch (msg.type) { 56 + case "resetSettings": 57 + resetSettings(); 58 + break; 59 + case "updateIcon": 60 + updateIcon(); 61 + break; 62 + case "closeTab": 63 + if (!sender.tab?.id) break; 64 + browser.tabs.remove(sender.tab.id); 65 + break; 66 + case "updateTabUrl": 67 + if (!sender.tab?.id) break; 68 + browser.tabs.update(sender.tab.id, { url: msg.url }); 69 + break; 70 + default: 71 + logger.error(`[background] unknown message received: ${msg}`); 72 } 73 74 return true; // return success ··· 133 if (new Date().getMonth() === 5) { 134 iconSuffix += "-ctp"; 135 } 136 + if ((await globalSettings.get()).global === false) { 137 iconSuffix += "-disabled"; 138 } 139 + if ((await updated.get()).icon === true) { 140 iconSuffix += "-badge"; 141 } 142
+11 -18
src/entrypoints/end.content.ts
··· 1 - import { browser, defineContentScript } from "#imports"; 2 import { EXCLUDE_MATCHES } from "@/utils/constants"; 3 import { logger } from "@/utils/logger"; 4 import { globalSettings, schoolboxUrls } from "@/utils/storage"; ··· 8 runAt: "document_end", 9 excludeMatches: EXCLUDE_MATCHES, 10 async main() { 11 - const settings = await globalSettings.storage.getValue(); 12 - const urls = (await schoolboxUrls.storage.getValue()).urls; 13 14 - logger.info((await schoolboxUrls.storage.getValue()).urls); 15 16 if (!settings.global) return; 17 const footer = document.querySelector("#footer > ul"); 18 if (footer && footer.innerHTML.includes("Schoolbox")) { 19 - const footerListItem = document.createElement("li"); 20 - const footerLink = document.createElement("a"); 21 - footerLink.href = "https://github.com/schooltape/schooltape"; 22 - footerLink.textContent = `Schooltape v${browser.runtime.getManifest().version}`; 23 - footerListItem.appendChild(footerLink); 24 - footer.appendChild(footerListItem); 25 - 26 if (!urls.includes(window.location.origin)) { 27 - logger.info("[end.content.ts] URL not in settings, adding..."); 28 - if (!urls.includes(window.location.origin)) { 29 - urls.push(window.location.origin); 30 - await schoolboxUrls.storage.setValue({ urls }); 31 - // TODO: hot reload 32 - window.location.reload(); 33 - } 34 } 35 } 36 },
··· 1 + import { defineContentScript } from "#imports"; 2 import { EXCLUDE_MATCHES } from "@/utils/constants"; 3 import { logger } from "@/utils/logger"; 4 import { globalSettings, schoolboxUrls } from "@/utils/storage"; ··· 8 runAt: "document_end", 9 excludeMatches: EXCLUDE_MATCHES, 10 async main() { 11 + const settings = await globalSettings.get(); 12 + const urls = (await schoolboxUrls.get()).urls; 13 14 + logger.info(urls); 15 16 if (!settings.global) return; 17 + 18 const footer = document.querySelector("#footer > ul"); 19 + 20 if (footer && footer.innerHTML.includes("Schoolbox")) { 21 if (!urls.includes(window.location.origin)) { 22 + logger.info(`URL ${window.location.origin} not in storage, adding...`); 23 + urls.push(window.location.origin); 24 + await schoolboxUrls.set({ urls }); 25 + // TODO: hot reload 26 + window.location.reload(); 27 } 28 } 29 },
+16
src/entrypoints/plugins/homepageSwitcher/Menu.svelte
···
··· 1 + <script lang="ts"> 2 + import Toggle from "@/entrypoints/popup/components/inputs/Toggle.svelte"; 3 + import type { Settings } from "."; 4 + 5 + let { settings }: { settings: Settings } = $props(); 6 + </script> 7 + 8 + <Toggle 9 + text="Close current tab" 10 + description="When switching to another tab, close the current one." 11 + size="small" 12 + id="closeCurrentTab" 13 + checked={settings.closeCurrentTab.state.toggle} 14 + update={async (toggle) => { 15 + settings.closeCurrentTab.set({ toggle }); 16 + }} />
+64
src/entrypoints/plugins/homepageSwitcher/index.ts
···
··· 1 + import { sendMessage } from "@/utils"; 2 + import { Plugin } from "@/utils/plugin"; 3 + import type { Toggle } from "@/utils/storage"; 4 + import type { StorageState } from "@/utils/storage/state.svelte"; 5 + 6 + let logos: HTMLAnchorElement[] | null = null; 7 + let controller: AbortController | null = null; 8 + 9 + export type Settings = { 10 + closeCurrentTab: StorageState<Toggle>; 11 + }; 12 + 13 + export default new Plugin<Settings>( 14 + { 15 + id: "homepageSwitcher", 16 + name: "Homepage Switcher", 17 + description: "The logo will switch to existing Schoolbox homepage when available.", 18 + }, 19 + false, 20 + { 21 + closeCurrentTab: { 22 + toggle: false, 23 + }, 24 + }, 25 + async (settings) => { 26 + if (logos !== null) return; 27 + 28 + logos = Array.from(document.getElementsByClassName("logo")) as HTMLAnchorElement[]; 29 + 30 + // add event listeners 31 + const closeCurrentTab = await settings.closeCurrentTab.get(); 32 + controller = new AbortController(); 33 + 34 + for (const logo of logos) { 35 + logo.addEventListener( 36 + "click", 37 + (e) => { 38 + if (window.location.pathname === "/") return; 39 + 40 + e.preventDefault(); 41 + 42 + if (logos) { 43 + const tabUrl = logos[0].href; 44 + if (closeCurrentTab.toggle) sendMessage({ type: "closeTab" }); 45 + sendMessage({ type: "updateTabUrl", url: tabUrl }); 46 + } 47 + }, 48 + { 49 + signal: controller.signal, 50 + }, 51 + ); 52 + } 53 + }, 54 + () => { 55 + // remove event listeners 56 + if (controller) { 57 + controller.abort(); 58 + controller = null; 59 + } 60 + 61 + logos = null; 62 + }, 63 + [".logo"], 64 + );
-23
src/entrypoints/plugins/homepageSwitcher.ts
··· 1 - import { browser } from "#imports"; 2 - import { definePlugin } from "@/utils/plugin"; 3 - 4 - export default function init() { 5 - definePlugin( 6 - "homepageSwitcher", 7 - (settings) => { 8 - const logos = Array.from(document.getElementsByClassName("logo")) as HTMLAnchorElement[]; 9 - logos.forEach((logo) => { 10 - logo.addEventListener("click", async function (e) { 11 - if (window.location.pathname === "/") return; 12 - e.preventDefault(); 13 - const tab = logos[0].href; 14 - if (settings?.toggle.closeCurrentTab === true) { 15 - window.close(); 16 - } 17 - browser.runtime.sendMessage({ toTab: tab }); 18 - }); 19 - }); 20 - }, 21 - [".logo"], 22 - ); 23 - }
···
+16
src/entrypoints/plugins/modernIcons/Menu.svelte
···
··· 1 + <script lang="ts"> 2 + import Toggle from "@/entrypoints/popup/components/inputs/Toggle.svelte"; 3 + import type { Settings } from "."; 4 + 5 + let { settings }: { settings: Settings } = $props(); 6 + </script> 7 + 8 + <Toggle 9 + text="Filled icons" 10 + description="Whether the icons should be filled or outlined." 11 + size="small" 12 + id="filled" 13 + checked={settings.filled.state.toggle} 14 + update={async (toggle) => { 15 + settings.filled.set({ toggle }); 16 + }} />
+106 -71
src/entrypoints/plugins/modernIcons/index.ts
··· 1 - import { injectStyles } from "@/utils"; 2 - import { definePlugin } from "@/utils/plugin"; 3 import styleText from "./styles.css?inline"; 4 5 - export default function init() { 6 - definePlugin( 7 - "modernIcons", 8 - async (settings) => { 9 - // [className, iconName] (material icons) 10 - const icons = { 11 - "icon-teacher": "school", 12 - "icon-due-work": "inventory_2", 13 - "icon-task": "inventory", 14 - "icon-timetable": "schedule", 15 - "icon-calendar": "calendar_month", 16 - "icon-news": "newspaper", 17 - "icon-email": "email", 18 - "icon-wolfram-alpha": "web", 19 - "icon-comment": "translate", 20 - "icon-canvas-lms": "medical_services", 21 - "icon-video": "videocam", 22 - "icon-office-365": "dvr", 23 - "icon-google-drive": "drive_export", 24 - "icon-help": "help", 25 - "icon-podcast": "music_note", 26 - "icon-music": "music_note", 27 - "icon-staff-students": "account_circle", 28 - "icon-settings": "settings", 29 - "icon-logout": "logout", 30 - "icon-course": "class", 31 - "icon-reply": "reply", 32 - "icon-approve": "check_circle", 33 - "icon-forms": "check_box", 34 - "icon-group": "group", 35 - "icon-info": "info", 36 - "icon-resource-booking": "photo_camera", 37 - "icon-files": "description", 38 - "icon-schoolbox": "language", 39 - "icon-user": "person", 40 - "icon-cloudy": "cloud", 41 - "icon-eportfolio": "work", 42 - "icon-open": "door_open", 43 - }; 44 45 - function insertIcon(className: string, iconName: string, fill: boolean) { 46 - const selectors = [`nav.tab-bar .top-menu .${className}`, `#overflow-nav .${className}`]; 47 - selectors.forEach((selector) => { 48 - const icon = document.querySelector(selector); 49 - // Check if the icon already exists 50 - if (icon && !icon.querySelector(".material-symbols-rounded")) { 51 - // logger.info(`Inserting icon for ${className} at ${selector}`); 52 - const iconElement = document.createElement("i"); 53 - iconElement.innerHTML = iconName; 54 - iconElement.classList.add("material-symbols-rounded"); 55 - iconElement.style.fontVariationSettings = `"FILL" ${fill ? "1" : "0"}`; 56 - icon.insertBefore(iconElement, icon.firstChild); 57 - } 58 - }); 59 - } 60 61 - const iconNames = [...new Set(Object.values(icons))].sort(); 62 - const fontUrl = `https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:FILL@0..1&icon_names=${iconNames.join(",")}`; 63 - // logger.info(fontUrl); 64 65 - // inject font face 66 - const style = document.createElement("link"); 67 - style.rel = "stylesheet"; 68 - style.href = fontUrl; 69 - style.type = "text/css"; 70 - document.head.appendChild(style); 71 72 - injectStyles(styleText); 73 74 - for (const [className, iconName] of Object.entries(icons)) { 75 - insertIcon(className, iconName, settings?.toggle.filled ?? false); 76 } 77 - }, 78 - ["nav.tab-bar .top-menu", "#overflow-nav"], 79 - ); 80 }
··· 1 + import { 2 + dataAttr, 3 + injectInlineStyles, 4 + injectStylesheet, 5 + setDataAttr, 6 + uninjectInlineStyles, 7 + uninjectStylesheet, 8 + } from "@/utils"; 9 + import { Plugin } from "@/utils/plugin"; 10 + import type { Toggle } from "@/utils/storage"; 11 + import type { StorageState } from "@/utils/storage/state.svelte"; 12 import styleText from "./styles.css?inline"; 13 14 + const ID = "modernIcons"; 15 + const PLUGIN_ID = `plugin-${ID}`; 16 + 17 + export type Settings = { 18 + filled: StorageState<Toggle>; 19 + }; 20 + 21 + export default new Plugin<Settings>( 22 + { 23 + id: ID, 24 + name: "Modern Icons", 25 + description: "Modernise the icons across Schoolbox.", 26 + }, 27 + true, 28 + { 29 + filled: { toggle: true }, 30 + }, 31 + 32 + async (settings) => { 33 + const iconNames = [...new Set(Object.values(icons))].sort(); 34 + const fontUrl = `https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:FILL@0..1&icon_names=${iconNames.join(",")}`; 35 + 36 + // inject font face 37 + injectStylesheet(fontUrl, PLUGIN_ID); 38 39 + // inject icon styling 40 + injectInlineStyles(styleText, PLUGIN_ID); 41 42 + // inject icons 43 + const filled = await settings.filled.get(); 44 + injectIcons(icons, filled.toggle); 45 + }, 46 + () => { 47 + uninjectStylesheet(PLUGIN_ID); 48 + uninjectInlineStyles(PLUGIN_ID); 49 + uninjectIcons(); 50 + }, 51 + ["nav.tab-bar .top-menu", "#overflow-nav"], 52 + ); 53 54 + // [className, iconName] (material icons) 55 + const icons = { 56 + "icon-teacher": "school", 57 + "icon-due-work": "inventory_2", 58 + "icon-task": "inventory", 59 + "icon-timetable": "schedule", 60 + "icon-calendar": "calendar_month", 61 + "icon-news": "newspaper", 62 + "icon-email": "email", 63 + "icon-wolfram-alpha": "web", 64 + "icon-comment": "translate", 65 + "icon-canvas-lms": "medical_services", 66 + "icon-video": "videocam", 67 + "icon-office-365": "dvr", 68 + "icon-google-drive": "drive_export", 69 + "icon-help": "help", 70 + "icon-podcast": "music_note", 71 + "icon-music": "music_note", 72 + "icon-staff-students": "account_circle", 73 + "icon-settings": "settings", 74 + "icon-logout": "logout", 75 + "icon-course": "class", 76 + "icon-reply": "reply", 77 + "icon-approve": "check_circle", 78 + "icon-forms": "check_box", 79 + "icon-group": "group", 80 + "icon-info": "info", 81 + "icon-resource-booking": "photo_camera", 82 + "icon-files": "description", 83 + "icon-schoolbox": "language", 84 + "icon-user": "person", 85 + "icon-cloudy": "cloud", 86 + "icon-eportfolio": "work", 87 + "icon-open": "door_open", 88 + }; 89 90 + function injectIcons(icons: Record<string, string>, filled: boolean) { 91 + for (const [className, iconName] of Object.entries(icons)) { 92 + const selectors = [`nav.tab-bar .top-menu .${className}`, `#overflow-nav .${className}`]; 93 94 + for (const selector of selectors) { 95 + const icon = document.querySelector(selector); 96 + // check if the icon already exists 97 + if (icon && !icon.querySelector(".material-symbols-rounded")) { 98 + // logger.info(`inserting icon for ${className} at ${selector}`); 99 + const iconElement = document.createElement("i"); 100 + iconElement.innerHTML = iconName; 101 + iconElement.classList.add("material-symbols-rounded"); 102 + iconElement.style.fontVariationSettings = `"FILL" ${filled ? "1" : "0"}`; 103 + setDataAttr(iconElement, `${PLUGIN_ID}-icon`); 104 + icon.insertBefore(iconElement, icon.firstChild); 105 } 106 + } 107 + } 108 + } 109 + 110 + function uninjectIcons() { 111 + const icons = document.querySelectorAll(dataAttr(`${PLUGIN_ID}-icon`)); 112 + for (const icon of icons) { 113 + icon.parentNode?.removeChild(icon); 114 + } 115 }
+41 -21
src/entrypoints/plugins/progressBar/index.ts
··· 1 - import { injectStyles } from "@/utils"; 2 import type { Period } from "@/utils/periodUtils"; 3 import { getListOfPeriods } from "@/utils/periodUtils"; 4 - import { definePlugin } from "@/utils/plugin"; 5 import styleText from "./styles.css?inline"; 6 7 - export default function init() { 8 - definePlugin( 9 - "progressBar", 10 - () => { 11 - if (window.location.pathname === "/" && document.querySelector(".timetable")) { 12 - const periodList = getListOfPeriods(); 13 14 - const progressRow = document.createElement("tr"); 15 - progressRow.classList.add("progress-container"); 16 - document.querySelector(".timetable > thead")?.insertAdjacentElement("beforeend", progressRow); 17 18 - injectStyles(styleText); 19 - insertProgressBars(periodList, progressRow); 20 - } 21 - }, 22 - [".timetable"], 23 - ); 24 - } 25 26 - function insertProgressBars(periodList: Period[], container: HTMLElement) { 27 - periodList.forEach((period) => { 28 const td = document.createElement("td"); 29 const progressBar = document.createElement("progress"); 30 const progress = period.getProgress(); ··· 44 45 td.appendChild(progressBar); 46 container.appendChild(td); 47 - }); 48 }
··· 1 + import { dataAttr, injectInlineStyles, setDataAttr, uninjectInlineStyles } from "@/utils"; 2 import type { Period } from "@/utils/periodUtils"; 3 import { getListOfPeriods } from "@/utils/periodUtils"; 4 + import { Plugin } from "@/utils/plugin"; 5 import styleText from "./styles.css?inline"; 6 7 + const ID = "progressBar"; 8 + const PLUGIN_ID = `plugin-${ID}`; 9 10 + export default new Plugin( 11 + { 12 + id: ID, 13 + name: "Progress Bar", 14 + description: "Displays a progress bar below the timetable to show the time of the day.", 15 + }, 16 + true, 17 + null, 18 + () => { 19 + if (window.location.pathname === "/" && document.querySelector(".timetable")) { 20 + const periodList = getListOfPeriods(); 21 22 + const progressRow = document.createElement("tr"); 23 + progressRow.classList.add("progress-container"); 24 + document.querySelector(".timetable > thead")?.insertAdjacentElement("beforeend", progressRow); 25 + 26 + injectInlineStyles(styleText, PLUGIN_ID); 27 + injectProgressBars(periodList, progressRow); 28 + 29 + setDataAttr(progressRow, `${PLUGIN_ID}-row`); 30 + } 31 + }, 32 + () => { 33 + uninjectInlineStyles(PLUGIN_ID); 34 + uninjectProgressBars(); 35 + }, 36 + [".timetable"], 37 + ); 38 39 + function injectProgressBars(periodList: Period[], container: HTMLElement) { 40 + if (document.querySelector(dataAttr(`${PLUGIN_ID}-row`))) return; 41 + 42 + for (const period of periodList) { 43 const td = document.createElement("td"); 44 const progressBar = document.createElement("progress"); 45 const progress = period.getProgress(); ··· 59 60 td.appendChild(progressBar); 61 container.appendChild(td); 62 + } 63 + } 64 + 65 + function uninjectProgressBars() { 66 + const row = document.querySelector(dataAttr(`${PLUGIN_ID}-row`)); 67 + row?.parentElement?.removeChild(row); 68 }
+23
src/entrypoints/plugins/scrollPeriod/Menu.svelte
···
··· 1 + <script lang="ts"> 2 + import Toggle from "@/entrypoints/popup/components/inputs/Toggle.svelte"; 3 + import Slider from "@/entrypoints/popup/components/inputs/Slider.svelte"; 4 + import type { Settings } from "."; 5 + 6 + let { settings }: { settings: Settings } = $props(); 7 + </script> 8 + 9 + <Toggle 10 + text="Reset on mouse move" 11 + description="Whether to reset the scrolling cooldown when you move your mouse." 12 + size="small" 13 + id="resetCooldownOnMouseMove" 14 + checked={settings.resetCooldownOnMouseMove.state.toggle} 15 + update={async (toggle) => { 16 + settings.resetCooldownOnMouseMove.set({ toggle }); 17 + }} /> 18 + 19 + <Slider 20 + name="Cooldown duration" 21 + id="cooldownDuration" 22 + update={(value) => settings.cooldownDuration.update({ value })} 23 + {...settings.cooldownDuration.state} />
+79
src/entrypoints/plugins/scrollPeriod/index.ts
···
··· 1 + import { getCurrentPeriod } from "@/utils/periodUtils"; 2 + import { Plugin } from "@/utils/plugin"; 3 + import type { Slider, Toggle } from "@/utils/storage"; 4 + import type { StorageState } from "@/utils/storage/state.svelte"; 5 + 6 + let interval: NodeJS.Timeout | null = null; 7 + let controller: AbortController | null = null; 8 + 9 + export type Settings = { 10 + resetCooldownOnMouseMove: StorageState<Toggle>; 11 + cooldownDuration: StorageState<Slider>; 12 + }; 13 + 14 + export default new Plugin<Settings>( 15 + { 16 + id: "scrollPeriod", 17 + name: "Scroll Period", 18 + description: "Scrolls to the current period on the timetable.", 19 + }, 20 + true, 21 + { 22 + resetCooldownOnMouseMove: { toggle: true }, 23 + cooldownDuration: { min: 1, max: 60, value: 10 }, 24 + }, 25 + async (settings) => { 26 + const timetable = document.querySelector("[data-timetable-container] div.scrollable"); 27 + 28 + if (window.location.pathname === "/" && timetable) { 29 + updateScrollbar(timetable); 30 + 31 + const cooldownDuration = await settings.cooldownDuration.get(); 32 + const resetCooldownOnMouseMove = await settings.resetCooldownOnMouseMove.get(); 33 + 34 + const setUpdateInterval = () => { 35 + interval = setInterval(() => updateScrollbar(timetable), (cooldownDuration?.value || 10) * 1000); 36 + }; 37 + 38 + setUpdateInterval(); 39 + 40 + if (resetCooldownOnMouseMove.toggle === true) { 41 + controller = new AbortController(); 42 + document.addEventListener( 43 + "mousemove", 44 + () => { 45 + if (interval) { 46 + clearInterval(interval); 47 + setUpdateInterval(); 48 + } 49 + }, 50 + { signal: controller.signal }, 51 + ); 52 + } 53 + } 54 + }, 55 + () => { 56 + if (controller) { 57 + controller.abort(); 58 + controller = null; 59 + } 60 + if (interval) { 61 + clearInterval(interval); 62 + interval = null; 63 + } 64 + }, 65 + [".timetable"], 66 + ); 67 + 68 + function updateScrollbar(timetable: Element) { 69 + const currentPeriod = getCurrentPeriod(); 70 + if (currentPeriod && currentPeriod.index && timetable) { 71 + const period = document.querySelector(`.timetable thead tr th:nth-child(${currentPeriod.index})`) as HTMLElement; 72 + if (period) { 73 + timetable.scroll({ 74 + left: period.offsetLeft - 55, // adjusted for alignment 75 + behavior: "smooth", // or 'auto' for instant scroll 76 + }); 77 + } 78 + } 79 + }
-51
src/entrypoints/plugins/scrollPeriod.ts
··· 1 - import { getCurrentPeriod } from "@/utils/periodUtils"; 2 - import { definePlugin } from "@/utils/plugin"; 3 - 4 - export default function init() { 5 - definePlugin( 6 - "scrollPeriod", 7 - async (settings) => { 8 - const timetable = document.querySelector("[data-timetable-container] div.scrollable"); 9 - 10 - if (window.location.pathname === "/" && document.getElementsByClassName("timetable")[0]) { 11 - updateScrollbar(); 12 - 13 - const cooldownDuration = settings?.slider.cooldownDuration; 14 - const resetCooldownOnMouseMove = settings?.toggle.resetCooldownOnMouseMove; 15 - 16 - let interval: string | number | NodeJS.Timeout | undefined; 17 - function start() { 18 - interval = setInterval(updateScrollbar, (cooldownDuration?.value || 10) * 1000); 19 - } 20 - function reset() { 21 - if (interval) { 22 - clearInterval(interval); 23 - start(); 24 - } 25 - } 26 - 27 - start(); 28 - 29 - if (resetCooldownOnMouseMove === true) { 30 - document.addEventListener("mousemove", reset); 31 - } 32 - } 33 - 34 - function updateScrollbar() { 35 - const currentPeriod = getCurrentPeriod(); 36 - if (currentPeriod && currentPeriod.index && timetable) { 37 - const period = document.querySelector( 38 - `.timetable thead tr th:nth-child(${currentPeriod.index})`, 39 - ) as HTMLElement; 40 - if (period) { 41 - timetable.scroll({ 42 - left: period.offsetLeft - 55, // adjusted for alignment 43 - behavior: "smooth", // or 'auto' for instant scroll 44 - }); 45 - } 46 - } 47 - } 48 - }, 49 - [".timetable"], 50 - ); 51 - }
···
+47 -16
src/entrypoints/plugins/scrollSegments/index.ts
··· 1 - import { injectStyles } from "@/utils"; 2 - import { definePlugin } from "@/utils/plugin"; 3 import styleText from "./styles.css?inline"; 4 5 - export default function init() { 6 - definePlugin( 7 - "scrollSegments", 8 - () => { 9 - const content = document.getElementById("content"); 10 - const footer = document.getElementById("footer"); 11 - if (content && footer) { 12 - content.appendChild(footer); 13 - } 14 - injectStyles(styleText); 15 - }, 16 - ["#content", "#footer"], 17 - ); 18 - }
··· 1 + import { dataAttr, injectInlineStyles, setDataAttr, uninjectInlineStyles } from "@/utils"; 2 + import { Plugin } from "@/utils/plugin"; 3 import styleText from "./styles.css?inline"; 4 5 + const ID = "scrollSegments"; 6 + const PLUGIN_ID = `plugin-${ID}`; 7 + 8 + export default new Plugin( 9 + { 10 + id: ID, 11 + name: "Scroll Segments", 12 + description: "Segments the Schoolbox page into scrollable sections.", 13 + }, 14 + true, 15 + null, 16 + () => { 17 + const footerCopy = document.querySelector(dataAttr(PLUGIN_ID)); 18 + if (footerCopy) return; 19 + 20 + // scroll to top to avoid hot reload bug 21 + window.scrollTo({ 22 + top: 0, 23 + behavior: "instant", 24 + }); 25 + 26 + const content = document.querySelector("#content"); 27 + const footer = document.querySelector<HTMLDivElement>("#footer"); 28 + 29 + // add copy of footer to content 30 + if (content && footer) { 31 + const clone = footer.cloneNode(true) as HTMLDivElement; 32 + setDataAttr(clone, PLUGIN_ID); 33 + content.appendChild(clone); 34 + } 35 + 36 + injectInlineStyles(styleText, PLUGIN_ID); 37 + }, 38 + () => { 39 + const footerCopy = document.querySelector(dataAttr(PLUGIN_ID)); 40 + if (!footerCopy) return; 41 + 42 + // remove copy of footer from content 43 + const content = document.querySelector("#content"); 44 + content?.removeChild(footerCopy); 45 + 46 + uninjectInlineStyles(PLUGIN_ID); 47 + }, 48 + ["#content", "#footer"], 49 + );
+16
src/entrypoints/plugins/subheader/Menu.svelte
···
··· 1 + <script lang="ts"> 2 + import Toggle from "@/entrypoints/popup/components/inputs/Toggle.svelte"; 3 + import type { Settings } from "."; 4 + 5 + let { settings }: { settings: Settings } = $props(); 6 + </script> 7 + 8 + <Toggle 9 + text="Open links in new tab" 10 + description="Whether to open class links in a new tab." 11 + size="small" 12 + id="openInNewTab" 13 + checked={settings.openInNewTab.state.toggle} 14 + update={async (toggle) => { 15 + settings.openInNewTab.set({ toggle }); 16 + }} />
+170
src/entrypoints/plugins/subheader/index.ts
···
··· 1 + import { dataAttr, injectInlineStyles, setDataAttr, uninjectInlineStyles } from "@/utils"; 2 + import { getCurrentPeriod } from "@/utils/periodUtils"; 3 + import { Plugin } from "@/utils/plugin"; 4 + import type { Toggle } from "@/utils/storage"; 5 + import type { StorageState } from "@/utils/storage/state.svelte"; 6 + import styleText from "./styles.css?inline"; 7 + 8 + const ID = "subheader"; 9 + const PLUGIN_ID = `plugin-${ID}`; 10 + 11 + let intervals: NodeJS.Timeout[] = []; 12 + let oldChildren: ChildNode[] = []; 13 + let subheader: HTMLHeadingElement | null = null; 14 + 15 + export type Settings = { 16 + openInNewTab: StorageState<Toggle>; 17 + }; 18 + 19 + export default new Plugin<Settings>( 20 + { 21 + id: ID, 22 + name: "Subheader Revamp", 23 + description: "Adds a clock and current period info to the subheader.", 24 + }, 25 + true, 26 + { 27 + openInNewTab: { toggle: true }, 28 + }, 29 + async (settings) => { 30 + const openInNewTab = await settings.openInNewTab.get(); 31 + injectSubheader(openInNewTab.toggle); 32 + }, 33 + uninjectSubheader, 34 + [".subheader", ".timetable"], 35 + ); 36 + 37 + function injectSubheader(openInNewTab: boolean) { 38 + // abort if plugin is injected 39 + if (subheader !== null) return; 40 + 41 + // abort if not on homepage 42 + if (window.location.pathname !== "/") return; 43 + 44 + subheader = document.querySelector("h2.subheader"); 45 + if (!subheader) return; 46 + 47 + // inject subheader styling 48 + injectInlineStyles(styleText, PLUGIN_ID); 49 + 50 + // delete all children of the subheader 51 + while (subheader.firstChild) { 52 + oldChildren.push(subheader.removeChild(subheader.firstChild)); 53 + } 54 + 55 + updatePeriodSpan(openInNewTab); 56 + updateClockSpan(); 57 + updateDateSpan(); 58 + 59 + intervals = [ 60 + setInterval(() => updatePeriodSpan(openInNewTab), 5000), 61 + setInterval(updateClockSpan, 1000), 62 + setInterval(updateDateSpan, 60000), 63 + ]; 64 + } 65 + 66 + function uninjectSubheader() { 67 + // abort if plugin is not injected 68 + if (subheader === null) return; 69 + 70 + // abort if not on homepage 71 + if (window.location.pathname !== "/") return; 72 + 73 + // stop updating the subheader 74 + intervals.forEach((interval) => clearInterval(interval)); 75 + 76 + // remove new children 77 + while (subheader.firstChild) { 78 + subheader.removeChild(subheader.firstChild); 79 + } 80 + 81 + // restore old children 82 + for (const child of oldChildren) { 83 + subheader.appendChild(child); 84 + } 85 + 86 + // uninject subheader styling 87 + uninjectInlineStyles(PLUGIN_ID); 88 + 89 + // reset variables 90 + intervals = []; 91 + oldChildren = []; 92 + subheader = null; 93 + } 94 + 95 + async function updatePeriodSpan(openInNewTab: boolean) { 96 + if (!subheader) return; 97 + 98 + const periodId = `${PLUGIN_ID}-period`; 99 + let periodSpan = document.querySelector<HTMLSpanElement>(`.subheader ${dataAttr(periodId)}`); 100 + 101 + if (!periodSpan) { 102 + periodSpan = document.createElement("span"); 103 + setDataAttr(periodSpan, periodId); 104 + subheader.appendChild(periodSpan); 105 + } 106 + 107 + periodSpan.textContent = ""; 108 + 109 + // set period span content 110 + const period = getCurrentPeriod(); 111 + if (period) { 112 + const name = period.data.name || period.header.name; 113 + const room = period.data.room ? ` (${period.data.room})` : ""; 114 + let periodLink = periodSpan.querySelector("a"); 115 + if (period.data.name && period.data.link) { 116 + // if there's period data 117 + if (!periodLink) { 118 + periodLink = document.createElement("a"); 119 + 120 + periodLink.target = openInNewTab ? "_blank" : "_self"; 121 + periodSpan.appendChild(periodLink); 122 + } 123 + periodLink.href = period.data.link; 124 + periodLink.textContent = `${name}${room}`; 125 + } else { 126 + // if there's only the header 127 + periodSpan.textContent = `${name}${room}`; 128 + if (periodLink) { 129 + periodSpan.removeChild(periodLink); 130 + } 131 + } 132 + } 133 + } 134 + 135 + function updateClockSpan() { 136 + if (!subheader) return; 137 + 138 + const clockId = `${PLUGIN_ID}-clock`; 139 + let clockSpan = document.querySelector<HTMLSpanElement>(`.subheader ${dataAttr(clockId)}`); 140 + 141 + if (!clockSpan) { 142 + clockSpan = document.createElement("span"); 143 + setDataAttr(clockSpan, clockId); 144 + subheader.appendChild(clockSpan); 145 + } 146 + 147 + // set clock span content 148 + const date = new Date(); 149 + clockSpan.textContent = date.toLocaleTimeString([], { 150 + hour: "2-digit", 151 + minute: "2-digit", 152 + }); 153 + } 154 + 155 + function updateDateSpan() { 156 + if (!subheader) return; 157 + 158 + const dateId = `${PLUGIN_ID}-date`; 159 + let dateSpan = document.querySelector<HTMLSpanElement>(`.subheader ${dataAttr(dateId)}`); 160 + 161 + if (!dateSpan) { 162 + dateSpan = document.createElement("span"); 163 + setDataAttr(dateSpan, dateId); 164 + subheader.appendChild(dateSpan); 165 + } 166 + 167 + // set date span content 168 + const date = new Date(); 169 + dateSpan.textContent = date.toDateString(); 170 + }
+7
src/entrypoints/plugins/subheader/styles.css
···
··· 1 + .subheader span:not(:last-child):not([data-schooltape="plugin-subheader-period"]:empty)::after { 2 + content: " | "; 3 + font-weight: bold; 4 + } 5 + .subheader a { 6 + color: inherit; 7 + }
-113
src/entrypoints/plugins/subheader.ts
··· 1 - import { getCurrentPeriod } from "@/utils/periodUtils"; 2 - import { definePlugin } from "@/utils/plugin"; 3 - 4 - export default function init() { 5 - definePlugin( 6 - "subheader", 7 - (settings) => { 8 - const style = document.createElement("style"); 9 - style.classList = "schooltape"; 10 - style.innerHTML = ` 11 - .subheader span:not(:last-child):not(.period:empty)::after { 12 - content: " | "; 13 - font-weight: bold; 14 - } 15 - .subheader a { 16 - color: inherit; 17 - } 18 - `; 19 - 20 - document.head.appendChild(style); 21 - 22 - if (window.location.pathname === "/" && document.getElementsByClassName("timetable")[0]) { 23 - createSubheader(); 24 - setInterval(updatePeriodSpan, 5000); 25 - setInterval(updateClockSpan, 1000); 26 - setInterval(updateDateSpan, 60000); 27 - } 28 - 29 - function createSubheader() { 30 - const subheader = document.querySelector("h2.subheader"); 31 - if (!subheader) return; 32 - // TODO: Refactor to support hot reload/uninjection 33 - // delete all children of the subheader 34 - while (subheader.firstChild) { 35 - subheader.removeChild(subheader.firstChild); 36 - } 37 - const span = document.createElement("span"); 38 - span.classList.add("schooltape"); 39 - subheader.appendChild(span); 40 - 41 - updatePeriodSpan(); 42 - updateClockSpan(); 43 - updateDateSpan(); 44 - } 45 - 46 - async function updatePeriodSpan() { 47 - let periodSpan = document.querySelector(".subheader .period"); 48 - if (!periodSpan) { 49 - const subheader = document.querySelector(".subheader .schooltape"); 50 - if (!subheader) return; 51 - periodSpan = document.createElement("span"); 52 - periodSpan.classList.add("period"); 53 - subheader.appendChild(periodSpan); 54 - } 55 - periodSpan.textContent = ""; 56 - 57 - const period = getCurrentPeriod(); 58 - if (period) { 59 - const name = period.data.name || period.header.name; 60 - const room = period.data.room ? ` (${period.data.room})` : ""; 61 - let periodLink = periodSpan.querySelector("a"); 62 - if (period.data.name && period.data.link) { 63 - // if there's period data 64 - if (!periodLink) { 65 - periodLink = document.createElement("a"); 66 - 67 - periodLink.target = settings?.toggle.openInNewTab ? "_blank" : "_self"; 68 - periodSpan.appendChild(periodLink); 69 - } 70 - periodLink.href = period.data.link; 71 - periodLink.textContent = `${name}${room}`; 72 - } else { 73 - // if there's only the header 74 - periodSpan.textContent = `${name}${room}`; 75 - if (periodLink) { 76 - periodSpan.removeChild(periodLink); 77 - } 78 - } 79 - } 80 - } 81 - 82 - function updateClockSpan() { 83 - let clockSpan = document.querySelector(".subheader .clock"); 84 - if (!clockSpan) { 85 - const subheader = document.querySelector(".subheader .schooltape"); 86 - if (!subheader) return; 87 - clockSpan = document.createElement("span"); 88 - clockSpan.classList.add("clock"); 89 - subheader.appendChild(clockSpan); 90 - } 91 - const date = new Date(); 92 - clockSpan.textContent = date.toLocaleTimeString([], { 93 - hour: "2-digit", 94 - minute: "2-digit", 95 - }); 96 - } 97 - 98 - function updateDateSpan() { 99 - let dateSpan = document.querySelector(".subheader .date"); 100 - if (!dateSpan) { 101 - const subheader = document.querySelector(".subheader .schooltape"); 102 - if (!subheader) return; 103 - dateSpan = document.createElement("span"); 104 - dateSpan.classList.add("date"); 105 - subheader.appendChild(dateSpan); 106 - } 107 - const date = new Date(); 108 - dateSpan.textContent = date.toDateString(); 109 - } 110 - }, 111 - [".subheader"], 112 - ); 113 - }
···
+16
src/entrypoints/plugins/tabTitle/Menu.svelte
···
··· 1 + <script lang="ts"> 2 + import Toggle from "@/entrypoints/popup/components/inputs/Toggle.svelte"; 3 + import type { Settings } from "."; 4 + 5 + let { settings }: { settings: Settings } = $props(); 6 + </script> 7 + 8 + <Toggle 9 + text="Show subject prefix" 10 + description="e.g. 'ENG - VCE English 1 & 2' becomes 'VCE English 1 & 2'" 11 + size="small" 12 + id="showSubjectPrefix" 13 + checked={settings.showSubjectPrefix.state.toggle} 14 + update={async (toggle) => { 15 + settings.showSubjectPrefix.set({ toggle }); 16 + }} />
+77
src/entrypoints/plugins/tabTitle/index.ts
···
··· 1 + import { Plugin } from "@/utils/plugin"; 2 + import type { Toggle } from "@/utils/storage"; 3 + import type { StorageState } from "@/utils/storage/state.svelte"; 4 + 5 + const ID = "tabTitle"; 6 + let originalTitle: string | null = null; 7 + 8 + export type Settings = { 9 + showSubjectPrefix: StorageState<Toggle>; 10 + }; 11 + 12 + export default new Plugin<Settings>( 13 + { 14 + id: ID, 15 + name: "Better Tab Titles", 16 + description: "Improves the tab titles for easier navigation.", 17 + }, 18 + true, 19 + { 20 + showSubjectPrefix: { toggle: true }, 21 + }, 22 + async (settings) => { 23 + // if already injected, abort 24 + if (originalTitle) return; 25 + 26 + // backup original title (used for uninjection) 27 + originalTitle = document.title; 28 + 29 + const path = window.location.pathname; 30 + const titleMap: { [key: string]: string } = { 31 + "/": "Homepage", 32 + "/calendar": "Calendar", 33 + "/news": "News", 34 + "/learning/classes": "Classes", 35 + "/resources": "Resources", 36 + "/groups": "Groups", 37 + "/settings/notifications": "Notifications Settings", 38 + "/mail/create": "Compose Email", 39 + "/feedback": "Support and Feedback", 40 + "/policy": "Guidelines of Use and Privacy Policy", 41 + }; 42 + 43 + if (titleMap[path]) { 44 + document.title = titleMap[path]; 45 + } else if (path.includes("/timetable")) { 46 + document.title = "Timetable"; 47 + } else if (path.includes("/calendar")) { 48 + document.title = "Calendar"; 49 + } else if (path.includes("/grades/")) { 50 + document.title = "Grades"; 51 + } else if (path.includes("/news/")) { 52 + document.title = `News (${document.getElementsByTagName("h1")[0].innerText})`; 53 + } else if (path.includes("/assessments/")) { 54 + document.title = `Assessments - ${document.getElementsByTagName("h1")[0].innerText})`; 55 + } else if (path.includes("/mail/create")) { 56 + document.title = "Compose Email"; 57 + } else if (path.includes("/search/user")) { 58 + document.title = `Profile - ${document.getElementsByTagName("h1")[0].innerText}`; 59 + } else if (path.includes("/learning/due/")) { 60 + document.title = "Due Work"; 61 + } else if (path.includes("/homepage/")) { 62 + if (!(await settings.showSubjectPrefix.get()).toggle) { 63 + document.title = document.getElementsByTagName("h1")[0].innerText.replace(/^.*- /, ""); 64 + } else { 65 + document.title = document.getElementsByTagName("h1")[0].innerText; 66 + } 67 + } 68 + }, 69 + () => { 70 + // if not injected, abort 71 + if (!originalTitle) return; 72 + 73 + document.title = originalTitle; 74 + originalTitle = null; 75 + }, 76 + ["h1"], 77 + );
-49
src/entrypoints/plugins/tabTitle.ts
··· 1 - import { definePlugin } from "@/utils/plugin"; 2 - 3 - export default function init() { 4 - definePlugin( 5 - "tabTitle", 6 - async (settings) => { 7 - const path = window.location.pathname; 8 - const titleMap: { [key: string]: string } = { 9 - "/": "Homepage", 10 - "/calendar": "Calendar", 11 - "/news": "News", 12 - "/learning/classes": "Classes", 13 - "/resources": "Resources", 14 - "/groups": "Groups", 15 - "/settings/notifications": "Notifications Settings", 16 - "/mail/create": "Compose Email", 17 - "/feedback": "Support and Feedback", 18 - "/policy": "Guidelines of Use and Privacy Policy", 19 - }; 20 - 21 - if (titleMap[path]) { 22 - document.title = titleMap[path]; 23 - } else if (path.includes("/timetable")) { 24 - document.title = "Timetable"; 25 - } else if (path.includes("/calendar")) { 26 - document.title = "Calendar"; 27 - } else if (path.includes("/grades/")) { 28 - document.title = "Grades"; 29 - } else if (path.includes("/news/")) { 30 - document.title = `News (${document.getElementsByTagName("h1")[0].innerText})`; 31 - } else if (path.includes("/assessments/")) { 32 - document.title = `Assessments - ${document.getElementsByTagName("h1")[0].innerText})`; 33 - } else if (path.includes("/mail/create")) { 34 - document.title = "Compose Email"; 35 - } else if (path.includes("/search/user")) { 36 - document.title = `Profile - ${document.getElementsByTagName("h1")[0].innerText}`; 37 - } else if (path.includes("/learning/due/")) { 38 - document.title = "Due Work"; 39 - } else if (path.includes("/homepage/")) { 40 - if (settings?.toggle.showSubjectPrefix === false) { 41 - document.title = document.getElementsByTagName("h1")[0].innerText.replace(/^.*- /, ""); 42 - } else { 43 - document.title = document.getElementsByTagName("h1")[0].innerText; 44 - } 45 - } 46 - }, 47 - ["h1"], 48 - ); 49 - }
···
+9 -7
src/entrypoints/plugins.content.ts
··· 8 import subheader from "./plugins/subheader"; 9 import tabTitle from "./plugins/tabTitle"; 10 11 export default defineContentScript({ 12 matches: ["<all_urls>"], 13 runAt: "document_start", 14 excludeMatches: EXCLUDE_MATCHES, 15 async main() { 16 - subheader(); 17 - scrollSegments(); 18 - scrollPeriod(); 19 - progressBar(); 20 - modernIcons(); 21 - tabTitle(); 22 - homepageSwitcher(); 23 }, 24 });
··· 8 import subheader from "./plugins/subheader"; 9 import tabTitle from "./plugins/tabTitle"; 10 11 + export const plugins = [subheader, scrollSegments, scrollPeriod, progressBar, modernIcons, tabTitle, homepageSwitcher]; 12 + 13 + export type PluginInstance = (typeof plugins)[number]; 14 + 15 export default defineContentScript({ 16 matches: ["<all_urls>"], 17 runAt: "document_start", 18 excludeMatches: EXCLUDE_MATCHES, 19 async main() { 20 + document.addEventListener("DOMContentLoaded", () => { 21 + for (const plugin of plugins) { 22 + plugin.init(); 23 + } 24 + }); 25 }, 26 });
+7 -28
src/entrypoints/popup/App.svelte
··· 1 <script lang="ts"> 2 import { flavors } from "@catppuccin/palette"; 3 - import { globalSettings, needsRefresh, schoolboxUrls, updated } from "@/utils/storage"; 4 - import { RotateCw } from "@lucide/svelte"; 5 - import { logger } from "@/utils/logger"; 6 - import { browser, onMount } from "#imports"; 7 8 import Router from "svelte-spa-router"; 9 import active from "svelte-spa-router/active"; ··· 11 import Plugins from "./routes/Plugins.svelte"; 12 import Themes from "./routes/Themes.svelte"; 13 import Snippets from "./routes/Snippets.svelte"; 14 - import Banner from "./components/Banner.svelte"; 15 16 const routes = { 17 "/": Home, ··· 20 "/snippets": Snippets, 21 }; 22 23 - async function refreshSchoolboxURLs() { 24 - logger.info("[App.svelte] Refreshing all Schoolbox URLs"); 25 - const urls = (await schoolboxUrls.storage.getValue()).urls.map((url) => url.replace(/^https:\/\//, "*://") + "/*"); 26 - const tabs = await browser.tabs.query({ url: urls }); 27 - tabs.forEach((tab) => { 28 - if (tab.id) { 29 - browser.tabs.reload(tab.id); 30 - } 31 - }); 32 - } 33 - 34 function getAccentRgb(accent: string, flavour: string) { 35 // eslint-disable-next-line @typescript-eslint/no-explicit-any 36 let x = (flavors as any)[flavour].colors[accent].rgb; ··· 40 let accentRgb = $derived(getAccentRgb(globalSettings.state.themeAccent, globalSettings.state.themeFlavour)); 41 42 onMount(() => { 43 - updated.set({ icon: false }); 44 - browser.runtime.sendMessage({ updateIcon: true }); 45 }); 46 </script> 47 48 <main 49 - class="bg-ctp-base flex flex-col items-center p-6 {globalSettings.state.themeFlavour}" 50 style="--ctp-accent: {accentRgb}"> 51 - <nav class="text-ctp-text mb-4 flex rounded-xl px-4 py-2" id="navbar"> 52 <a href="#/" class="navbutton-left" use:active={{ className: "active" }}>Settings</a> 53 <a href="#/plugins" class="navbutton-center" use:active={{ className: "active" }}>Plugins</a> 54 <a href="#/themes" class="navbutton-center" use:active={{ className: "active" }}>Themes</a> 55 <a href="#/snippets" class="navbutton-right" use:active={{ className: "active" }}>Snippets</a> 56 </nav> 57 - 58 - <Banner 59 - message="Click here to apply changes" 60 - visible={needsRefresh.state} 61 - onclick={() => { 62 - needsRefresh.storage.setValue(false); 63 - refreshSchoolboxURLs(); 64 - }}><RotateCw /></Banner> 65 66 <Router {routes} /> 67 </main>
··· 1 <script lang="ts"> 2 import { flavors } from "@catppuccin/palette"; 3 + import { globalSettings, updated } from "@/utils/storage"; 4 + import { onMount } from "#imports"; 5 6 import Router from "svelte-spa-router"; 7 import active from "svelte-spa-router/active"; ··· 9 import Plugins from "./routes/Plugins.svelte"; 10 import Themes from "./routes/Themes.svelte"; 11 import Snippets from "./routes/Snippets.svelte"; 12 + import { sendMessage } from "@/utils"; 13 14 const routes = { 15 "/": Home, ··· 18 "/snippets": Snippets, 19 }; 20 21 function getAccentRgb(accent: string, flavour: string) { 22 // eslint-disable-next-line @typescript-eslint/no-explicit-any 23 let x = (flavors as any)[flavour].colors[accent].rgb; ··· 27 let accentRgb = $derived(getAccentRgb(globalSettings.state.themeAccent, globalSettings.state.themeFlavour)); 28 29 onMount(() => { 30 + updated.update({ icon: false }); 31 + sendMessage({ type: "updateIcon" }); 32 }); 33 </script> 34 35 <main 36 + class="flex flex-col items-center bg-ctp-base p-6 {globalSettings.state.themeFlavour}" 37 style="--ctp-accent: {accentRgb}"> 38 + <nav class="mb-4 flex rounded-xl px-4 py-2 text-ctp-text" id="navbar"> 39 <a href="#/" class="navbutton-left" use:active={{ className: "active" }}>Settings</a> 40 <a href="#/plugins" class="navbutton-center" use:active={{ className: "active" }}>Plugins</a> 41 <a href="#/themes" class="navbutton-center" use:active={{ className: "active" }}>Themes</a> 42 <a href="#/snippets" class="navbutton-right" use:active={{ className: "active" }}>Snippets</a> 43 </nav> 44 45 <Router {routes} /> 46 </main>
+11 -11
src/entrypoints/popup/app.css
··· 5 6 body { 7 width: 450px; /* max: 1000px */ 8 - @apply bg-ctp-base text-ctp-text font-lexend text-base; 9 } 10 11 @theme { ··· 27 mask: var(--icon) no-repeat; 28 mask-size: 100% 100%; 29 background-color: currentColor; 30 - @apply text-(--ctp-accent) h-16 w-20; 31 } 32 33 .slider { 34 - @apply bg-ctp-surface1 after:bg-ctp-text peer-checked:after:bg-ctp-mantle peer-checked:bg-(--ctp-accent) ml-4 flex h-5 w-11 flex-shrink-0 items-center rounded-lg p-1 duration-500 ease-in-out after:rounded-lg after:shadow-md after:duration-300 group-hover:after:translate-x-1 peer-checked:after:translate-x-6; 35 } 36 37 .slider.big { ··· 59 } 60 61 #card { 62 - @apply bg-ctp-mantle bg-linear-to-b outline-(--ctp-accent) outline-solid flex w-full flex-col items-center justify-center rounded-3xl p-6 shadow-xl outline-2; 63 } 64 65 #card h1 { 66 - @apply from-ctp-blue to-ctp-teal bg-linear-to-r bg-clip-text text-5xl font-bold text-transparent; 67 68 &.ctp { 69 background-image: linear-gradient( ··· 79 } 80 81 #card h2 { 82 - @apply text-(--ctp-accent) text-4xl font-semibold; 83 } 84 85 #card h3 { ··· 95 } 96 97 button#toggle { 98 - @apply text-ctp-base rounded-xl px-4 py-2 text-xl font-bold; 99 } 100 101 button.small { ··· 123 } 124 125 .navbutton-left { 126 - @apply outline-ctp-overlay2/10 outline-solid hover:bg-(--ctp-accent)/50 active:bg-(--ctp-accent)/80 rounded-l-xl py-2 pl-4 pr-2 outline-1; 127 } 128 129 .navbutton-center { 130 - @apply outline-ctp-overlay2/10 outline-solid hover:bg-(--ctp-accent)/50 active:bg-(--ctp-accent)/80 p-2 outline-1; 131 } 132 133 .navbutton-right { 134 - @apply outline-ctp-overlay2/10 outline-solid hover:bg-(--ctp-accent)/50 active:bg-(--ctp-accent)/80 rounded-r-xl py-2 pl-2 pr-4 outline-1; 135 } 136 137 div[title] { ··· 139 } 140 141 div[title]:hover::after { 142 - @apply bg-ctp-surface0 text-ctp-text absolute bottom-full left-1/2 whitespace-nowrap; 143 content: attr(title); 144 transform: translateX(-50%); 145 padding: 0.5rem;
··· 5 6 body { 7 width: 450px; /* max: 1000px */ 8 + @apply bg-ctp-base font-lexend text-base text-ctp-text; 9 } 10 11 @theme { ··· 27 mask: var(--icon) no-repeat; 28 mask-size: 100% 100%; 29 background-color: currentColor; 30 + @apply h-16 w-20 text-(--ctp-accent); 31 } 32 33 .slider { 34 + @apply ml-4 flex h-5 w-11 flex-shrink-0 items-center rounded-lg bg-ctp-surface1 p-1 duration-500 ease-in-out peer-checked:bg-(--ctp-accent) after:rounded-lg after:bg-ctp-text after:shadow-md after:duration-300 group-hover:after:translate-x-1 peer-checked:after:translate-x-6 peer-checked:after:bg-ctp-mantle; 35 } 36 37 .slider.big { ··· 59 } 60 61 #card { 62 + @apply flex w-full flex-col items-center justify-center rounded-3xl bg-ctp-mantle bg-linear-to-b p-6 shadow-xl outline-2 outline-(--ctp-accent) outline-solid; 63 } 64 65 #card h1 { 66 + @apply bg-linear-to-r from-ctp-blue to-ctp-teal bg-clip-text text-5xl font-bold text-transparent; 67 68 &.ctp { 69 background-image: linear-gradient( ··· 79 } 80 81 #card h2 { 82 + @apply text-4xl font-semibold text-(--ctp-accent); 83 } 84 85 #card h3 { ··· 95 } 96 97 button#toggle { 98 + @apply rounded-xl px-4 py-2 text-xl font-bold text-ctp-base; 99 } 100 101 button.small { ··· 123 } 124 125 .navbutton-left { 126 + @apply rounded-l-xl py-2 pr-2 pl-4 outline-1 outline-ctp-overlay2/10 outline-solid hover:bg-(--ctp-accent)/50 active:bg-(--ctp-accent)/80; 127 } 128 129 .navbutton-center { 130 + @apply p-2 outline-1 outline-ctp-overlay2/10 outline-solid hover:bg-(--ctp-accent)/50 active:bg-(--ctp-accent)/80; 131 } 132 133 .navbutton-right { 134 + @apply rounded-r-xl py-2 pr-4 pl-2 outline-1 outline-ctp-overlay2/10 outline-solid hover:bg-(--ctp-accent)/50 active:bg-(--ctp-accent)/80; 135 } 136 137 div[title] { ··· 139 } 140 141 div[title]:hover::after { 142 + @apply absolute bottom-full left-1/2 bg-ctp-surface0 whitespace-nowrap text-ctp-text; 143 content: attr(title); 144 transform: translateX(-50%); 145 padding: 0.5rem;
+1 -1
src/entrypoints/popup/components/Banner.svelte
··· 4 5 {#if visible} 6 <button 7 - class="banner bg-ctp-blue text-ctp-crust mb-4 flex w-full cursor-pointer items-center justify-center gap-2 rounded-lg p-4" 8 {onclick}> 9 {@render children()} 10 <p>{message}</p>
··· 4 5 {#if visible} 6 <button 7 + class="banner mb-4 flex w-full cursor-pointer items-center justify-center gap-2 rounded-lg bg-ctp-blue p-4 text-ctp-crust" 8 {onclick}> 9 {@render children()} 10 <p>{message}</p>
+8 -7
src/entrypoints/popup/components/Footer.svelte
··· 4 import { updated } from "@/utils/storage"; 5 import Button from "./inputs/Button.svelte"; 6 import { RotateCcw, Globe, GitBranch } from "@lucide/svelte"; 7 8 let version = $state(); 9 ··· 18 <footer class="mt-4 flex min-w-full justify-around p-4"> 19 <span class="relative inline-flex"> 20 <Button 21 - onclick={async () => { 22 - await updated.set({ changelog: false }); 23 24 browser.tabs.create({ 25 url: `https://github.com/schooltape/schooltape/releases/tag/v${version}`, ··· 31 <GitBranch size={18} /> v{version} 32 <!-- show ripple badge if the extension has been updated (unread release notes) --> 33 {#if updated.state.changelog} 34 - <span class="absolute right-0 top-0 -mr-1 -mt-1 flex size-3"> 35 - <span class="bg-ctp-blue absolute inline-flex h-full w-full animate-ping rounded-full opacity-75"></span> 36 - <span class="bg-ctp-blue relative inline-flex size-3 rounded-full"></span> 37 </span> 38 {/if} 39 </Button> ··· 52 onclick={() => { 53 window.open("https://discord.gg/rZxtGJ98BE", "_blank"); 54 }} 55 - ><svg class="fill-ctp-text h-[22px]" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" 56 ><title>Discord</title><path 57 d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" /></svg 58 ></Button> ··· 63 classList="text-ctp-text hover:text-ctp-base hover:bg-ctp-red" 64 onclick={() => { 65 if (confirm("Are you sure you want to reset all settings?")) { 66 - browser.runtime.sendMessage({ resetSettings: true }); 67 location.reload(); 68 } 69 }}><RotateCcw size={22} /></Button>
··· 4 import { updated } from "@/utils/storage"; 5 import Button from "./inputs/Button.svelte"; 6 import { RotateCcw, Globe, GitBranch } from "@lucide/svelte"; 7 + import { sendMessage } from "@/utils"; 8 9 let version = $state(); 10 ··· 19 <footer class="mt-4 flex min-w-full justify-around p-4"> 20 <span class="relative inline-flex"> 21 <Button 22 + onclick={() => { 23 + updated.update({ changelog: false }); 24 25 browser.tabs.create({ 26 url: `https://github.com/schooltape/schooltape/releases/tag/v${version}`, ··· 32 <GitBranch size={18} /> v{version} 33 <!-- show ripple badge if the extension has been updated (unread release notes) --> 34 {#if updated.state.changelog} 35 + <span class="absolute top-0 right-0 -mt-1 -mr-1 flex size-3"> 36 + <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-ctp-blue opacity-75"></span> 37 + <span class="relative inline-flex size-3 rounded-full bg-ctp-blue"></span> 38 </span> 39 {/if} 40 </Button> ··· 53 onclick={() => { 54 window.open("https://discord.gg/rZxtGJ98BE", "_blank"); 55 }} 56 + ><svg class="h-[22px] fill-ctp-text" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" 57 ><title>Discord</title><path 58 d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" /></svg 59 ></Button> ··· 64 classList="text-ctp-text hover:text-ctp-base hover:bg-ctp-red" 65 onclick={() => { 66 if (confirm("Are you sure you want to reset all settings?")) { 67 + sendMessage({ type: "resetSettings" }); 68 location.reload(); 69 } 70 }}><RotateCcw size={22} /></Button>
+2 -3
src/entrypoints/popup/components/Modal.svelte
··· 15 onclick={(e) => { 16 if (e.target === dialog) dialog.close(); 17 }} 18 - class="bg-ctp-base text-ctp-text open:animate-zoom-in relative m-auto rounded-lg backdrop:backdrop-blur-md"> 19 - <!-- svelte-ignore a11y_autofocus --> 20 - <button autofocus onclick={() => dialog?.close()} class="small bg-ctp-surface1 absolute right-0 top-0 m-2" 21 ><X /></button> 22 23 <div class="p-4">
··· 15 onclick={(e) => { 16 if (e.target === dialog) dialog.close(); 17 }} 18 + class="relative m-auto w-screen rounded-lg bg-ctp-base text-ctp-text backdrop:backdrop-blur-md open:animate-zoom-in"> 19 + <button autofocus onclick={() => dialog?.close()} class="small absolute top-0 right-0 m-2 bg-ctp-surface1" 20 ><X /></button> 21 22 <div class="p-4">
+1 -1
src/entrypoints/popup/components/Motd.svelte
··· 12 </script> 13 14 <!-- MOTD --> 15 - <div class="text-ctp-subtext0 text-center italic"> 16 <!-- eslint-disable-next-line svelte/no-at-html-tags --> 17 <p>{@html motd.state.motd}</p> 18 <!-- Free and <a href='https://github.com/schooltape/schooltape' class='text-(--ctp-accent)'> open source</a>! -->
··· 12 </script> 13 14 <!-- MOTD --> 15 + <div class="text-center text-ctp-subtext0 italic"> 16 <!-- eslint-disable-next-line svelte/no-at-html-tags --> 17 <p>{@html motd.state.motd}</p> 18 <!-- Free and <a href='https://github.com/schooltape/schooltape' class='text-(--ctp-accent)'> open source</a>! -->
+1 -1
src/entrypoints/popup/components/Title.svelte
··· 10 let { update, title = "", checked }: Props = $props(); 11 </script> 12 13 - <label for="theme-toggle" class="text-ctp-text group relative flex items-center justify-between p-2 text-xl"> 14 <h2>{title}</h2> 15 <Toggle id="theme-toggle" size="big" {checked} {update} /> 16 </label>
··· 10 let { update, title = "", checked }: Props = $props(); 11 </script> 12 13 + <label for="theme-toggle" class="group relative flex items-center justify-between p-2 text-xl text-ctp-text"> 14 <h2>{title}</h2> 15 <Toggle id="theme-toggle" size="big" {checked} {update} /> 16 </label>
+1 -1
src/entrypoints/popup/components/inputs/Button.svelte
··· 12 let { children, title, id, onclick, classList = "text-ctp-text hover:bg-ctp-surface1" }: Props = $props(); 13 </script> 14 15 - <button {title} {id} class="bg-ctp-surface0 small flex h-8 cursor-pointer items-center gap-3 {classList}" {onclick}> 16 {@render children()} 17 </button>
··· 12 let { children, title, id, onclick, classList = "text-ctp-text hover:bg-ctp-surface1" }: Props = $props(); 13 </script> 14 15 + <button {title} {id} class="small flex h-8 cursor-pointer items-center gap-3 bg-ctp-surface0 {classList}" {onclick}> 16 {@render children()} 17 </button>
+2 -2
src/entrypoints/popup/components/inputs/Slider.svelte
··· 15 16 <div class="mt-4 space-y-2"> 17 {#if name} 18 - <label for={id} class="text-ctp-text block">{name}</label> 19 {/if} 20 <div class="flex items-center gap-4"> 21 <input ··· 34 {max} 35 {value} 36 class="styled-slider slider-progress" /> 37 - <span id={id + "-value"} class="text-ctp-text text-sm font-medium">{currentValue}</span> 38 </div> 39 {#if description} 40 <p class="text-ctp-overlay1">{description}</p>
··· 15 16 <div class="mt-4 space-y-2"> 17 {#if name} 18 + <label for={id} class="block text-ctp-text">{name}</label> 19 {/if} 20 <div class="flex items-center gap-4"> 21 <input ··· 34 {max} 35 {value} 36 class="styled-slider slider-progress" /> 37 + <span id={id + "-value"} class="text-sm font-medium text-ctp-text">{currentValue}</span> 38 </div> 39 {#if description} 40 <p class="text-ctp-overlay1">{description}</p>
+2 -2
src/entrypoints/popup/components/inputs/TextInput.svelte
··· 11 </script> 12 13 <div class="flex w-full items-center justify-center"> 14 - <input {id} class="bg-ctp-surface0 text-ctp-text w-full rounded-l-xl p-2" bind:value {placeholder} type="text" /> 15 - <button class="text-ctp-crust bg-(--ctp-accent) rounded-r-xl p-2" type="submit" {onclick}>{label}</button> 16 </div>
··· 11 </script> 12 13 <div class="flex w-full items-center justify-center"> 14 + <input {id} class="w-full rounded-l-xl bg-ctp-surface0 p-2 text-ctp-text" bind:value {placeholder} type="text" /> 15 + <button class="rounded-r-xl bg-(--ctp-accent) p-2 text-ctp-crust" type="submit" {onclick}>{label}</button> 16 </div>
+2 -2
src/entrypoints/popup/components/inputs/Toggle.svelte
··· 14 let { update, checked, id, size = "big", text = "", description = "", children }: Props = $props(); 15 </script> 16 17 - <label class="text-ctp-text group relative flex cursor-pointer items-center justify-between py-2"> 18 <h4 class="text-ctp-text">{text}</h4> 19 <input 20 {id} ··· 29 </label> 30 31 <div 32 - class="text-ctp-overlay1 group-hover:text-ctp-subtext0 flex items-center justify-between transition-colors duration-500 ease-in-out"> 33 <div>{description}</div> 34 <div>{@render children?.()}</div> 35 </div>
··· 14 let { update, checked, id, size = "big", text = "", description = "", children }: Props = $props(); 15 </script> 16 17 + <label class="group relative flex cursor-pointer items-center justify-between py-2 text-ctp-text"> 18 <h4 class="text-ctp-text">{text}</h4> 19 <input 20 {id} ··· 29 </label> 30 31 <div 32 + class="flex items-center justify-between text-ctp-overlay1 transition-colors duration-500 ease-in-out group-hover:text-ctp-subtext0"> 33 <div>{description}</div> 34 <div>{@render children?.()}</div> 35 </div>
+2 -2
src/entrypoints/popup/routes/Home.svelte
··· 14 <button 15 class="bg-(--ctp-accent) hover:opacity-75 {globalSettings.state.global ? '' : 'opacity-60'}" 16 id="toggle" 17 - onclick={() => { 18 - globalSettings.set({ global: !globalSettings.get().global }); 19 }} 20 >{globalSettings.state.global ? "enabled" : "disabled"} 21 </button>
··· 14 <button 15 class="bg-(--ctp-accent) hover:opacity-75 {globalSettings.state.global ? '' : 'opacity-60'}" 16 id="toggle" 17 + onclick={async () => { 18 + globalSettings.update({ global: !(await globalSettings.get()).global }); 19 }} 20 >{globalSettings.state.global ? "enabled" : "disabled"} 21 </button>
+26 -39
src/entrypoints/popup/routes/Plugins.svelte
··· 1 <script lang="ts"> 2 - import type { PluginId } from "@/utils/storage"; 3 - import { globalSettings, plugins } from "@/utils/storage"; 4 import { Settings } from "@lucide/svelte"; 5 import Title from "../components/Title.svelte"; 6 import Button from "../components/inputs/Button.svelte"; 7 import Modal from "../components/Modal.svelte"; 8 import Toggle from "../components/inputs/Toggle.svelte"; 9 - import Slider from "../components/inputs/Slider.svelte"; 10 11 let showModal = $state(false); 12 - let selectedPluginId: PluginId | undefined = $state(); 13 - let selectedPlugin = $derived.by(() => { 14 - if (selectedPluginId !== undefined) { 15 - return plugins[selectedPluginId]; 16 } 17 }); 18 </script> ··· 22 title="Plugins" 23 checked={globalSettings.state.plugins} 24 update={(toggled: boolean) => { 25 - globalSettings.set({ plugins: toggled }); 26 }} /> 27 28 <div class="plugins-container"> 29 - {#each Object.entries(plugins) as [id, plugin] (id)} 30 <div class="group my-4"> 31 <Toggle 32 - {id} 33 checked={plugin.toggle.state.toggle} 34 update={(toggled: boolean) => { 35 plugin.toggle.set({ toggle: toggled }); 36 }} 37 - text={plugin.name} 38 - description={plugin.description} 39 size="small"> 40 - {#if plugin.settings !== undefined} 41 <Button 42 - title={plugin.name + " Settings"} 43 - {id} 44 onclick={() => { 45 - selectedPluginId = id as PluginId; 46 showModal = true; 47 }}><Settings size={22} /></Button> 48 {/if} ··· 55 {#if selectedPlugin} 56 <Modal bind:showModal> 57 {#snippet header()} 58 - <h2 class="mb-4 text-xl">{selectedPlugin.name}</h2> 59 {/snippet} 60 - {#if selectedPlugin.settings !== undefined} 61 - {#each Object.entries(selectedPlugin.settings) as [id, setting] (id)} 62 - {#if setting.type === "toggle"} 63 - <Toggle 64 - text={setting.name} 65 - description={setting.description} 66 - size="small" 67 - checked={setting.state.state.toggle} 68 - update={async (toggled) => { 69 - setting.state.set({ toggle: toggled }); 70 - }} 71 - {id} /> 72 - {:else if setting.type === "slider"} 73 - <Slider 74 - {id} 75 - update={async (newValue) => { 76 - setting.state.set({ value: newValue }); 77 - }} 78 - {...setting.state.state} /> 79 - {/if} 80 - {/each} 81 - {/if} 82 </Modal> 83 {/if}
··· 1 <script lang="ts"> 2 + import { globalSettings } from "@/utils/storage"; 3 import { Settings } from "@lucide/svelte"; 4 import Title from "../components/Title.svelte"; 5 import Button from "../components/inputs/Button.svelte"; 6 import Modal from "../components/Modal.svelte"; 7 import Toggle from "../components/inputs/Toggle.svelte"; 8 + import { plugins } from "@/entrypoints/plugins.content"; 9 + import type { PluginInstance } from "@/entrypoints/plugins.content"; 10 + import { onMount } from "svelte"; 11 + import type { Component } from "svelte"; 12 13 let showModal = $state(false); 14 + let components: Record<string, Component> = $state({}); 15 + let selectedPlugin: PluginInstance | undefined = $state(); 16 + let Menu = $derived(selectedPlugin ? components[selectedPlugin.meta.id] : undefined); 17 + 18 + onMount(async () => { 19 + for (const plugin of plugins) { 20 + if (!plugin.settings) continue; 21 + components[plugin.meta.id] = (await import(`@/entrypoints/plugins/${plugin.meta.id}/Menu.svelte`)).default; 22 } 23 }); 24 </script> ··· 28 title="Plugins" 29 checked={globalSettings.state.plugins} 30 update={(toggled: boolean) => { 31 + globalSettings.update({ plugins: toggled }); 32 }} /> 33 34 <div class="plugins-container"> 35 + {#each plugins as plugin (plugin.meta.id)} 36 <div class="group my-4"> 37 <Toggle 38 + id={plugin.meta.id} 39 + text={plugin.meta.name} 40 + description={plugin.meta.description} 41 checked={plugin.toggle.state.toggle} 42 update={(toggled: boolean) => { 43 plugin.toggle.set({ toggle: toggled }); 44 }} 45 size="small"> 46 + {#if plugin.settings} 47 <Button 48 + title={plugin.meta.name + " Settings"} 49 + id={plugin.meta.id} 50 onclick={() => { 51 + selectedPlugin = plugin; 52 showModal = true; 53 }}><Settings size={22} /></Button> 54 {/if} ··· 61 {#if selectedPlugin} 62 <Modal bind:showModal> 63 {#snippet header()} 64 + {#if selectedPlugin} 65 + <h2 class="mb-4 text-xl">{selectedPlugin.meta.name}</h2> 66 + {/if} 67 {/snippet} 68 + <Menu settings={selectedPlugin.settings} /> 69 </Modal> 70 {/if}
+18 -19
src/entrypoints/popup/routes/Snippets.svelte
··· 1 <script lang="ts"> 2 - import type { SnippetId, UserSnippet } from "@/utils/storage"; 3 - import { globalSettings, snippets } from "@/utils/storage"; 4 5 import Title from "../components/Title.svelte"; 6 import Toggle from "../components/inputs/Toggle.svelte"; ··· 24 let sections = snippetURL.split("/"); 25 let key = sections[sections.length - 1].split(".")[0]; 26 27 - let settings = await globalSettings.storage.getValue(); 28 settings.userSnippets[key] = { 29 author: sections[3], 30 name: getMatch(data, /\/\*\s*name:\s*(.*?)\s*\*\//) || key, ··· 32 url: snippetURL, 33 toggle: true, 34 }; 35 - await globalSettings.storage.setValue(settings); 36 } 37 </script> 38 ··· 41 title="Snippets" 42 checked={globalSettings.state.snippets} 43 update={(toggled: boolean) => { 44 - globalSettings.set({ snippets: toggled }); 45 }} /> 46 47 <div class="snippets-container w-full"> 48 - {#each Object.entries(snippets) as [id, snippet] (id)} 49 <div class="group my-4 w-full"> 50 <Toggle 51 - {id} 52 checked={snippet.toggle.state.toggle} 53 - update={(toggled: boolean) => { 54 - snippet.toggle.set({ toggle: toggled }); 55 - }} 56 - text={snippet.name} 57 - description={snippet.description} 58 size="small" /> 59 </div> 60 {/each} 61 </div> 62 <div class="w-full"> 63 - <h3 class="text-ctp-text my-4">User Snippets</h3> 64 - <p class="text-ctp-overlay2 mb-4"> 65 To learn how to make your own snippets, please read the 66 <a 67 class="text-ctp-blue hover:underline" ··· 80 {id} 81 checked={snippet.toggle} 82 update={async (toggled: boolean) => { 83 - let settings = await globalSettings.storage.getValue(); 84 settings.userSnippets[id].toggle = toggled; 85 - await globalSettings.storage.setValue(settings); 86 }} 87 text={snippet.name} 88 description={snippet.description} ··· 90 <button 91 class="xsmall hover:bg-ctp-red hover:text-ctp-mantle" 92 onclick={async () => { 93 - let settings = await globalSettings.storage.getValue(); 94 delete settings.userSnippets[id]; 95 - await globalSettings.storage.setValue(settings); 96 }}>Remove</button> 97 <a href={snippet.url} target="_blank" 98 - ><button class="xsmall hover:text-ctp-mantle hover:bg-(--ctp-accent)">Gist</button></a> 99 </div> 100 {/each} 101 </div>
··· 1 <script lang="ts"> 2 + import type { UserSnippet } from "@/utils/storage"; 3 + import { globalSettings } from "@/utils/storage"; 4 + import { snippets } from "@/entrypoints/snippets.content"; 5 6 import Title from "../components/Title.svelte"; 7 import Toggle from "../components/inputs/Toggle.svelte"; ··· 25 let sections = snippetURL.split("/"); 26 let key = sections[sections.length - 1].split(".")[0]; 27 28 + let settings = await globalSettings.get(); 29 settings.userSnippets[key] = { 30 author: sections[3], 31 name: getMatch(data, /\/\*\s*name:\s*(.*?)\s*\*\//) || key, ··· 33 url: snippetURL, 34 toggle: true, 35 }; 36 + await globalSettings.set(settings); 37 } 38 </script> 39 ··· 42 title="Snippets" 43 checked={globalSettings.state.snippets} 44 update={(toggled: boolean) => { 45 + globalSettings.update({ snippets: toggled }); 46 }} /> 47 48 <div class="snippets-container w-full"> 49 + {#each snippets as snippet (snippet.meta.id)} 50 <div class="group my-4 w-full"> 51 <Toggle 52 + id={snippet.meta.id} 53 checked={snippet.toggle.state.toggle} 54 + update={(toggle) => snippet.toggle.set({ toggle })} 55 + text={snippet.meta.name} 56 + description={snippet.meta.description} 57 size="small" /> 58 </div> 59 {/each} 60 </div> 61 <div class="w-full"> 62 + <h3 class="my-4 text-ctp-text">User Snippets</h3> 63 + <p class="mb-4 text-ctp-overlay2"> 64 To learn how to make your own snippets, please read the 65 <a 66 class="text-ctp-blue hover:underline" ··· 79 {id} 80 checked={snippet.toggle} 81 update={async (toggled: boolean) => { 82 + let settings = await globalSettings.get(); 83 settings.userSnippets[id].toggle = toggled; 84 + await globalSettings.set(settings); 85 }} 86 text={snippet.name} 87 description={snippet.description} ··· 89 <button 90 class="xsmall hover:bg-ctp-red hover:text-ctp-mantle" 91 onclick={async () => { 92 + let settings = await globalSettings.get(); 93 delete settings.userSnippets[id]; 94 + await globalSettings.set(settings); 95 }}>Remove</button> 96 <a href={snippet.url} target="_blank" 97 + ><button class="xsmall hover:bg-(--ctp-accent) hover:text-ctp-mantle">Gist</button></a> 98 </div> 99 {/each} 100 </div>
+7 -7
src/entrypoints/popup/routes/Themes.svelte
··· 45 {#each Object.entries(logos) as [logoId, logo] (logoId)} 46 <button 47 onclick={() => { 48 - globalSettings.set({ themeLogo: logoId as LogoId }); 49 }} 50 class:highlight={globalSettings.state.themeLogo === logoId} 51 - class="border-(--ctp-accent) flex flex-col rounded-lg border p-2"> 52 <span>{logo.name}</span> 53 {#if logo.disable !== true} 54 <div class="flex h-full w-full items-center justify-center"> ··· 67 <div class="mt-4"> 68 <Toggle 69 update={(toggled) => { 70 - globalSettings.set({ themeLogoAsFavicon: toggled }); 71 }} 72 checked={globalSettings.state.themeLogoAsFavicon} 73 id="setAsFavicon" ··· 81 title="Themes" 82 checked={globalSettings.state.themes} 83 update={(toggled: boolean) => { 84 - globalSettings.set({ themes: toggled }); 85 }} /> 86 87 - <div id="flavours" class="text-ctp-text my-6 flex rounded-xl py-2"> 88 {#each flavours as flavour (flavour)} 89 <button 90 class:active={globalSettings.state.themeFlavour === flavour} ··· 92 class:navbutton-right={flavour === "mocha"} 93 class:navbutton-center={flavour === "macchiato" || flavour === "frappe"} 94 onclick={() => { 95 - globalSettings.set({ themeFlavour: flavour }); 96 }}>{flavour}</button> 97 {/each} 98 </div> ··· 105 aria-label={cleanAccent(accent)} 106 title={cleanAccent(accent)} 107 onclick={() => { 108 - globalSettings.set({ themeAccent: cleanAccent(accent) }); 109 }}></button> 110 {/each} 111 </div>
··· 45 {#each Object.entries(logos) as [logoId, logo] (logoId)} 46 <button 47 onclick={() => { 48 + globalSettings.update({ themeLogo: logoId as LogoId }); 49 }} 50 class:highlight={globalSettings.state.themeLogo === logoId} 51 + class="flex flex-col rounded-lg border border-(--ctp-accent) p-2"> 52 <span>{logo.name}</span> 53 {#if logo.disable !== true} 54 <div class="flex h-full w-full items-center justify-center"> ··· 67 <div class="mt-4"> 68 <Toggle 69 update={(toggled) => { 70 + globalSettings.update({ themeLogoAsFavicon: toggled }); 71 }} 72 checked={globalSettings.state.themeLogoAsFavicon} 73 id="setAsFavicon" ··· 81 title="Themes" 82 checked={globalSettings.state.themes} 83 update={(toggled: boolean) => { 84 + globalSettings.update({ themes: toggled }); 85 }} /> 86 87 + <div id="flavours" class="my-6 flex rounded-xl py-2 text-ctp-text"> 88 {#each flavours as flavour (flavour)} 89 <button 90 class:active={globalSettings.state.themeFlavour === flavour} ··· 92 class:navbutton-right={flavour === "mocha"} 93 class:navbutton-center={flavour === "macchiato" || flavour === "frappe"} 94 onclick={() => { 95 + globalSettings.update({ themeFlavour: flavour }); 96 }}>{flavour}</button> 97 {/each} 98 </div> ··· 105 aria-label={cleanAccent(accent)} 106 title={cleanAccent(accent)} 107 onclick={() => { 108 + globalSettings.update({ themeAccent: cleanAccent(accent) }); 109 }}></button> 110 {/each} 111 </div>
+12
src/entrypoints/snippets/censor/index.ts
···
··· 1 + import { Snippet } from "@/utils/snippet"; 2 + import styles from "./styles.css?inline"; 3 + 4 + export default new Snippet( 5 + { 6 + id: "censor", 7 + name: "Censor", 8 + description: "Censors all text and images. This is intended for development purposes.", 9 + }, 10 + false, 11 + styles, 12 + );
+15
src/entrypoints/snippets/censor/styles.css
···
··· 1 + @import url("https://fonts.googleapis.com/css2?family=Flow+Circular&display=swap"); 2 + 3 + *, 4 + #side-menu > h3 { 5 + font-family: "Flow Circular", cursive; 6 + } 7 + 8 + .Component_Dashboard_TextboxController, 9 + .tileList { 10 + display: none; 11 + } 12 + 13 + img:not(.account-menu > img) { 14 + display: none; 15 + }
-15
src/entrypoints/snippets/censor.css
··· 1 - @import url("https://fonts.googleapis.com/css2?family=Flow+Circular&display=swap"); 2 - 3 - *, 4 - #side-menu > h3 { 5 - font-family: "Flow Circular", cursive; 6 - } 7 - 8 - .Component_Dashboard_TextboxController, 9 - .tileList { 10 - display: none; 11 - } 12 - 13 - img:not(.account-menu > img) { 14 - display: none; 15 - }
···
+12
src/entrypoints/snippets/hidePfp/index.ts
···
··· 1 + import { Snippet } from "@/utils/snippet"; 2 + import styles from "./styles.css?inline"; 3 + 4 + export default new Snippet( 5 + { 6 + id: "hidePfp", 7 + name: "Hide Profile Picture", 8 + description: "Hide your profile picture across Schoolbox.", 9 + }, 10 + true, 11 + styles, 12 + );
+12
src/entrypoints/snippets/hidePwaPrompt/index.ts
···
··· 1 + import { Snippet } from "@/utils/snippet"; 2 + import styles from "./styles.css?inline"; 3 + 4 + export default new Snippet( 5 + { 6 + id: "hidePwaPrompt", 7 + name: "Hide PWA Prompt", 8 + description: "Hide the prompt in the notifications menu to install Schoolbox as a PWA and enable notifications.", 9 + }, 10 + true, 11 + styles, 12 + );
+3
src/entrypoints/snippets/hidePwaPrompt/styles.css
···
··· 1 + [data-vue-app="pwa-menu"] { 2 + display: none; 3 + }
-3
src/entrypoints/snippets/hidePwaPrompt.css
··· 1 - [data-vue-app="pwa-menu"] { 2 - display: none; 3 - }
···
+12
src/entrypoints/snippets/roundedCorners/index.ts
···
··· 1 + import { Snippet } from "@/utils/snippet"; 2 + import styles from "./styles.css?inline"; 3 + 4 + export default new Snippet( 5 + { 6 + id: "roundedCorners", 7 + name: "Rounded Corners", 8 + description: "Hide your profile picture across Schoolbox.", 9 + }, 10 + true, 11 + styles, 12 + );
+194
src/entrypoints/snippets/roundedCorners/styles.css
···
··· 1 + /* 2 + Be specific as possible with your selectors, try and use the verbose Schoolbox component class names 3 + For example: 4 + .Schoolbox_Learning_Component_Dashboard_UpcomingWorkController <your selectors> 5 + */ 6 + 7 + /* ----- global ----- */ 8 + 9 + /* div#calendar-15403, TODO)) not sure if I need this 10 + .scrollable, 11 + section.content, */ 12 + .tile, 13 + .button:not(.icon-search):not(.mark-all-read) { 14 + border-radius: 10px !important; 15 + overflow: clip; 16 + } 17 + 18 + /* advanced search button */ 19 + .icon-search.button { 20 + border-radius: 0px 0px 10px 10px !important; 21 + } 22 + .c-header-search__results { 23 + border: none !important; 24 + } 25 + 26 + .small-12.island > section { 27 + border-radius: 0px 0px 10px 10px; 28 + overflow: clip; 29 + } 30 + 31 + /* timetable on homepage */ 32 + div[data-timetable-container] > section:has(.timetable) { 33 + border-radius: 10px !important; 34 + overflow: clip; 35 + } 36 + 37 + /* account and action dropdown */ 38 + #account-content, 39 + .f-dropdown { 40 + border-radius: 0px 0px 10px 10px !important; 41 + } 42 + /* see all notifications button */ 43 + #message-list > li > a.button:not(.mark-all-read) { 44 + border-radius: 0px 0px 10px 10px !important; 45 + } 46 + 47 + /* round action buttons */ 48 + .actions-small-3 > nav a[class*="icon-"]:hover, 49 + .actions-small-3 > nav a[class^="icon-"]:hover, 50 + .island .row.actions-small-3 > nav a[class*="icon-"]:hover, 51 + .island .row.actions-small-3 > nav a[class^="icon-"]:hover, 52 + .left-off-canvas-menu .left-menu-dock:hover { 53 + border-radius: 10px; 54 + } 55 + 56 + /* ----- notifications page ----- */ 57 + /* url:/notifications */ 58 + 59 + .notification-list .information-list > li:first-child > div, 60 + .notification-list .information-list > li:first-child { 61 + border-radius: 10px 10px 0px 0px; 62 + } 63 + .notification-list .information-list > li:last-child > div, 64 + .notification-list .information-list > li:last-child { 65 + border-radius: 0px 0px 10px 10px; 66 + } 67 + .notification-list .information-list, 68 + .notification-list .information-list > li:only-child > div { 69 + border-radius: 10px; 70 + } 71 + 72 + /* news component */ 73 + #news-component { 74 + /* round tabs at the bottom */ 75 + & .tabs { 76 + border-radius: 10px 10px 0px 0px; 77 + } 78 + 79 + /* round the news cards */ 80 + & ul.information-list { 81 + background: none !important; 82 + & > li { 83 + background-color: hsl(var(--ctp-surface0)) !important; 84 + /* round the bottom of the last card */ 85 + &:last-child { 86 + border-radius: 0px 0px 10px 10px !important; 87 + } 88 + } 89 + } 90 + } 91 + 92 + /* empty component */ 93 + .Schoolbox_Calendar_Component_Dashboard_Controller.component-container .empty-state { 94 + border-radius: 10px; 95 + } 96 + 97 + /* upcoming work component */ 98 + .Schoolbox_Learning_Component_Dashboard_UpcomingWorkController .information-list, 99 + .Schoolbox_Learning_Component_Dashboard_UpcomingWorkController .information-list .card { 100 + border-radius: 10px !important; 101 + } 102 + 103 + /* calendar */ 104 + .calendar-15403, 105 + .fc { 106 + border-radius: 10px; 107 + } 108 + 109 + /* slideshow */ 110 + .Component_Dashboard_SlideshowController.component-container .swiper { 111 + border-radius: 10px !important; 112 + } 113 + 114 + /* ----- calendar page ----- */ 115 + /* url:/calendar */ 116 + 117 + /* buttons */ 118 + .fc-button-group > button:first-child { 119 + border-radius: 10px 0px 0px 10px; 120 + } 121 + .fc-button-group > button:last-child { 122 + border-radius: 0px 10px 10px 0px; 123 + } 124 + 125 + /* ----- classes page ----- */ 126 + /* url:/learning/classes */ 127 + 128 + .v-card { 129 + border-radius: 10px; 130 + } 131 + .card-class-image { 132 + border-radius: 10px 10px 0px 0px; 133 + } 134 + 135 + /* ----- homepage (class pages) ----- */ 136 + /* url:/homepage/code/:id */ 137 + 138 + div.columns .component-titlebar { 139 + border-radius: 10px; 140 + } 141 + 142 + /* social stream */ 143 + .Component_Homepage_SocialStreamController section, 144 + .Component_Homepage_SocialStreamController .information-list { 145 + border-radius: 10px; 146 + } 147 + 148 + /* news */ 149 + .component-action > section { 150 + background-color: var(--left-bg-primary) !important; 151 + border-radius: 10px !important; 152 + border: none !important; 153 + } 154 + .component-action { 155 + background: var(--left-bg-primary) !important; 156 + border-radius: 0px 0px 10px 10px; 157 + } 158 + .Schoolbox_Comms_News_Component_Homepage_Controller.component-container .island > section, 159 + .Component_Homepage_ImageController.component-container .island > section, 160 + .Component_Homepage_ClassListController.component-container .island > section { 161 + background: none !important; 162 + } 163 + 164 + .Component_Homepage_CountdownController.component-container, 165 + .Schoolbox_Resource_Textbox_Component_Homepage_Controller.component-container, 166 + .Component_Homepage_SocialStreamController.component-container, 167 + .Component_Homepage_SlideshowController.component-container, 168 + .Component_Homepage_AudioController.component-container, 169 + .Component_Homepage_TeachersController.component-container, 170 + .Component_Homepage_ClassListController.component-container, 171 + .Schoolbox_Comms_News_Component_Homepage_Controller.component-container, 172 + .Component_Homepage_LinkListController.component-container, 173 + .Component_Homepage_FileListController.component-container, 174 + .Schoolbox_Learning_Assessment_Component_Homepage_Outline_Controller.component-container, 175 + .Schoolbox_Learning_Assessment_DueWork_Component_Homepage_Controller.component-container, 176 + .Schoolbox_Learning_Assessment_Task_Component_Homepage_Controller.component-container, 177 + .Schoolbox_Learning_Assessment_Quiz_Component_Homepage_Controller.component-container, 178 + .Component_Homepage_ImageController.component-container, 179 + .Schoolbox_Tile_Component_HomepageTileController { 180 + background: hsl(var(--ctp-accent)); 181 + border-radius: 10px; 182 + } 183 + 184 + .Component_Homepage_ImageController.component-container img { 185 + border-radius: 0px 0px 10px 10px !important; 186 + } 187 + 188 + /* round some other components and clip overflow */ 189 + 190 + .Schoolbox_Fixtures_Component_Dashboard_Controller section, 191 + .Component_Dashboard_TextboxController section { 192 + border-radius: 10px !important; 193 + overflow: clip !important; 194 + }
-194
src/entrypoints/snippets/roundedCorners.css
··· 1 - /* 2 - Be specific as possible with your selectors, try and use the verbose Schoolbox component class names 3 - For example: 4 - .Schoolbox_Learning_Component_Dashboard_UpcomingWorkController <your selectors> 5 - */ 6 - 7 - /* ----- global ----- */ 8 - 9 - /* div#calendar-15403, TODO)) not sure if I need this 10 - .scrollable, 11 - section.content, */ 12 - .tile, 13 - .button:not(.icon-search):not(.mark-all-read) { 14 - border-radius: 10px !important; 15 - overflow: clip; 16 - } 17 - 18 - /* advanced search button */ 19 - .icon-search.button { 20 - border-radius: 0px 0px 10px 10px !important; 21 - } 22 - .c-header-search__results { 23 - border: none !important; 24 - } 25 - 26 - .small-12.island > section { 27 - border-radius: 0px 0px 10px 10px; 28 - overflow: clip; 29 - } 30 - 31 - /* timetable on homepage */ 32 - div[data-timetable-container] > section:has(.timetable) { 33 - border-radius: 10px !important; 34 - overflow: clip; 35 - } 36 - 37 - /* account and action dropdown */ 38 - #account-content, 39 - .f-dropdown { 40 - border-radius: 0px 0px 10px 10px !important; 41 - } 42 - /* see all notifications button */ 43 - #message-list > li > a.button:not(.mark-all-read) { 44 - border-radius: 0px 0px 10px 10px !important; 45 - } 46 - 47 - /* round action buttons */ 48 - .actions-small-3 > nav a[class*="icon-"]:hover, 49 - .actions-small-3 > nav a[class^="icon-"]:hover, 50 - .island .row.actions-small-3 > nav a[class*="icon-"]:hover, 51 - .island .row.actions-small-3 > nav a[class^="icon-"]:hover, 52 - .left-off-canvas-menu .left-menu-dock:hover { 53 - border-radius: 10px; 54 - } 55 - 56 - /* ----- notifications page ----- */ 57 - /* url:/notifications */ 58 - 59 - .notification-list .information-list > li:first-child > div, 60 - .notification-list .information-list > li:first-child { 61 - border-radius: 10px 10px 0px 0px; 62 - } 63 - .notification-list .information-list > li:last-child > div, 64 - .notification-list .information-list > li:last-child { 65 - border-radius: 0px 0px 10px 10px; 66 - } 67 - .notification-list .information-list, 68 - .notification-list .information-list > li:only-child > div { 69 - border-radius: 10px; 70 - } 71 - 72 - /* news component */ 73 - #news-component { 74 - /* round tabs at the bottom */ 75 - & .tabs { 76 - border-radius: 10px 10px 0px 0px; 77 - } 78 - 79 - /* round the news cards */ 80 - & ul.information-list { 81 - background: none !important; 82 - & > li { 83 - background-color: hsl(var(--ctp-surface0)) !important; 84 - /* round the bottom of the last card */ 85 - &:last-child { 86 - border-radius: 0px 0px 10px 10px !important; 87 - } 88 - } 89 - } 90 - } 91 - 92 - /* empty component */ 93 - .Schoolbox_Calendar_Component_Dashboard_Controller.component-container .empty-state { 94 - border-radius: 10px; 95 - } 96 - 97 - /* upcoming work component */ 98 - .Schoolbox_Learning_Component_Dashboard_UpcomingWorkController .information-list, 99 - .Schoolbox_Learning_Component_Dashboard_UpcomingWorkController .information-list .card { 100 - border-radius: 10px !important; 101 - } 102 - 103 - /* calendar */ 104 - .calendar-15403, 105 - .fc { 106 - border-radius: 10px; 107 - } 108 - 109 - /* slideshow */ 110 - .Component_Dashboard_SlideshowController.component-container .swiper { 111 - border-radius: 10px !important; 112 - } 113 - 114 - /* ----- calendar page ----- */ 115 - /* url:/calendar */ 116 - 117 - /* buttons */ 118 - .fc-button-group > button:first-child { 119 - border-radius: 10px 0px 0px 10px; 120 - } 121 - .fc-button-group > button:last-child { 122 - border-radius: 0px 10px 10px 0px; 123 - } 124 - 125 - /* ----- classes page ----- */ 126 - /* url:/learning/classes */ 127 - 128 - .v-card { 129 - border-radius: 10px; 130 - } 131 - .card-class-image { 132 - border-radius: 10px 10px 0px 0px; 133 - } 134 - 135 - /* ----- homepage (class pages) ----- */ 136 - /* url:/homepage/code/:id */ 137 - 138 - div.columns .component-titlebar { 139 - border-radius: 10px; 140 - } 141 - 142 - /* social stream */ 143 - .Component_Homepage_SocialStreamController section, 144 - .Component_Homepage_SocialStreamController .information-list { 145 - border-radius: 10px; 146 - } 147 - 148 - /* news */ 149 - .component-action > section { 150 - background-color: var(--left-bg-primary) !important; 151 - border-radius: 10px !important; 152 - border: none !important; 153 - } 154 - .component-action { 155 - background: var(--left-bg-primary) !important; 156 - border-radius: 0px 0px 10px 10px; 157 - } 158 - .Schoolbox_Comms_News_Component_Homepage_Controller.component-container .island > section, 159 - .Component_Homepage_ImageController.component-container .island > section, 160 - .Component_Homepage_ClassListController.component-container .island > section { 161 - background: none !important; 162 - } 163 - 164 - .Component_Homepage_CountdownController.component-container, 165 - .Schoolbox_Resource_Textbox_Component_Homepage_Controller.component-container, 166 - .Component_Homepage_SocialStreamController.component-container, 167 - .Component_Homepage_SlideshowController.component-container, 168 - .Component_Homepage_AudioController.component-container, 169 - .Component_Homepage_TeachersController.component-container, 170 - .Component_Homepage_ClassListController.component-container, 171 - .Schoolbox_Comms_News_Component_Homepage_Controller.component-container, 172 - .Component_Homepage_LinkListController.component-container, 173 - .Component_Homepage_FileListController.component-container, 174 - .Schoolbox_Learning_Assessment_Component_Homepage_Outline_Controller.component-container, 175 - .Schoolbox_Learning_Assessment_DueWork_Component_Homepage_Controller.component-container, 176 - .Schoolbox_Learning_Assessment_Task_Component_Homepage_Controller.component-container, 177 - .Schoolbox_Learning_Assessment_Quiz_Component_Homepage_Controller.component-container, 178 - .Component_Homepage_ImageController.component-container, 179 - .Schoolbox_Tile_Component_HomepageTileController { 180 - background: hsl(var(--ctp-accent)); 181 - border-radius: 10px; 182 - } 183 - 184 - .Component_Homepage_ImageController.component-container img { 185 - border-radius: 0px 0px 10px 10px !important; 186 - } 187 - 188 - /* round some other components and clip overflow */ 189 - 190 - .Schoolbox_Fixtures_Component_Dashboard_Controller section, 191 - .Component_Dashboard_TextboxController section { 192 - border-radius: 10px !important; 193 - overflow: clip !important; 194 - }
···
+9 -9
src/entrypoints/snippets.content.ts
··· 1 import { defineContentScript } from "#imports"; 2 import { EXCLUDE_MATCHES } from "@/utils/constants"; 3 - import { defineSnippet } from "@/utils/snippet"; 4 - import censor from "./snippets/censor.css?inline"; 5 - import hidePfp from "./snippets/hidePfp/styles.css?inline"; 6 - import hidePwaPrompt from "./snippets/hidePwaPrompt.css?inline"; 7 - import roundedCorners from "./snippets/roundedCorners.css?inline"; 8 9 export default defineContentScript({ 10 matches: ["<all_urls>"], 11 runAt: "document_start", 12 excludeMatches: EXCLUDE_MATCHES, 13 async main() { 14 - defineSnippet("roundedCorners", roundedCorners); 15 - defineSnippet("hidePfp", hidePfp); 16 - defineSnippet("hidePwaPrompt", hidePwaPrompt); 17 - defineSnippet("censor", censor); 18 }, 19 });
··· 1 import { defineContentScript } from "#imports"; 2 import { EXCLUDE_MATCHES } from "@/utils/constants"; 3 + import censor from "./snippets/censor"; 4 + import hidePfp from "./snippets/hidePfp"; 5 + import hidePwaPrompt from "./snippets/hidePwaPrompt"; 6 + import roundedCorners from "./snippets/roundedCorners"; 7 + 8 + export const snippets = [roundedCorners, hidePfp, hidePwaPrompt, censor]; 9 10 export default defineContentScript({ 11 matches: ["<all_urls>"], 12 runAt: "document_start", 13 excludeMatches: EXCLUDE_MATCHES, 14 async main() { 15 + for (const snippet of snippets) { 16 + snippet.init(); 17 + } 18 }, 19 });
+75 -11
src/entrypoints/start.content.ts
··· 1 import { browser, defineContentScript } from "#imports"; 2 - import { injectCatppuccin, injectLogo, injectStylesheet, injectUserSnippets } from "@/utils"; 3 import { EXCLUDE_MATCHES, LOGO_INFO } from "@/utils/constants"; 4 - import type { LogoId } from "@/utils/storage"; 5 - import { globalSettings, schoolboxUrls } from "@/utils/storage"; 6 import cssUrl from "./catppuccin.css?url"; 7 8 export default defineContentScript({ ··· 11 runAt: "document_start", 12 excludeMatches: EXCLUDE_MATCHES, 13 async main() { 14 - const settings = await globalSettings.storage.getValue(); 15 - const urls = (await schoolboxUrls.storage.getValue()).urls; 16 17 - if (settings.global && urls.includes(window.location.origin)) { 18 // inject themes 19 if (settings.themes) { 20 - injectStylesheet(cssUrl); 21 - injectCatppuccin(settings.themeFlavour, settings.themeAccent); 22 } 23 24 // inject logo 25 injectLogo(LOGO_INFO[settings.themeLogo as LogoId], settings.themeLogoAsFavicon); 26 27 - // inject snippets 28 if (settings.snippets) { 29 - injectUserSnippets(settings.userSnippets); 30 } 31 32 // update icon 33 - browser.runtime.sendMessage({ updateIcon: true }); 34 } 35 }, 36 });
··· 1 import { browser, defineContentScript } from "#imports"; 2 + import { 3 + hasChanged, 4 + injectCatppuccin, 5 + injectLogo, 6 + injectStylesheet, 7 + injectUserSnippet, 8 + onSchoolboxPage, 9 + sendMessage, 10 + uninjectCatppuccin, 11 + uninjectStylesheet, 12 + uninjectUserSnippet, 13 + } from "@/utils"; 14 import { EXCLUDE_MATCHES, LOGO_INFO } from "@/utils/constants"; 15 + import type { LogoId, Settings } from "@/utils/storage"; 16 + import { globalSettings } from "@/utils/storage"; 17 + import type { WatchCallback } from "wxt/utils/storage"; 18 import cssUrl from "./catppuccin.css?url"; 19 20 export default defineContentScript({ ··· 23 runAt: "document_start", 24 excludeMatches: EXCLUDE_MATCHES, 25 async main() { 26 + // if not on Schoolbox page 27 + if (!(await onSchoolboxPage())) return; 28 + 29 + const updateThemes: WatchCallback<Settings> = async (newValue, oldValue) => { 30 + // if global or themes was changed 31 + if (hasChanged(newValue, oldValue, ["global", "themes", "themeFlavour", "themeAccent"])) { 32 + if (newValue.global && newValue.themes) { 33 + injectThemes(); 34 + injectCatppuccin(); 35 + } else { 36 + uninjectThemes(); 37 + uninjectCatppuccin(); 38 + } 39 + } 40 + }; 41 42 + const updateUserSnippets: WatchCallback<Settings> = async (newValue, oldValue) => { 43 + // if global or userSnippets were changed 44 + if (hasChanged(newValue, oldValue, ["global", "userSnippets"])) { 45 + // uninject removed snippets 46 + if (oldValue) { 47 + for (const id of Object.keys(oldValue.userSnippets)) { 48 + if (!newValue.userSnippets[id]) { 49 + uninjectUserSnippet(id); 50 + } 51 + } 52 + } 53 + 54 + // inject/uninject current snippets 55 + for (const [id, userSnippet] of Object.entries(newValue.userSnippets)) { 56 + if (newValue.global && newValue.snippets && userSnippet.toggle) { 57 + injectUserSnippet(id); 58 + } else { 59 + uninjectUserSnippet(id); 60 + } 61 + } 62 + } 63 + }; 64 + 65 + // @ts-expect-error unlisted CSS not a PublicPath 66 + const injectThemes = () => injectStylesheet(browser.runtime.getURL(cssUrl), "themes"); 67 + const uninjectThemes = () => uninjectStylesheet("themes"); 68 + 69 + // storage listeners for hot reload 70 + globalSettings.watch((newValue, oldValue) => { 71 + updateThemes(newValue, oldValue); 72 + updateUserSnippets(newValue, oldValue); 73 + }); 74 + 75 + const settings = await globalSettings.get(); 76 + if (settings.global && (await onSchoolboxPage())) { 77 // inject themes 78 if (settings.themes) { 79 + injectThemes(); 80 + injectCatppuccin(); 81 } 82 83 // inject logo 84 injectLogo(LOGO_INFO[settings.themeLogo as LogoId], settings.themeLogoAsFavicon); 85 86 + // inject user snippets 87 if (settings.snippets) { 88 + const userSnippets = (await globalSettings.get()).userSnippets; 89 + for (const [id, snippet] of Object.entries(userSnippets)) { 90 + if (snippet.toggle) { 91 + injectUserSnippet(id); 92 + } 93 + } 94 } 95 96 // update icon 97 + sendMessage({ type: "updateIcon" }); 98 } 99 }, 100 });
+104 -28
src/utils/index.ts
··· 1 import { browser } from "#imports"; 2 import { flavorEntries } from "@catppuccin/palette"; 3 import { logger } from "./logger"; 4 - import type { LogoInfo, UserSnippet } from "./storage"; 5 6 - export function injectStyles(styleText: string) { 7 - logger.info(`[content-utils] Injecting styles`); 8 const style = document.createElement("style"); 9 style.textContent = styleText; 10 - style.classList.add("schooltape"); 11 document.head.append(style); 12 } 13 14 - export function injectCatppuccin(flavour: string, accent: string) { 15 - logger.info(`[content-utils] Injecting Catppuccin: ${flavour} ${accent}`); 16 let styleText = ":root {"; 17 const flavourArray = flavorEntries.find((entry) => entry[0] === flavour); 18 if (flavourArray) { ··· 24 }); 25 } 26 styleText += "}"; 27 - injectStyles(styleText); 28 } 29 30 export function injectLogo(logo: LogoInfo, setAsFavicon: boolean) { ··· 33 // eslint-disable-next-line @typescript-eslint/no-explicit-any 34 url = browser.runtime.getURL(url as any); 35 } 36 - logger.info(`[content-utils] Injecting Logo: ${logo.name}`); 37 if (logo.disable) { 38 return; 39 } ··· 71 } 72 } 73 74 - // eslint-disable-next-line @typescript-eslint/no-explicit-any 75 - export function injectStylesheet(url: any) { 76 - logger.info(`[content-utils] Injecting stylesheet: ${url}`); 77 const link = document.createElement("link"); 78 link.rel = "stylesheet"; 79 - link.href = browser.runtime.getURL(url); 80 - link.classList.add("schooltape"); 81 document.head.appendChild(link); 82 } 83 84 - export async function injectUserSnippets(userSnippets: Record<string, UserSnippet>) { 85 - logger.info("[content-utils] Injecting snippets"); 86 - // user snippets 87 - Object.keys(userSnippets).forEach((snippetId) => { 88 - const userSnippet = userSnippets[snippetId]; 89 - if (userSnippet.toggle) { 90 - fetch(`https://gist.githubusercontent.com/${userSnippet.author}/${snippetId}/raw`) 91 - .then((response) => response.text()) 92 - .then((css) => { 93 - const style = document.createElement("style"); 94 - style.textContent = css; 95 - style.classList.add("schooltape"); 96 - document.head.appendChild(style); 97 - }); 98 } 99 - }); 100 }
··· 1 import { browser } from "#imports"; 2 import { flavorEntries } from "@catppuccin/palette"; 3 import { logger } from "./logger"; 4 + import type { BackgroundMessage, LogoInfo } from "./storage"; 5 + import { globalSettings, schoolboxUrls } from "./storage"; 6 + 7 + export const dataAttr = (id: string) => `[data-schooltape="${id}"]`; 8 + export function setDataAttr(el: HTMLElement, id: string) { 9 + el.dataset.schooltape = id; 10 + } 11 + 12 + export async function onSchoolboxPage(): Promise<boolean> { 13 + return (await schoolboxUrls.get()).urls.includes(window.location.origin); 14 + } 15 16 + export const sendMessage = (msg: BackgroundMessage) => browser.runtime.sendMessage(msg); 17 + 18 + export function injectInlineStyles(styleText: string, id: string) { 19 + logger.info(`injecting styles with id ${id}`); 20 const style = document.createElement("style"); 21 style.textContent = styleText; 22 + setDataAttr(style, `inline-${id}`); 23 document.head.append(style); 24 + // logger.info(`injected styles with id ${id}`); 25 } 26 27 + export function uninjectInlineStyles(id: string) { 28 + logger.info(`uninjecting styles with id ${id}`); 29 + const style = document.querySelector(dataAttr(`inline-${id}`)); 30 + if (style) document.head.removeChild(style); 31 + } 32 + 33 + export async function injectCatppuccin() { 34 + const settings = await globalSettings.get(); 35 + const flavour = settings.themeFlavour; 36 + const accent = settings.themeAccent; 37 + 38 + logger.info(`injecting catppuccin: ${flavour} ${accent}`); 39 let styleText = ":root {"; 40 const flavourArray = flavorEntries.find((entry) => entry[0] === flavour); 41 if (flavourArray) { ··· 47 }); 48 } 49 styleText += "}"; 50 + injectInlineStyles(styleText, "catppuccin"); 51 + } 52 + 53 + export function uninjectCatppuccin() { 54 + uninjectInlineStyles("catppuccin"); 55 } 56 57 export function injectLogo(logo: LogoInfo, setAsFavicon: boolean) { ··· 60 // eslint-disable-next-line @typescript-eslint/no-explicit-any 61 url = browser.runtime.getURL(url as any); 62 } 63 + logger.info(`injecting logo: ${logo.name}`); 64 if (logo.disable) { 65 return; 66 } ··· 98 } 99 } 100 101 + export function injectStylesheet(url: string, id: string) { 102 + // check if stylesheet has already been injected 103 + const existingLink = document.querySelector(dataAttr(`stylesheet-${id}`)); 104 + if (existingLink) return; 105 + 106 + // inject stylesheet 107 + logger.info(`injecting stylesheet with id ${id}: ${url}`); 108 const link = document.createElement("link"); 109 link.rel = "stylesheet"; 110 + link.href = url; 111 + setDataAttr(link, `stylesheet-${id}`); 112 document.head.appendChild(link); 113 } 114 115 + export function uninjectStylesheet(id: string) { 116 + logger.info(`uninjecting stylesheet with id ${id}`); 117 + 118 + const link = document.querySelector(dataAttr(`stylesheet-${id}`)); 119 + if (link) document.head.removeChild(link); 120 + } 121 + 122 + export async function injectUserSnippet(id: string) { 123 + logger.info(`injecting user snippet with id ${id}`); 124 + 125 + const userSnippets = (await globalSettings.get()).userSnippets; 126 + const snippet = userSnippets[id]; 127 + 128 + if (!snippet) { 129 + logger.error(`user snippet with id ${id} not found, aborting`); 130 + return; 131 + } 132 + 133 + if (!snippet.toggle) { 134 + logger.error(`trying to inject user snippet with id ${id} which is disabled, aborting`); 135 + return; 136 + } 137 + 138 + // check not already injected 139 + if (document.querySelector(dataAttr(`userSnippet-${id}`))) { 140 + logger.info(`user snippet with id ${id} already injected, aborting`); 141 + return; 142 + } 143 + 144 + // inject user snippet 145 + const response = await fetch(`https://gist.githubusercontent.com/${snippet.author}/${id}/raw`); 146 + const css = await response.text(); 147 + const style = document.createElement("style"); 148 + 149 + style.textContent = css; 150 + setDataAttr(style, `userSnippet-${id}`); 151 + document.head.appendChild(style); 152 + 153 + logger.info(`injected user snippet with id ${id}`); 154 + } 155 + 156 + export function uninjectUserSnippet(id: string) { 157 + logger.info(`uninjecting user snippet with id ${id}`); 158 + 159 + const style = document.querySelector(dataAttr(`userSnippet-${id}`)); 160 + if (!style) return; 161 + 162 + document.head.removeChild(style); 163 + logger.info(`uninjected user snippet with id ${id}`); 164 + } 165 + 166 + export function hasChanged<T>(newValue: T, oldValue: T, keys: (keyof T)[]) { 167 + const changed: (keyof T)[] = []; 168 + 169 + for (const key in newValue) { 170 + if (Object.prototype.hasOwnProperty.call(newValue, key) && oldValue[key] !== newValue[key]) { 171 + changed.push(key); 172 } 173 + } 174 + 175 + return keys.some((item) => changed.includes(item)); 176 }
+12 -7
src/utils/logger.ts
··· 1 - import pino from "pino"; 2 3 - export const logger = pino({ 4 - browser: { 5 - disabled: import.meta.env.MODE === "production", 6 - asObject: true, 7 - }, 8 - });
··· 1 + function print(method: (...args: unknown[]) => void, ...args: unknown[]) { 2 + if (import.meta.env.MODE === "production") return; 3 + 4 + const css = "background: #7fd4fa; color: #051d29; border-radius:10px"; 5 6 + method("%c schooltape ", css, ...args); 7 + } 8 + 9 + export const logger = { 10 + info: (...args: unknown[]) => print(console.log, ...args), 11 + warn: (...args: unknown[]) => print(console.warn, ...args), 12 + error: (...args: unknown[]) => print(console.error, ...args), 13 + };
+2 -5
src/utils/periodUtils.ts
··· 1 // these utility functions are intended to be used on the dashboard, as that is where the timetable is displayed 2 3 - import { logger } from "./logger"; 4 - 5 interface PeriodHeader { 6 name: string; 7 time: { ··· 138 return date; 139 }); 140 return { start, end }; 141 - } catch (error) { 142 - logger.error("Error extracting times:", error); 143 - throw new Error("Failed to extract times"); 144 } 145 }
··· 1 // these utility functions are intended to be used on the dashboard, as that is where the timetable is displayed 2 3 interface PeriodHeader { 4 name: string; 5 time: { ··· 136 return date; 137 }); 138 return { start, end }; 139 + } catch (e) { 140 + throw new Error("Failed to extract times", e as Error); 141 } 142 }
+101 -60
src/utils/plugin.ts
··· 1 import { logger } from "./logger"; 2 - import type { PluginId, PluginSetting, Slider } from "./storage"; 3 - import { globalSettings, plugins, schoolboxUrls } from "./storage"; 4 5 - export async function definePlugin( 6 - pluginId: PluginId, 7 - callback: (settings?: { toggle: Record<string, boolean>; slider: Record<string, Slider> }) => Promise<void> | void, 8 - elementsToWaitFor: string[] = [], 9 - ) { 10 - const plugin = await plugins[pluginId].toggle.storage.getValue(); 11 12 - logger.info(`${plugins[pluginId].name}: ${plugin.toggle ? "enabled" : "disabled"}`); 13 - 14 - const settings = await globalSettings.storage.getValue(); 15 - const urls = (await schoolboxUrls.storage.getValue()).urls; 16 - 17 - if (plugin && typeof window !== "undefined" && urls.includes(window.location.origin)) { 18 - if (settings.global && settings.plugins && plugin.toggle) { 19 - const injectPlugin = () => { 20 - callback(getSettingsValues(plugins[pluginId]?.settings)); 21 - }; 22 23 - const loadPlugin = () => { 24 - // wait for elements to be loaded 25 - if (elementsToWaitFor.length > 0) { 26 - const observer = new MutationObserver((_mutations, observer) => { 27 - const allElementsPresent = elementsToWaitFor.every((selector) => document.querySelector(selector) !== null); 28 - if (allElementsPresent) { 29 - observer.disconnect(); 30 - logger.info(`all elements present, injecting plugin: ${plugins[pluginId].name}`); 31 - injectPlugin(); 32 - } 33 - }); 34 35 - observer.observe(document.body, { childList: true, subtree: true }); 36 37 - // check if elements are already present 38 - const allElementsPresent = elementsToWaitFor.every((selector) => document.querySelector(selector) !== null); 39 - if (allElementsPresent) { 40 observer.disconnect(); 41 - logger.info(`all elements already present, injecting plugin: ${plugins[pluginId].name}`); 42 - injectPlugin(); 43 } 44 - } else { 45 - // no elements to wait for 46 - logger.info(`injecting plugin: ${plugins[pluginId].name}`); 47 - injectPlugin(); 48 - } 49 - }; 50 51 - if (document.body) { 52 - loadPlugin(); 53 } else { 54 - document.addEventListener("DOMContentLoaded", loadPlugin); 55 } 56 } 57 } 58 - } 59 60 - function getSettingsValues(settings?: Record<string, PluginSetting>) { 61 - if (!settings) return undefined; 62 63 - const result: { 64 - toggle: Record<string, boolean>; 65 - slider: Record<string, Slider>; 66 - } = { toggle: {}, slider: {} }; 67 - for (const [key, setting] of Object.entries(settings)) { 68 - if (setting.type === "toggle") { 69 - const value = setting.state.get(); 70 - result.toggle[key] = value.toggle; 71 - } else if (setting.type === "slider") { 72 - const value = setting.state.get(); 73 - result.slider[key] = value; 74 - } 75 } 76 - return result; 77 }
··· 1 + import { storage } from "#imports"; 2 + import { hasChanged, onSchoolboxPage } from "."; 3 import { logger } from "./logger"; 4 + import type { Toggle } from "./storage"; 5 + import { globalSettings } from "./storage"; 6 + import { StorageState } from "./storage/state.svelte"; 7 8 + export class Plugin<T extends Record<string, unknown> | undefined = undefined> { 9 + private injected = false; 10 + public toggle: StorageState<Toggle>; 11 + public settings!: T; 12 + public menu: string | undefined; 13 14 + constructor( 15 + public meta: { 16 + id: string; 17 + name: string; 18 + description: string; 19 + }, 20 + defaultToggle: boolean, 21 + settings: Record<string, object> | null, 22 + private injectCallback: (settings: T) => Promise<void> | void, 23 + private uninjectCallback: (settings: T) => Promise<void> | void, 24 + private elementsToWaitFor: string[] = [], 25 + ) { 26 + // init plugin storage 27 + this.toggle = new StorageState( 28 + storage.defineItem(`local:plugin-${meta.id}`, { 29 + fallback: { toggle: defaultToggle }, 30 + }), 31 + ); 32 + if (settings) { 33 + this.settings = Object.fromEntries( 34 + Object.entries(settings).map(([key, value]) => [ 35 + key, 36 + new StorageState( 37 + storage.defineItem(`local:plugin-${meta.id}-${key}`, { 38 + fallback: value, 39 + }), 40 + ), 41 + ]), 42 + ) as T; 43 + } 44 + } 45 46 + async init() { 47 + // if not on Schoolbox page 48 + if (!(await onSchoolboxPage())) return; 49 50 + logger.info(`init plugin: ${this.meta.name}`); 51 52 + if (await this.isEnabled()) { 53 + // wait for elements to be loaded 54 + if (this.elementsToWaitFor.length > 0) { 55 + // create an observer to wait for all elements to be loaded 56 + const observer = new MutationObserver((_mutations, observer) => { 57 + if (this.allElementsPresent()) { 58 observer.disconnect(); 59 + this.inject(); 60 } 61 + }); 62 + observer.observe(document.body, { childList: true, subtree: true }); 63 64 + // check if elements are already present 65 + if (this.allElementsPresent()) { 66 + observer.disconnect(); 67 + this.inject(); 68 + } 69 } else { 70 + // no elements to wait for 71 + this.inject(); 72 + } 73 + } 74 + 75 + // init watchers 76 + globalSettings.watch((newValue, oldValue) => { 77 + if (hasChanged(newValue, oldValue, ["global", "plugins"])) this.reload(); 78 + }); 79 + this.toggle.watch(this.reload.bind(this)); 80 + if (this.settings) { 81 + for (const setting of Object.values(this.settings)) { 82 + if (!(setting instanceof StorageState)) continue; 83 + setting.watch(this.reload.bind(this)); 84 } 85 } 86 } 87 88 + private inject() { 89 + if (this.injected) return; 90 + if (!this.allElementsPresent()) return; 91 + logger.info(`injecting plugin: ${this.meta.name}`); 92 + this.injectCallback(this.settings); 93 + this.injected = true; 94 + } 95 96 + private uninject() { 97 + if (!this.injected) return; 98 + logger.info(`uninjecting plugin: ${this.meta.name}`); 99 + this.uninjectCallback(this.settings); 100 + this.injected = false; 101 } 102 + 103 + private async reload() { 104 + if (this.injected) this.uninject(); 105 + if (await this.isEnabled()) this.inject(); 106 + } 107 + 108 + private async isEnabled(): Promise<boolean> { 109 + const settings = await globalSettings.get(); 110 + const toggle = await this.toggle.get(); 111 + 112 + return settings.global && settings.plugins && toggle.toggle; 113 + } 114 + 115 + private allElementsPresent() { 116 + return this.elementsToWaitFor.every((selector) => document.querySelector(selector) !== null); 117 + } 118 }
+62 -14
src/utils/snippet.ts
··· 1 - import { injectStyles } from "."; 2 import { logger } from "./logger"; 3 - import type { SnippetId } from "./storage"; 4 - import { globalSettings, schoolboxUrls, snippets } from "./storage"; 5 6 - export async function defineSnippet(snippetId: SnippetId, styleText: string) { 7 - const snippet = await snippets[snippetId].toggle.storage.getValue(); 8 9 - logger.info(`${snippets[snippetId].name}: ${snippet.toggle ? "enabled" : "disabled"}`); 10 11 - const settings = await globalSettings.storage.getValue(); 12 - const urls = (await schoolboxUrls.storage.getValue()).urls; 13 14 - if (snippet && typeof window !== "undefined" && urls.includes(window.location.origin)) { 15 - if (settings.global && settings.snippets && snippet.toggle) { 16 - // inject 17 - logger.info(`Injecting snippet: ${snippets[snippetId].name}`); 18 - injectStyles(styleText); 19 - } 20 } 21 }
··· 1 + import { storage } from "#imports"; 2 + import { hasChanged, injectInlineStyles, onSchoolboxPage, uninjectInlineStyles } from "."; 3 import { logger } from "./logger"; 4 + import type { Toggle } from "./storage"; 5 + import { globalSettings } from "./storage"; 6 + import { StorageState } from "./storage/state.svelte"; 7 8 + export class Snippet { 9 + private injected = false; 10 + public toggle: StorageState<Toggle>; 11 12 + constructor( 13 + public meta: { 14 + id: string; 15 + name: string; 16 + description: string; 17 + }, 18 + defaultToggle: boolean, 19 + private styleText: string, 20 + ) { 21 + // init snippet storage 22 + this.toggle = new StorageState( 23 + storage.defineItem(`local:snippet-${meta.id}`, { 24 + fallback: { toggle: defaultToggle }, 25 + }), 26 + ); 27 + } 28 29 + async init() { 30 + // if not on Schoolbox page 31 + if (!(await onSchoolboxPage())) return; 32 33 + logger.info(`init snippet: ${this.meta.name}`); 34 + 35 + if (await this.isEnabled()) this.inject(); 36 + 37 + // init watchers 38 + globalSettings.watch((newValue, oldValue) => { 39 + if (hasChanged(newValue, oldValue, ["global", "snippets"])) this.reload(); 40 + }); 41 + this.toggle.watch(this.reload.bind(this)); 42 + } 43 + 44 + private inject() { 45 + if (this.injected) return; 46 + logger.info(`injecting snippet: ${this.meta.name}`); 47 + injectInlineStyles(this.styleText, `snippet-${this.meta.id}`); 48 + this.injected = true; 49 + } 50 + 51 + private uninject() { 52 + if (!this.injected) return; 53 + logger.info(`uninjecting snippet: ${this.meta.name}`); 54 + uninjectInlineStyles(`snippet-${this.meta.id}`); 55 + this.injected = false; 56 + } 57 + 58 + private async reload() { 59 + if (this.injected) this.uninject(); 60 + if (await this.isEnabled()) this.inject(); 61 + } 62 + 63 + private async isEnabled(): Promise<boolean> { 64 + const settings = await globalSettings.get(); 65 + const toggle = await this.toggle.get(); 66 + 67 + return settings.global && settings.snippets && toggle.toggle; 68 } 69 }
-7
src/utils/storage/global.ts
··· 18 userSnippets: {}, 19 }, 20 }), 21 - true, 22 - ); 23 - 24 - export const needsRefresh = new StorageState( 25 - storage.defineItem<boolean>("local:needsRefresh", { 26 - fallback: false, 27 - }), 28 ); 29 30 // whether schooltape was recently updated
··· 18 userSnippets: {}, 19 }, 20 }), 21 ); 22 23 // whether schooltape was recently updated
-2
src/utils/storage/index.ts
··· 1 export * from "./global"; 2 - export * from "./plugins"; 3 - export * from "./snippets"; 4 export * from "./types";
··· 1 export * from "./global"; 2 export * from "./types";
-141
src/utils/storage/plugins.ts
··· 1 - import { storage } from "#imports"; 2 - import { StorageState } from "./state.svelte"; 3 - import type * as Types from "./types"; 4 - 5 - export const pluginConfig: Record<Types.PluginId, Types.PluginConfig> = { 6 - subheader: { 7 - name: "Subheader Revamp", 8 - description: "Adds a clock and current period info to the subheader.", 9 - default: true, 10 - settings: { 11 - openInNewTab: { 12 - type: "toggle", 13 - name: "Open links in new tab", 14 - description: "Whether to open the class link in a new tab.", 15 - default: { toggle: true }, 16 - }, 17 - }, 18 - }, 19 - scrollSegments: { 20 - name: "Scroll Segments", 21 - description: "Segments the Schoolbox page into scrollable sections.", 22 - default: true, 23 - }, 24 - scrollPeriod: { 25 - name: "Scroll Period", 26 - description: "Scrolls to the current period on the timetable.", 27 - default: true, 28 - settings: { 29 - resetCooldownOnMouseMove: { 30 - type: "toggle", 31 - name: "Reset on mouse move", 32 - description: "Whether to reset the scrolling cooldown when you move your mouse.", 33 - default: { toggle: true }, 34 - }, 35 - cooldownDuration: { 36 - type: "slider", 37 - name: "Cooldown duration (s)", 38 - description: "How long to wait before scrolling.", 39 - default: { min: 1, max: 60, value: 10 }, 40 - }, 41 - }, 42 - }, 43 - progressBar: { 44 - name: "Progress Bar", 45 - description: "Displays a progress bar below the timetable to show the time of the day.", 46 - default: true, 47 - }, 48 - modernIcons: { 49 - name: "Modern Icons", 50 - description: "Modernise the icons across Schoolbox.", 51 - default: true, 52 - settings: { 53 - filled: { 54 - type: "toggle", 55 - name: "Filled Icons", 56 - description: "Whether the icons should be filled or outlined.", 57 - default: { toggle: true }, 58 - }, 59 - }, 60 - }, 61 - tabTitle: { 62 - name: "Better Tab Titles", 63 - description: "Improves the tab titles for easier navigation.", 64 - default: true, 65 - settings: { 66 - showSubjectPrefix: { 67 - type: "toggle", 68 - name: "Show subject prefix", 69 - description: `e.g. "ENG - VCE English 1 & 2" becomes "VCE English 1 & 2"`, 70 - default: { toggle: true }, 71 - }, 72 - }, 73 - }, 74 - homepageSwitcher: { 75 - name: "Homepage Switcher", 76 - description: "The logo will switch to existing Schoolbox homepage when available.", 77 - default: true, 78 - settings: { 79 - closeCurrentTab: { 80 - type: "toggle", 81 - name: "Close current tab", 82 - description: "When switching to another tab, close the current one.", 83 - default: { toggle: false }, 84 - }, 85 - }, 86 - }, 87 - } as const; 88 - 89 - export const plugins = buildPluginsFromConfig(pluginConfig); 90 - 91 - function buildPluginsFromConfig( 92 - config: Record<Types.PluginId, Types.PluginConfig>, 93 - ): Record<Types.PluginId, Types.PluginData> { 94 - const plugins: Partial<Record<Types.PluginId, Types.PluginData>> = {}; 95 - 96 - for (const [pluginId, pluginConfig] of Object.entries(config)) { 97 - const plugin: Types.PluginData = { 98 - name: pluginConfig.name, 99 - description: pluginConfig.description, 100 - toggle: new StorageState( 101 - storage.defineItem<Types.Toggle>(`local:plugin-${pluginId}`, { 102 - fallback: { toggle: pluginConfig.default }, 103 - }), 104 - true, 105 - ), 106 - }; 107 - 108 - // define settings 109 - if (pluginConfig.settings) { 110 - plugin.settings = {}; 111 - 112 - // iterate over each setting and create state 113 - for (const [settingId, settingConfig] of Object.entries(pluginConfig.settings)) { 114 - let state; 115 - if (settingConfig.type === "toggle") { 116 - state = new StorageState( 117 - storage.defineItem<Types.Toggle>(`local:plugin-${pluginId}-${settingId}`, { 118 - fallback: settingConfig.default, 119 - }), 120 - ); 121 - } else { 122 - state = new StorageState( 123 - storage.defineItem<Types.Slider>(`local:plugin-${pluginId}-${settingId}`, { 124 - fallback: settingConfig.default, 125 - }), 126 - ); 127 - } 128 - plugin.settings[settingId] = { 129 - state, 130 - type: settingConfig.type, 131 - name: settingConfig.name, 132 - description: settingConfig.description, 133 - } as Types.PluginSetting; 134 - } 135 - } 136 - 137 - plugins[pluginId as Types.PluginId] = plugin; 138 - } 139 - 140 - return plugins as Record<Types.PluginId, Types.PluginData>; 141 - }
···
-54
src/utils/storage/snippets.ts
··· 1 - import { storage } from "#imports"; 2 - import { StorageState } from "./state.svelte"; 3 - import type * as Types from "./types"; 4 - 5 - export const snippetConfig: Record<Types.SnippetId, Types.SnippetConfig> = { 6 - roundedCorners: { 7 - name: "Rounded Corners", 8 - description: "Adds rounded corners to all elements across Schoolbox.", 9 - default: true, 10 - }, 11 - 12 - hidePfp: { 13 - name: "Hide PFP", 14 - description: "Hide your profile picture across Schoolbox.", 15 - default: true, 16 - }, 17 - 18 - hidePwaPrompt: { 19 - name: "Hide PWA Prompt", 20 - description: "Hides the prompt in the notifications menu to install Schoolbox as a PWA and enable notifications.", 21 - default: true, 22 - }, 23 - 24 - censor: { 25 - name: "Censor", 26 - description: "Censors all text and images. This is intended for development purposes.", 27 - default: false, 28 - }, 29 - }; 30 - 31 - export const snippets = buildSnippetsFromConfig(snippetConfig); 32 - 33 - function buildSnippetsFromConfig( 34 - config: Record<Types.SnippetId, Types.SnippetConfig>, 35 - ): Record<Types.SnippetId, Types.SnippetData> { 36 - const snippets: Partial<Record<Types.SnippetId, Types.SnippetData>> = {}; 37 - 38 - for (const [snippetId, snippetConfig] of Object.entries(config)) { 39 - const snippet: Types.SnippetData = { 40 - name: snippetConfig.name, 41 - description: snippetConfig.description, 42 - toggle: new StorageState( 43 - storage.defineItem<Types.Toggle>(`local:snippet-${snippetId}`, { 44 - fallback: { toggle: snippetConfig.default }, 45 - }), 46 - true, 47 - ), 48 - }; 49 - 50 - snippets[snippetId as Types.SnippetId] = snippet; 51 - } 52 - 53 - return snippets as Record<Types.SnippetId, Types.SnippetData>; 54 - }
···
+18 -20
src/utils/storage/state.svelte.ts
··· 1 import type { WxtStorageItem } from "#imports"; 2 - import { needsRefresh } from "./global"; 3 4 export class StorageState<T> { 5 public state; 6 7 - constructor( 8 - public storage: WxtStorageItem<T, {}>, 9 - refresh: boolean = false, 10 - ) { 11 this.storage = storage; 12 this.state = $state(this.storage.fallback); 13 14 - this.storage.getValue().then(this.update); 15 - this.storage.watch((newState) => this.update(newState, refresh)); 16 } 17 18 - private update = (newState: T | null, refresh?: boolean) => { 19 this.state = newState ?? this.storage.fallback; 20 - if (refresh && this.storage.key !== "local:needsRefresh") { 21 - needsRefresh.storage.setValue(true); 22 - } 23 }; 24 25 - async set(updates: Partial<T>) { 26 - const newState = { 27 - ...(await this.storage.getValue()), 28 - ...updates, 29 - }; 30 31 - await this.storage.setValue(newState); 32 } 33 34 - get() { 35 - this.storage.getValue().then(this.update); 36 - return $state.snapshot(this.state) as T; 37 } 38 }
··· 1 import type { WxtStorageItem } from "#imports"; 2 + import type { WatchCallback } from "wxt/utils/storage"; 3 4 export class StorageState<T> { 5 public state; 6 + private storage; 7 8 + constructor(storage: WxtStorageItem<T, {}>) { 9 this.storage = storage; 10 this.state = $state(this.storage.fallback); 11 12 + this.storage.getValue().then(this.updateState); 13 + this.storage.watch((newState) => this.updateState(newState)); 14 } 15 16 + private updateState = (newState: T | null) => { 17 this.state = newState ?? this.storage.fallback; 18 }; 19 20 + watch = (cb: WatchCallback<T>) => this.storage.watch(cb); 21 22 + get() { 23 + return this.storage.getValue(); 24 + } 25 + 26 + set(newValue: T) { 27 + return this.storage.setValue(newValue); 28 } 29 30 + async update(updates: Partial<T>) { 31 + await this.set({ 32 + ...(await this.get()), 33 + ...updates, 34 + }); 35 } 36 }
+6 -59
src/utils/storage/types.ts
··· 1 - import type { StorageState } from "./state.svelte"; 2 3 - // Global 4 export interface Settings { 5 global: boolean; 6 plugins: boolean; ··· 45 toggle: boolean; 46 } 47 48 - // Common for plugins and snippets 49 - export interface ItemInfo { 50 - name: string; 51 - description: string; 52 - } 53 - 54 - // Plugins 55 - export type PluginId = 56 - | "subheader" 57 - | "scrollSegments" 58 - | "scrollPeriod" 59 - | "progressBar" 60 - | "modernIcons" 61 - | "tabTitle" 62 - | "homepageSwitcher"; 63 - 64 export type Toggle = { toggle: boolean }; 65 66 export type Slider = { ··· 68 min: number; 69 max: number; 70 }; 71 - 72 - export type PluginData = { 73 - toggle: StorageState<Toggle>; 74 - settings?: Record<string, PluginSetting>; 75 - } & ItemInfo; 76 - 77 - export type PluginConfig = { 78 - default: boolean; 79 - settings?: Record<string, PluginSettingConfig>; 80 - } & ItemInfo; 81 - 82 - export type PluginSetting = 83 - | ({ 84 - type: "toggle"; 85 - state: StorageState<Toggle>; 86 - } & ItemInfo) 87 - | ({ 88 - type: "slider"; 89 - state: StorageState<Slider>; 90 - } & ItemInfo); 91 - 92 - export type PluginSettingConfig = 93 - | ({ 94 - type: "toggle"; 95 - default: Toggle; 96 - } & ItemInfo) 97 - | ({ 98 - type: "slider"; 99 - default: Slider; 100 - } & ItemInfo); 101 - 102 - // Snippets 103 - export type SnippetId = "roundedCorners" | "hidePfp" | "hidePwaPrompt" | "censor"; 104 - 105 - export type SnippetData = { 106 - toggle: StorageState<Toggle>; 107 - } & ItemInfo; 108 - 109 - export type SnippetConfig = { 110 - default: boolean; 111 - } & ItemInfo;
··· 1 + export type BackgroundMessage = 2 + | { type: "resetSettings" } 3 + | { type: "updateIcon" } 4 + | { type: "closeTab" } 5 + | { type: "updateTabUrl"; url: string }; 6 7 + // global 8 export interface Settings { 9 global: boolean; 10 plugins: boolean; ··· 49 toggle: boolean; 50 } 51 52 export type Toggle = { toggle: boolean }; 53 54 export type Slider = { ··· 56 min: number; 57 max: number; 58 };