diff --git a/src/background.ts b/src/background.ts index 2bc7f515..414d823a 100644 --- a/src/background.ts +++ b/src/background.ts @@ -49,7 +49,7 @@ browser.runtime.onMessage.addListener( break; case "setDefaultStorage": - SetStorageValue(DefaultValues); + SetStorageValue(getDefaultValues()); break; case "sendNews": @@ -64,59 +64,71 @@ browser.runtime.onMessage.addListener( }, ); -const DefaultValues: SettingsState = { - onoff: true, - animatedbk: true, - bksliderinput: "50", - transparencyEffects: false, - lessonalert: true, - defaultmenuorder: [], - menuitems: { - assessments: { toggle: true }, - courses: { toggle: true }, - dashboard: { toggle: true }, - documents: { toggle: true }, - forums: { toggle: true }, - goals: { toggle: true }, - home: { toggle: true }, - messages: { toggle: true }, - myed: { toggle: true }, - news: { toggle: true }, - notices: { toggle: true }, - portals: { toggle: true }, - reports: { toggle: true }, - settings: { toggle: true }, - timetable: { toggle: true }, - welcome: { toggle: true }, - }, - menuorder: [], - subjectfilters: {}, - selectedTheme: "", - selectedColor: - "linear-gradient(40deg, rgba(201,61,0,1) 0%, RGBA(170, 5, 58, 1) 100%)", - originalSelectedColor: "", - DarkMode: true, - animations: true, - assessmentsAverage: true, - defaultPage: "home", - shortcuts: [ - { - name: "Outlook", - enabled: true, +function detectLowEndDevice(): boolean { + // Check for low-end hardware indicators + const lowCoreCount = navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4; + const lowMemory = (navigator as any).deviceMemory && (navigator as any).deviceMemory <= 2; + + return lowCoreCount || lowMemory; +} + +function getDefaultValues(): SettingsState { + const isLowEndDevice = detectLowEndDevice(); + + return { + onoff: true, + animatedbk: true, + bksliderinput: "50", + transparencyEffects: false, + lessonalert: true, + defaultmenuorder: [], + menuitems: { + assessments: { toggle: true }, + courses: { toggle: true }, + dashboard: { toggle: true }, + documents: { toggle: true }, + forums: { toggle: true }, + goals: { toggle: true }, + home: { toggle: true }, + messages: { toggle: true }, + myed: { toggle: true }, + news: { toggle: true }, + notices: { toggle: true }, + portals: { toggle: true }, + reports: { toggle: true }, + settings: { toggle: true }, + timetable: { toggle: true }, + welcome: { toggle: true }, }, - { - name: "Office", - enabled: true, - }, - { - name: "Google", - enabled: true, - }, - ], - customshortcuts: [], - lettergrade: false, - newsSource: "australia", -}; + menuorder: [], + subjectfilters: {}, + selectedTheme: "", + selectedColor: + "linear-gradient(40deg, rgba(201,61,0,1) 0%, RGBA(170, 5, 58, 1) 100%)", + originalSelectedColor: "", + DarkMode: true, + animations: !isLowEndDevice, + assessmentsAverage: true, + defaultPage: "home", + shortcuts: [ + { + name: "Outlook", + enabled: true, + }, + { + name: "Office", + enabled: true, + }, + { + name: "Google", + enabled: true, + }, + ], + customshortcuts: [], + lettergrade: false, + newsSource: "australia", + }; +} function SetStorageValue(object: any) { for (var i in object) { @@ -124,78 +136,11 @@ function SetStorageValue(object: any) { } } -function convertBksliderToSpeed(bksliderinput: number): number { - const minBase = 50; - const maxBase = 150; - - const scaledValue = - 2 + ((maxBase - bksliderinput) / (maxBase - minBase)) ** 4; - const baseSpeed = 3; - - const speed = baseSpeed / scaledValue; - return speed; -} - -async function migrateLegacySettings() { - const storage = (await browser.storage.local.get( - null, - )) as unknown as SettingsState; - - // Animated Background Migration - if ("animatedbk" in storage || "bksliderinput" in storage) { - const animatedSettings = { - enabled: storage.animatedbk ?? true, - speed: storage.bksliderinput - ? convertBksliderToSpeed(parseFloat(storage.bksliderinput)) - : 1, - }; - await browser.storage.local.set({ - "plugin.animated-background.settings": animatedSettings, - }); - } - - // Assessments Average Migration - if ("assessmentsAverage" in storage || "lettergrade" in storage) { - const assessmentsSettings = { - enabled: storage.assessmentsAverage ?? true, - lettergrade: storage.lettergrade ?? false, - }; - await browser.storage.local.set({ - "plugin.assessments-average.settings": assessmentsSettings, - }); - } - - if ("selectedTheme" in storage) { - const themesSettings = { enabled: true }; - await browser.storage.local.set({ - "plugin.themes.settings": themesSettings, - }); - } - if (storage.notificationCollector !== false) { - await browser.storage.local.set({ - "plugin.notificationCollector.settings": { enabled: true }, - }); - } else { - await browser.storage.local.set({ - "plugin.notificationCollector.settings": { enabled: false }, - }); - } - - const keysToRemove = [ - "animatedbk", - "bksliderinput", - "assessmentsAverage", - "lettergrade", - ]; - await browser.storage.local.remove(keysToRemove); -} - browser.runtime.onInstalled.addListener(function (event) { browser.storage.local.remove(["justupdated"]); browser.storage.local.remove(["data"]); if (event.reason == "install" || event.reason == "update") { browser.storage.local.set({ justupdated: true }); - migrateLegacySettings(); } }); diff --git a/src/plugins/built-in/globalSearch/lazy.ts b/src/plugins/built-in/globalSearch/lazy.ts new file mode 100644 index 00000000..1f49a1f0 --- /dev/null +++ b/src/plugins/built-in/globalSearch/lazy.ts @@ -0,0 +1,88 @@ +import { defineLazyPlugin } from "../../core/dynamicLoader"; +import { + booleanSetting, + buttonSetting, + defineSettings, + hotkeySetting, +} from "../../core/settingsHelpers"; +// Platform-aware default hotkey +const getDefaultHotkey = () => { + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + return isMac ? "cmd+k" : "ctrl+k"; +}; + +const settings = defineSettings({ + searchHotkey: hotkeySetting({ + default: getDefaultHotkey(), + title: "Search Hotkey", + description: "Keyboard shortcut to open the search", + }), + showRecentFirst: booleanSetting({ + default: true, + title: "Show Recent First", + description: "Sort dynamic content by most recent first", + }), + transparencyEffects: booleanSetting({ + default: true, + title: "Transparency Effects", + description: "Enable transparency effects for the search bar", + }), + runIndexingOnLoad: booleanSetting({ + default: true, + title: "Index on Page Load", + description: "Run content indexing when SEQTA loads", + }), + resetIndex: buttonSetting({ + title: "Reset Index", + description: "Reset the search index and storage", + trigger: async () => { + const confirmed = confirm("Are you sure you want to reset the search index and storage?"); + + if (confirmed) { + try { + // Dynamically import the worker manager to avoid loading heavy dependencies + const { VectorWorkerManager } = await import("./src/indexing/worker/vectorWorkerManager"); + const workerManager = VectorWorkerManager.getInstance(); + await workerManager.resetWorker(); + console.log("Vector worker reset successfully"); + } catch (e) { + console.warn("Failed to reset vector worker:", e); + } + + // Delete both 'embeddiaDB' and 'betterseqta-index' using native IndexedDB APIs + const deleteDb = (dbName: string) => { + return new Promise((resolve, reject) => { + const req = indexedDB.deleteDatabase(dbName); + req.onsuccess = () => resolve(); + req.onerror = () => reject(req.error); + req.onblocked = () => { + reject(new Error(`One database is open, failed to remove: ${dbName}`)); + }; + }); + }; + try { + await deleteDb("embeddiaDB"); + await deleteDb("betterseqta-index"); + alert("Search index and storage have been reset."); + } catch (e) { + alert("Failed to reset one or more databases: " + String(e)); + } + } + }, + }), +}); + +// Create the lazy plugin definition - this loads immediately but doesn't import heavy dependencies +export default defineLazyPlugin({ + id: "global-search", + name: "Global Search", + description: "Quick search for everything in SEQTA", + version: "1.0.0", + settings, + disableToggle: true, + defaultEnabled: false, + beta: true, + + // Lazy loader - only imports the heavy plugin when actually needed + loader: () => import("./src/core/index") +}); diff --git a/src/plugins/core/dynamicLoader.ts b/src/plugins/core/dynamicLoader.ts new file mode 100644 index 00000000..75e32222 --- /dev/null +++ b/src/plugins/core/dynamicLoader.ts @@ -0,0 +1,66 @@ +import type { Plugin, PluginSettings } from "./types"; + +/** + * Interface for lazy-loaded plugin definitions + */ +export interface LazyPlugin { + id: string; + name: string; + description: string; + version: string; + settings: T; + styles?: string; + disableToggle?: boolean; + defaultEnabled?: boolean; + beta?: boolean; + + // Instead of a run function, we have a loader that imports the actual plugin + loader: () => Promise<{ default: Plugin }>; +} + +/** + * Converts a lazy plugin into a regular plugin by wrapping the run function + * with dynamic import logic + */ +export function createLazyPlugin( + lazyPlugin: LazyPlugin +): Plugin { + return { + id: lazyPlugin.id, + name: lazyPlugin.name, + description: lazyPlugin.description, + version: lazyPlugin.version, + settings: lazyPlugin.settings, + styles: lazyPlugin.styles, + disableToggle: lazyPlugin.disableToggle, + defaultEnabled: lazyPlugin.defaultEnabled, + beta: lazyPlugin.beta, + + run: async (api) => { + console.info(`[BetterSEQTA+] Dynamically loading plugin "${lazyPlugin.id}"...`); + + try { + // Dynamically import the actual plugin implementation + const { default: actualPlugin } = await lazyPlugin.loader(); + + console.info(`[BetterSEQTA+] Successfully loaded plugin "${lazyPlugin.id}"`); + + // Execute the actual plugin's run function + return await actualPlugin.run(api); + } catch (error) { + console.error(`[BetterSEQTA+] Failed to dynamically load plugin "${lazyPlugin.id}":`, error); + throw error; + } + } + }; +} + +/** + * Helper function to create a lazy plugin definition + */ +export function defineLazyPlugin( + config: LazyPlugin +): Plugin { + return createLazyPlugin(config); +} + diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 1c06d785..f4c5e989 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,16 +1,18 @@ import { PluginManager } from "./core/manager"; -// plugins +// Lightweight plugins (load immediately) import timetablePlugin from "./built-in/timetable"; import notificationCollectorPlugin from "./built-in/notificationCollector"; import themesPlugin from "./built-in/themes"; import animatedBackgroundPlugin from "./built-in/animatedBackground"; import assessmentsAveragePlugin from "./built-in/assessmentsAverage"; -import globalSearchPlugin from "./built-in/globalSearch/src/core"; import profilePicturePlugin from "./built-in/profilePicture"; import assessmentsOverviewPlugin from "./built-in/assessmentsOverview"; //import testPlugin from './built-in/test'; +// Heavy plugins (lazy-loaded only when enabled) +import globalSearchPluginLazy from "./built-in/globalSearch/lazy"; + // Initialize plugin manager const pluginManager = PluginManager.getInstance(); @@ -20,11 +22,13 @@ pluginManager.registerPlugin(animatedBackgroundPlugin); pluginManager.registerPlugin(assessmentsAveragePlugin); pluginManager.registerPlugin(notificationCollectorPlugin); pluginManager.registerPlugin(timetablePlugin); -pluginManager.registerPlugin(globalSearchPlugin); pluginManager.registerPlugin(profilePicturePlugin); pluginManager.registerPlugin(assessmentsOverviewPlugin); //pluginManager.registerPlugin(testPlugin); +// Register heavy plugins with lazy loading +pluginManager.registerPlugin(globalSearchPluginLazy); + export { init as Monofile } from "./monofile"; export async function initializePlugins(): Promise {