schoolbox web extension :)
1import { browser, defineContentScript } from "#imports";
2import {
3 hasChanged,
4 injectCatppuccin,
5 injectLogo,
6 injectStylesheet,
7 injectUserSnippet,
8 onSchoolboxPage,
9 sendMessage,
10 uninjectCatppuccin,
11 uninjectStylesheet,
12 uninjectUserSnippet,
13} from "@/utils";
14import { EXCLUDE_MATCHES, LOGO_INFO } from "@/utils/constants";
15import type { LogoId, Settings } from "@/utils/storage";
16import { globalSettings } from "@/utils/storage";
17import type { WatchCallback } from "wxt/utils/storage";
18import cssUrl from "./catppuccin.css?url";
19
20export default defineContentScript({
21 matches: ["<all_urls>"],
22 cssInjectionMode: "manual",
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});