From 824cedca2fa3a4bd137e833ca332a3044de8f15f Mon Sep 17 00:00:00 2001 From: Aden Linday Date: Sun, 19 Jul 2026 18:08:52 +0930 Subject: [PATCH] feat: optimisations --- .parcelrc | 5 - package.json | 56 +- src/SEQTA.ts | 9 +- src/background/calendarBackground.ts | 6 +- src/background/cloudSettingsAutoSync.ts | 1 + .../components/store/Backgrounds.svelte | 25 +- src/interface/hooks/BackgroundDataLoader.ts | 143 +---- src/interface/main.ts | 13 +- src/interface/pages/settings.svelte | 140 ----- src/interface/pages/themeCreator.svelte | 3 +- src/interface/renderInShadow.ts | 26 - .../built-in/assessmentsAverage/index.ts | 32 +- .../built-in/assessmentsAverage/lazy.ts | 20 + .../built-in/assessmentsAverage/rubricCopy.ts | 8 +- .../built-in/assessmentsAverage/utils.ts | 3 +- .../built-in/assessmentsOverview/index.ts | 2 +- .../built-in/enhancedNavigation/index.ts | 2 - .../built-in/enhancedNavigation/lazy.ts | 23 + .../src/components/SearchBar.svelte | 5 +- .../built-in/globalSearch/src/core/index.ts | 16 +- .../globalSearch/src/core/mountSearchBar.ts | 13 +- .../src/search/vector/vectorSearch.ts | 7 + .../globalSearch/src/utils/calculator.ts | 196 ++----- src/plugins/built-in/messageFolders/index.ts | 140 ++--- src/plugins/built-in/messageFolders/lazy.ts | 30 ++ src/plugins/built-in/test/index.ts | 57 -- src/plugins/built-in/themes/theme-manager.ts | 9 - src/plugins/built-in/themes/theme-runtime.ts | 46 +- .../timetable/CalendarSyncControl.svelte | 80 +-- .../built-in/timetable/calendarSyncUi.ts | 4 +- src/plugins/built-in/timetableEdit/index.ts | 18 +- src/plugins/built-in/timetableEdit/lazy.ts | 14 + src/plugins/core/createAPI.ts | 1 + src/plugins/index.ts | 20 +- src/plugins/monofile.ts | 136 ++--- src/plugins/runtime.ts | 6 - src/seqta/main.ts | 4 - src/seqta/ui/Loading.ts | 8 +- src/seqta/ui/SettingsResizer.ts | 2 +- src/seqta/ui/loading-spinner.html | 4 + src/seqta/utils/Loaders/LoadHomePage.ts | 39 +- src/seqta/utils/Openers/OpenAboutPage.ts | 32 +- .../OpenBsCloudAutoSyncAnnouncement.ts | 72 --- .../utils/Openers/OpenPrivacyStatement.ts | 49 -- src/seqta/utils/Openers/OpenWhatsNewPopup.ts | 410 +------------- ...verPopup.ts => createPopupSocialFooter.ts} | 102 +--- src/seqta/utils/Openers/whatsNewChangelog.ts | 510 ++++++++++++++++++ src/seqta/utils/calendarSync/settings.ts | 6 +- src/seqta/utils/calendarSync/syncEngine.ts | 4 +- src/seqta/utils/calendarSync/syncRunner.ts | 87 ++- src/seqta/utils/cloudSettingsSync.ts | 2 +- .../googleCalendar/calendarSyncListener.ts | 6 +- src/seqta/utils/googleCalendar/storage.ts | 9 - .../utils/googleCalendar/syncEngine.test.ts | 8 +- src/seqta/utils/isEqual.test.ts | 24 + src/seqta/utils/isEqual.ts | 20 + src/seqta/utils/listeners/EventManager.ts | 7 +- src/seqta/utils/listeners/SettingsState.ts | 41 +- src/seqta/utils/outlookCalendar/storage.ts | 8 - src/seqta/utils/seqtaMenuColourPatch.js | 16 +- src/seqta/utils/sidebarMenuIcons.ts | 1 + src/seqta/utils/updateTimetableTimes.ts | 13 +- vite.config.ts | 18 - 63 files changed, 1098 insertions(+), 1719 deletions(-) delete mode 100644 .parcelrc delete mode 100644 src/interface/renderInShadow.ts create mode 100644 src/plugins/built-in/assessmentsAverage/lazy.ts create mode 100644 src/plugins/built-in/enhancedNavigation/lazy.ts create mode 100644 src/plugins/built-in/messageFolders/lazy.ts delete mode 100644 src/plugins/built-in/test/index.ts create mode 100644 src/plugins/built-in/timetableEdit/lazy.ts delete mode 100644 src/plugins/runtime.ts create mode 100644 src/seqta/ui/loading-spinner.html delete mode 100644 src/seqta/utils/Openers/OpenBsCloudAutoSyncAnnouncement.ts delete mode 100644 src/seqta/utils/Openers/OpenPrivacyStatement.ts rename src/seqta/utils/Openers/{OpenMinecraftServerPopup.ts => createPopupSocialFooter.ts} (78%) create mode 100644 src/seqta/utils/Openers/whatsNewChangelog.ts delete mode 100644 src/seqta/utils/googleCalendar/storage.ts create mode 100644 src/seqta/utils/isEqual.test.ts create mode 100644 src/seqta/utils/isEqual.ts delete mode 100644 src/seqta/utils/outlookCalendar/storage.ts diff --git a/.parcelrc b/.parcelrc deleted file mode 100644 index 6f883d01..00000000 --- a/.parcelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ -"transformers": { - "raw:*": ["@parcel/transformer-raw"] - } -} \ No newline at end of file diff --git a/package.json b/package.json index fda71559..4bf8cc04 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,6 @@ "publish": "bun lib/publish.js --b", "zip": "bedframe zip" }, - "targets": { - "prod": { - "sourceMap": { - "inline": false - } - } - }, "keywords": [], "author": { "name": "SethBurkart123", @@ -42,35 +35,44 @@ }, "license": "MIT", "devDependencies": { - "@babel/plugin-transform-runtime": "^7.26.9", "@babel/runtime": "^7.26.9", "@bedframe/cli": "^0.1.2", "@crxjs/vite-plugin": "^2.6.1", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@tailwindcss/forms": "^0.5.10", + "@tsconfig/svelte": "^5.0.4", + "@types/chrome": "^0.1.4", + "@types/color": "^4.2.0", "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.8", "@types/jest": "^30.0.0", "@types/mime-types": "^3.0.1", - "@types/react": "^19.0.10", - "@types/react-dom": "^19.0.4", + "@types/node": "^24.3.0", + "@types/qrcode": "^1.5.6", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@types/sortablejs": "^1.15.8", + "@types/webextension-polyfill": "^0.12.3", "@typescript-eslint/eslint-plugin": "^8.60.1", "@typescript-eslint/parser": "^8.60.1", + "autoprefixer": "^10.4.21", "cross-env": "^10.0.0", "dependency-cruiser": "^17.0.1", + "esbuild": "^0.25.3", "eslint": "^9.33.0", "eslint-plugin-import": "^2.31.0", "glob": "^13.0.6", "jest": "^30.4.2", "jest-environment-jsdom": "^30.4.1", "mime-types": "^3.0.1", + "postcss": "^8.5.3", "prettier": "^3.5.3", - "process": "^0.11.10", "publish-browser-extension": "^4.0.4", "sass": "^1.85.1", - "sass-loader": "^16.0.5", - "semver": "^7.7.1", "tailwindcss": "3", "ts-jest": "^29.4.11", - "url": "^0.11.4" + "typescript": "^5.8.2", + "vite": "^6.2.1" }, "dependencies": { "@bedframe/core": "^0.1.0", @@ -81,53 +83,31 @@ "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.4", - "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@tailwindcss/forms": "^0.5.10", - "@tsconfig/svelte": "^5.0.4", - "@types/chrome": "^0.1.4", - "@types/color": "^4.2.0", - "@types/lodash": "^4.17.16", - "@types/node": "^24.3.0", - "@types/qrcode": "^1.5.6", - "@types/sortablejs": "^1.15.8", - "@types/uuid": "^10.0.0", - "@types/webextension-polyfill": "^0.12.3", + "@huggingface/transformers": "^3.8.1", "@uiw/codemirror-extensions-color": "^4.23.10", "@uiw/codemirror-theme-github": "^4.23.10", - "autoprefixer": "^10.4.21", "canvas-confetti": "^1.9.3", - "codemirror": "^6.0.1", "color": "^5.0.0", "d3-scale": "^4.0.2", "d3-shape": "^3.2.0", "dompurify": "^3.2.4", - "@huggingface/transformers": "^3.8.1", "embeddia": "^1.3.0", "embla-carousel-autoplay": "^8.5.2", "embla-carousel-svelte": "^8.5.2", - "esbuild": "^0.25.3", - "events": "^3.3.0", - "flexsearch": "^0.8.147", "fuse.js": "^7.1.0", - "idb": "^8.0.2", "layerchart": "2.0.0-next.27", "localforage": "^1.10.0", - "lodash": "^4.17.21", "mathjs": "^14.4.0", - "million": "^3.1.11", "motion": "^12.4.12", "pdfjs-dist": "^5.4.530", - "postcss": "^8.5.3", "qrcode": "^1.5.4", "react": "17", "react-best-gradient-color-picker": "3.0.11", "react-dom": "17", "rss-parser": "^3.13.0", + "semver": "^7.7.1", "sortablejs": "^1.15.6", "svelte": "^5.46.4", - "typescript": "^5.8.2", - "uuid": "^11.1.0", - "vite": "^6.2.1", "webextension-polyfill": "^0.12.0" }, "overrides": { diff --git a/src/SEQTA.ts b/src/SEQTA.ts index 2cbb3239..6d4c5394 100644 --- a/src/SEQTA.ts +++ b/src/SEQTA.ts @@ -6,7 +6,6 @@ import documentLoadCSS from "@/css/documentload.scss?inline"; import icon48 from "@/resources/icons/icon-48.png?base64"; import browser from "webextension-polyfill"; -import { init as Monofile } from "@/plugins/monofile"; import { main } from "@/seqta/main"; import { delay } from "./seqta/utils/delay"; import { initializeHideSensitiveToggle } from "@/seqta/utils/hideSensitiveToggle"; @@ -66,10 +65,6 @@ async function init() { IsSEQTAPage = true; verboseInfo("[BetterSEQTA+] Verified SEQTA Page"); - if (typeof window !== "undefined" && window === window.top) { - void browser.runtime.sendMessage({ type: "cloudSettingsPoll" }).catch(() => {}); - } - registerFetchSeqtaAppLinkListener(); const documentLoadStyle = document.createElement("style"); @@ -112,10 +107,12 @@ async function init() { } await main(); + + const { init: Monofile } = await import("@/plugins/monofile"); Monofile(); if (settingsState.onoff) { - const { initializePlugins } = await import("@/plugins/runtime"); + const { initializePlugins } = await import("@/plugins/index"); await initializePlugins(); } diff --git a/src/background/calendarBackground.ts b/src/background/calendarBackground.ts index d01df6e7..bd53b598 100644 --- a/src/background/calendarBackground.ts +++ b/src/background/calendarBackground.ts @@ -39,7 +39,7 @@ import { clearGoogleCalendarState, readGoogleCalendarState, writeGoogleCalendarState, -} from "@/seqta/utils/googleCalendar/storage"; +} from "@/seqta/utils/calendarSync/providerStorage"; import { ensureGoogleAppCalendar } from "@/seqta/utils/googleCalendar/calendarProvisioning"; import type { GoogleCalendarStatus, @@ -49,8 +49,8 @@ import { clearOutlookCalendarState, readOutlookCalendarState, writeOutlookCalendarState, -} from "@/seqta/utils/outlookCalendar/storage"; -import type { OutlookCalendarStatus } from "@/seqta/utils/outlookCalendar/storage"; + type OutlookCalendarStatus, +} from "@/seqta/utils/calendarSync/providerStorage"; import browser from "webextension-polyfill"; const WEEKLY_PERIOD_MINUTES = 7 * 24 * 60; diff --git a/src/background/cloudSettingsAutoSync.ts b/src/background/cloudSettingsAutoSync.ts index 54661fdc..843cbfbf 100644 --- a/src/background/cloudSettingsAutoSync.ts +++ b/src/background/cloudSettingsAutoSync.ts @@ -473,5 +473,6 @@ export function initCloudSettingsAutoSync(deps: { reloadSeqtaPages: () => void } if (autoSyncInitialized) return; autoSyncInitialized = true; browser.storage.onChanged.addListener(onStorageChanged); + void runCloudSettingsPoll(); } diff --git a/src/interface/components/store/Backgrounds.svelte b/src/interface/components/store/Backgrounds.svelte index a6bcc6eb..3311014b 100644 --- a/src/interface/components/store/Backgrounds.svelte +++ b/src/interface/components/store/Backgrounds.svelte @@ -2,7 +2,6 @@ import { hasEnoughStorageSpace, isIndexedDBSupported, writeData, openDatabase, readAllData, deleteData } from '@/interface/hooks/BackgroundDataLoader'; import Spinner from '../Spinner.svelte'; import { settingsState } from '@/seqta/utils/listeners/SettingsState' - import { Index } from 'flexsearch'; import { backgroundUpdates } from '@/interface/hooks/BackgroundUpdates' import { ThemeManager } from '@/plugins/built-in/themes/theme-manager' @@ -20,8 +19,6 @@ let savedBackgrounds = $state([]); let installingBackgrounds = $state>(new Set()); let debugInfo = $state(''); - let searchIndex = $state(null); - // New state variables let activeTab = $state<'all' | 'installed' | 'photos' | 'videos'>('all'); let sortBy = $state<'newest' | 'popular' | 'name'>('newest'); @@ -36,19 +33,6 @@ } const data = await response.json(); backgrounds = data.backgrounds; - - // Initialize FlexSearch index - const index = new Index({ - tokenize: "forward", - preset: "score" - }); - - // Add backgrounds to the index - backgrounds.forEach((bg, i) => { - index.add(i, bg.name + " " + bg.description); - }); - - searchIndex = index; debugInfo = `Loaded ${backgrounds.length} backgrounds`; await loadSavedBackgrounds(); } catch (e) { @@ -79,10 +63,11 @@ let filteredBackgrounds = $derived((() => { let filtered = backgrounds; - // Use FlexSearch if there's a search term - if (searchTerm.trim() && searchIndex) { - const results = searchIndex.search(searchTerm) as number[]; - filtered = results.map(i => backgrounds[i]); + if (searchTerm.trim()) { + const q = searchTerm.trim().toLowerCase(); + filtered = backgrounds.filter((bg) => + `${bg.name} ${bg.description}`.toLowerCase().includes(q), + ); } // Apply category filtering diff --git a/src/interface/hooks/BackgroundDataLoader.ts b/src/interface/hooks/BackgroundDataLoader.ts index 141ae05b..17f79338 100644 --- a/src/interface/hooks/BackgroundDataLoader.ts +++ b/src/interface/hooks/BackgroundDataLoader.ts @@ -1,149 +1,55 @@ -import { type DBSchema, type IDBPDatabase, openDB } from "idb"; +import localforage from "localforage"; -/** - * Defines the schema for the IndexedDB database used for storing background image data. - * - * @interface BackgroundDB - * @extends {DBSchema} - * @property {object} backgrounds - The object store for background images. - * @property {string} backgrounds.key - The type of the key for the object store (in this case, it's `id` as defined in `keyPath`). - * @property {object} backgrounds.value - The structure of the objects stored. - * @property {string} backgrounds.value.id - The unique identifier for the background image record. - * @property {string} backgrounds.value.type - The MIME type of the image (e.g., "image/png", "image/jpeg"). - * @property {Blob} backgrounds.value.blob - The binary large object (Blob) containing the image data. - */ -interface BackgroundDB extends DBSchema { - backgrounds: { - key: string; // Corresponds to the 'id' property due to keyPath: "id" - value: { - id: string; - type: string; - blob: Blob; - }; - }; +type BackgroundRecord = { id: string; type: string; blob: Blob }; + +const store = localforage.createInstance({ + name: "BackgroundDB", + storeName: "backgrounds", +}); + +export async function openDatabase(): Promise { + await store.ready(); + return store; } -let db: IDBPDatabase | null = null; - -/** - * Initializes and opens an IndexedDB connection or returns an existing one. - * If the database doesn't exist or needs an upgrade, the `upgrade` callback - * creates the 'backgrounds' object store with 'id' as the keyPath. - * - * @async - * @returns {Promise>} A promise that resolves with the database instance. - */ -export async function openDatabase(): Promise> { - if (db) return db; - - db = await openDB("BackgroundDB", 1, { - upgrade(db: IDBPDatabase) { - db.createObjectStore("backgrounds", { keyPath: "id" }); - }, +export async function readAllData(): Promise { + await store.ready(); + const items: BackgroundRecord[] = []; + await store.iterate((value) => { + if (value?.id && value.blob) items.push(value); }); - - return db; + return items; } -/** - * Retrieves all background image records from the 'backgrounds' object store in IndexedDB. - * - * @async - * @returns {Promise>} A promise that resolves with an array of all background image records. - */ -export async function readAllData(): Promise< - Array<{ id: string; type: string; blob: Blob }> -> { - const db = await openDatabase(); - return db.getAll("backgrounds"); -} - -/** - * Writes or updates a background image record in the 'backgrounds' object store. - * If a record with the given `id` already exists, it will be updated. Otherwise, a new record is created. - * - * @async - * @param {string} id - The unique identifier for the background image record. - * @param {string} type - The MIME type of the image (e.g., "image/png"). - * @param {Blob} blob - The Blob object containing the image data. - * @returns {Promise} A promise that resolves when the data has been successfully written. - */ export async function writeData( id: string, type: string, blob: Blob, ): Promise { - const db = await openDatabase(); - await db.put("backgrounds", { id, type, blob }); + await store.setItem(id, { id, type, blob }); } -/** - * Deletes a background image record from the 'backgrounds' object store by its ID. - * - * @async - * @param {string} id - The unique identifier of the background image record to delete. - * @returns {Promise} A promise that resolves when the data has been successfully deleted. - */ export async function deleteData(id: string): Promise { - const db = await openDatabase(); - await db.delete("backgrounds", id); + await store.removeItem(id); } -/** - * Clears all records from the 'backgrounds' object store in IndexedDB. - * - * @async - * @returns {Promise} A promise that resolves when all data has been successfully cleared. - */ export async function clearAllData(): Promise { - const db = await openDatabase(); - await db.clear("backgrounds"); + await store.clear(); } -/** - * Retrieves a single background image record from the 'backgrounds' object store by its ID. - * - * @async - * @param {string} id - The unique identifier of the background image record to retrieve. - * @returns {Promise<{id: string, type: string, blob: Blob} | undefined>} A promise that resolves with the - * background image record if found, or undefined otherwise. - */ export async function getDataById( id: string, -): Promise<{ id: string; type: string; blob: Blob } | undefined> { - const db = await openDatabase(); - return db.get("backgrounds", id); +): Promise { + const item = await store.getItem(id); + return item ?? undefined; } -/** - * Closes the active IndexedDB connection and nullifies the global `db` variable. - * This is important to release resources and allow for proper database management. - */ -export function closeDatabase(): void { - if (db) { - db.close(); - db = null; - } -} +export function closeDatabase(): void {} -/** - * Checks if IndexedDB is supported by the current browser environment. - * - * @returns {boolean} True if IndexedDB is supported, false otherwise. - */ export function isIndexedDBSupported(): boolean { return "indexedDB" in window; } -/** - * Estimates available storage space and checks if it's sufficient for the specified `requiredSpace`. - * Uses the `navigator.storage.estimate()` API if available. - * If the API is not available or cannot determine space, it defaults to assuming enough space is available. - * - * @async - * @param {number} requiredSpace - The amount of storage space required, in bytes. - * @returns {Promise} A promise that resolves with true if enough space is estimated to be available, false otherwise. - */ export async function hasEnoughStorageSpace( requiredSpace: number, ): Promise { @@ -153,6 +59,5 @@ export async function hasEnoughStorageSpace( return quota - usage > requiredSpace; } } - // If we can't determine, assume there's enough space return true; } diff --git a/src/interface/main.ts b/src/interface/main.ts index d3f1e4ee..3cd8a164 100644 --- a/src/interface/main.ts +++ b/src/interface/main.ts @@ -1,11 +1,20 @@ import { mount } from "svelte"; import type { SvelteComponent } from "svelte"; -import style from "./index.css?inline"; +import settingsStyle from "./index.css?inline"; +import contentShadowStyle from "./contentShadow.css?inline"; + +const shadowStyles = { + settings: settingsStyle, + content: contentShadowStyle, +} as const; + +export type ShadowStyleVariant = keyof typeof shadowStyles; export default function renderSvelte( Component: SvelteComponent | any, mountPoint: ShadowRoot | HTMLElement, props: Record = {}, + shadowStyle: ShadowStyleVariant = "settings", ) { const app = mount(Component, { target: mountPoint, @@ -17,7 +26,7 @@ export default function renderSvelte( if (mountPoint instanceof ShadowRoot) { const styleElement = document.createElement("style"); - styleElement.textContent = style; + styleElement.textContent = shadowStyles[shadowStyle]; mountPoint.appendChild(styleElement); } diff --git a/src/interface/pages/settings.svelte b/src/interface/pages/settings.svelte index 4f778991..7b3d0c25 100644 --- a/src/interface/pages/settings.svelte +++ b/src/interface/pages/settings.svelte @@ -12,7 +12,6 @@ import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup"; import { OpenAboutPage } from "@/seqta/utils/Openers/OpenAboutPage"; import { OpenWhatsNewPopup } from "@/seqta/utils/Openers/OpenWhatsNewPopup"; - //import { OpenMinecraftServerPopup } from "@/seqta/utils/Openers/OpenMinecraftServerPopup"; import type { Component } from "svelte"; import FontPickerModal from "../components/FontPickerModal.svelte"; @@ -87,11 +86,6 @@ closeExtensionPopup(); }; - /* const openMinecraftServer = () => { - OpenMinecraftServerPopup(); - closeExtensionPopup(); - }; */ - const openPrivacyStatement = () => { window.open("https://betterseqta.org/privacy", "_blank"); closeExtensionPopup(); @@ -221,140 +215,6 @@ {"\uecba"} - - {/if} diff --git a/src/interface/pages/themeCreator.svelte b/src/interface/pages/themeCreator.svelte index 10c5b2bd..8a0a1453 100644 --- a/src/interface/pages/themeCreator.svelte +++ b/src/interface/pages/themeCreator.svelte @@ -1,5 +1,4 @@