schoolbox web extension :)
1import { browser, defineContentScript } from "#imports";
2import {
3 hasChanged,
4 injectCatppuccin,
5 injectLogo,
6 injectStylesheet,
7 injectUserSnippet,
8 uninjectCatppuccin,
9 uninjectStylesheet,
10 uninjectUserSnippet,
11} from "@/utils";
12import { EXCLUDE_MATCHES, LOGO_INFO } from "@/utils/constants";
13import type { LogoId, Settings } from "@/utils/storage";
14import { globalSettings, schoolboxUrls } from "@/utils/storage";
15import type { WatchCallback } from "wxt/utils/storage";
16import cssUrl from "./catppuccin.css?url";
17
18export default defineContentScript({
19 matches: ["<all_urls>"],
20 cssInjectionMode: "manual",
21 runAt: "document_start",
22 excludeMatches: EXCLUDE_MATCHES,
23 async main() {
24 const settings = await globalSettings.get();
25 const urls = (await schoolboxUrls.get()).urls;
26
27 const updateThemes: WatchCallback<Settings> = (newValue, oldValue) => {
28 // if global or themes was changed
29 if (hasChanged(newValue, oldValue, ["global", "themes", "themeFlavour", "themeAccent"])) {
30 if (newValue.global && newValue.themes) {
31 injectThemes();
32 injectCatppuccin();
33 } else {
34 uninjectThemes();
35 uninjectCatppuccin();
36 }
37 }
38 };
39
40 const updateUserSnippets: WatchCallback<Settings> = (newValue, oldValue) => {
41 // if global or userSnippets were changed
42 if (hasChanged(newValue, oldValue, ["global", "userSnippets"])) {
43 // uninject removed snippets
44 if (oldValue) {
45 for (const id of Object.keys(oldValue.userSnippets)) {
46 if (!newValue.userSnippets[id]) {
47 uninjectUserSnippet(id);
48 }
49 }
50 }
51
52 // inject/uninject current snippets
53 for (const [id, userSnippet] of Object.entries(newValue.userSnippets)) {
54 if (newValue.global && newValue.snippets && userSnippet.toggle) {
55 injectUserSnippet(id);
56 } else {
57 uninjectUserSnippet(id);
58 }
59 }
60 }
61 };
62
63 // @ts-expect-error unlisted CSS not a PublicPath
64 const injectThemes = () => injectStylesheet(browser.runtime.getURL(cssUrl), "themes");
65 const uninjectThemes = () => uninjectStylesheet("themes");
66
67 // storage listeners for hot reload
68 globalSettings.watch((newValue, oldValue) => {
69 updateThemes(newValue, oldValue);
70 updateUserSnippets(newValue, oldValue);
71 });
72
73 if (settings.global && urls.includes(window.location.origin)) {
74 // inject themes
75 if (settings.themes) {
76 injectThemes();
77 injectCatppuccin();
78 }
79
80 // inject logo
81 injectLogo(LOGO_INFO[settings.themeLogo as LogoId], settings.themeLogoAsFavicon);
82
83 // inject user snippets
84 if (settings.snippets) {
85 const userSnippets = (await globalSettings.get()).userSnippets;
86 for (const [id, snippet] of Object.entries(userSnippets)) {
87 if (snippet.toggle) {
88 injectUserSnippet(id);
89 }
90 }
91 }
92
93 // update icon
94 browser.runtime.sendMessage({ updateIcon: true });
95 }
96 },
97});