+7
.env.example
+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
+2
-2
.github/workflows/build.yml
+29
.github/workflows/check.yml
+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
+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
+1
-1
.github/workflows/release-please.yml
+2
-3
.gitignore
+2
-3
.gitignore
+1
-1
.prettierrc.json
+1
-1
.prettierrc.json
+68
CHANGELOG.md
+68
CHANGELOG.md
···
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
+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
+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
+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
+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
+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
+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
-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
+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
+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
+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
+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
+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
-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
+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
+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
+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
+7
src/entrypoints/plugins/subheader/styles.css
-113
src/entrypoints/plugins/subheader.ts
-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
+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
+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
-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
+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
+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
+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
+1
-1
src/entrypoints/popup/components/Banner.svelte
+2
-3
src/entrypoints/popup/components/Modal.svelte
+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
+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
+1
-1
src/entrypoints/popup/components/Title.svelte
+1
-1
src/entrypoints/popup/components/inputs/Button.svelte
+1
-1
src/entrypoints/popup/components/inputs/Button.svelte
+2
-2
src/entrypoints/popup/components/inputs/Slider.svelte
+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
+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
+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
+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={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
+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
+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
+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
+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
+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
-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
+12
src/entrypoints/snippets/hidePfp/index.ts
+12
src/entrypoints/snippets/hidePwaPrompt/index.ts
+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
+3
src/entrypoints/snippets/hidePwaPrompt/styles.css
-3
src/entrypoints/snippets/hidePwaPrompt.css
-3
src/entrypoints/snippets/hidePwaPrompt.css
+12
src/entrypoints/snippets/roundedCorners/index.ts
+12
src/entrypoints/snippets/roundedCorners/index.ts
···
+194
src/entrypoints/snippets/roundedCorners/styles.css
+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
-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
+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
+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
+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
+12
-7
src/utils/logger.ts
···
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
+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
+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
+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
-7
src/utils/storage/global.ts
-2
src/utils/storage/index.ts
-2
src/utils/storage/index.ts
-141
src/utils/storage/plugins.ts
-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
-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
+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
+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
};