mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 11:44:40 +00:00
dev
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
|||||||
|
--- a/Users/sethburkart/Documents/Coding/betterseqta-plus/src/plugins/core/settings.ts
|
||||||
|
+++ b/Users/sethburkart/Documents/Coding/betterseqta-plus/src/plugins/core/settings.ts
|
||||||
|
@@ -2,7 +2,7 @@
|
||||||
|
|
||||||
|
// Base interfaces for our settings
|
||||||
|
interface BaseSettingOptions {
|
||||||
|
- title: string;
|
||||||
|
+ readonly title: string; // Mark as readonly where appropriate
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -11,21 +11,21 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StringSettingOptions extends BaseSettingOptions {
|
||||||
|
- default: string;
|
||||||
|
+ readonly default: string;
|
||||||
|
maxLength?: number;
|
||||||
|
pattern?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NumberSettingOptions extends BaseSettingOptions {
|
||||||
|
- default: number;
|
||||||
|
+ readonly default: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
step?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectSettingOptions<T extends string> extends BaseSettingOptions {
|
||||||
|
- default: T;
|
||||||
|
- options: readonly T[];
|
||||||
|
+ readonly default: T;
|
||||||
|
+ readonly options: readonly T[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual decorators
|
||||||
|
@@ -34,14 +34,16 @@
|
||||||
|
// Ensure the settings property exists on the constructor's prototype
|
||||||
|
const proto = target.constructor.prototype;
|
||||||
|
if (!proto.hasOwnProperty('settings')) {
|
||||||
|
- proto.settings = {};
|
||||||
|
+ // Initialize with a base type that can be extended
|
||||||
|
+ Object.defineProperty(proto, 'settings', {
|
||||||
|
+ value: {},
|
||||||
|
+ writable: true, // Allows adding properties
|
||||||
|
+ configurable: true,
|
||||||
|
+ enumerable: true
|
||||||
|
+ });
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+
|
||||||
|
// Add the setting to the prototype's settings object with const assertion
|
||||||
|
proto.settings[propertyKey] = {
|
||||||
|
type: 'boolean' as const,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
- };
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-export function StringSetting(options: StringSettingOptions): PropertyDecorator {
|
||||||
|
- return (target: Object, propertyKey: string | symbol) => {
|
||||||
|
- // Ensure the settings property exists on the constructor's prototype
|
||||||
|
- const proto = target.constructor.prototype;
|
||||||
|
- if (!proto.hasOwnProperty('settings')) {
|
||||||
|
- proto.settings = {};
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- // Add the setting to the prototype's settings object with const assertion
|
||||||
|
- proto.settings[propertyKey] = {
|
||||||
|
- type: 'string' as const,
|
||||||
|
- ...options
|
||||||
|
- };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -50,14 +52,16 @@
|
||||||
|
// Ensure the settings property exists on the constructor's prototype
|
||||||
|
const proto = target.constructor.prototype;
|
||||||
|
if (!proto.hasOwnProperty('settings')) {
|
||||||
|
- proto.settings = {};
|
||||||
|
+ Object.defineProperty(proto, 'settings', {
|
||||||
|
+ value: {},
|
||||||
|
+ writable: true,
|
||||||
|
+ configurable: true,
|
||||||
|
+ enumerable: true
|
||||||
|
+ });
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+
|
||||||
|
// Add the setting to the prototype's settings object with const assertion
|
||||||
|
proto.settings[propertyKey] = {
|
||||||
|
type: 'number' as const,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
- };
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-export function SelectSetting<T extends string>(options: SelectSettingOptions<T>): PropertyDecorator {
|
||||||
|
- return (target: Object, propertyKey: string | symbol) => {
|
||||||
|
- // Ensure the settings property exists on the constructor's prototype
|
||||||
|
- const proto = target.constructor.prototype;
|
||||||
|
- if (!proto.hasOwnProperty('settings')) {
|
||||||
|
- proto.settings = {};
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- // Add the setting to the prototype's settings object with const assertion
|
||||||
|
- proto.settings[propertyKey] = {
|
||||||
|
- type: 'select' as const,
|
||||||
|
- ...options
|
||||||
|
- };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base plugin class that handles settings
|
||||||
|
export abstract class BasePlugin<T extends PluginSettings = PluginSettings> {
|
||||||
|
// The settings property will be populated by decorators
|
||||||
|
- settings!: T;
|
||||||
|
-
|
||||||
|
+ // Keep the instance property and constructor logic as is,
|
||||||
|
+ // as changing it would require changing animated-background/index.ts
|
||||||
|
+ settings!: T; // Use definite assignment assertion
|
||||||
|
+
|
||||||
|
constructor() {
|
||||||
|
// Copy settings from the prototype to the instance
|
||||||
|
// This ensures that each instance has its own settings object
|
||||||
@@ -1010,34 +1010,6 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
margin-right: 157.5px;
|
margin-right: 157.5px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
.bg {
|
|
||||||
animation: slide 3s ease-in-out infinite alternate;
|
|
||||||
background: var(--better-main);
|
|
||||||
bottom: 0;
|
|
||||||
left: -50%;
|
|
||||||
opacity: 0.5;
|
|
||||||
position: fixed;
|
|
||||||
right: -50%;
|
|
||||||
top: 0;
|
|
||||||
z-index: 0 !important;
|
|
||||||
overflow: hidden;
|
|
||||||
scale: 1.5;
|
|
||||||
}
|
|
||||||
.bg2 {
|
|
||||||
animation-direction: alternate-reverse;
|
|
||||||
animation-duration: 4s;
|
|
||||||
}
|
|
||||||
.bg3 {
|
|
||||||
animation-duration: 5s;
|
|
||||||
}
|
|
||||||
@keyframes slide {
|
|
||||||
0% {
|
|
||||||
transform: translate(50%) rotate(-60deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(5%) rotate(-60deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.home-root {
|
.home-root {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import type { Plugin } from '../../core/types';
|
||||||
|
import styles from './styles.css?inline';
|
||||||
|
import { BasePlugin, NumberSetting } from '../../core/settings';
|
||||||
|
|
||||||
|
class AnimatedBackgroundPluginClass extends BasePlugin {
|
||||||
|
@NumberSetting({
|
||||||
|
default: 1,
|
||||||
|
title: "Animation Speed",
|
||||||
|
description: "Controls the speed of the animated background",
|
||||||
|
min: 0.1,
|
||||||
|
max: 2
|
||||||
|
})
|
||||||
|
speed!: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsInstance = new AnimatedBackgroundPluginClass();
|
||||||
|
|
||||||
|
const animatedBackgroundPlugin: Plugin<typeof settingsInstance.settings> = {
|
||||||
|
id: 'animated-background',
|
||||||
|
name: 'Animated Background',
|
||||||
|
description: 'Adds an animated background to BetterSEQTA+',
|
||||||
|
version: '1.0.0',
|
||||||
|
disableToggle: true,
|
||||||
|
styles,
|
||||||
|
settings: settingsInstance.settings,
|
||||||
|
|
||||||
|
run: async (api) => {
|
||||||
|
// Create the background elements
|
||||||
|
const container = document.getElementById("container");
|
||||||
|
const menu = document.getElementById("menu");
|
||||||
|
|
||||||
|
if (!container || !menu) {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const backgrounds = [
|
||||||
|
{ classes: ["bg"] },
|
||||||
|
{ classes: ["bg", "bg2"] },
|
||||||
|
{ classes: ["bg", "bg3"] }
|
||||||
|
];
|
||||||
|
|
||||||
|
backgrounds.forEach(({ classes }) => {
|
||||||
|
const bk = document.createElement("div");
|
||||||
|
classes.forEach(cls => bk.classList.add(cls));
|
||||||
|
container.insertBefore(bk, menu);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set initial speed
|
||||||
|
updateAnimationSpeed(api.settings.speed);
|
||||||
|
|
||||||
|
// Listen for speed changes
|
||||||
|
const speedUnregister = api.settings.onChange('speed', updateAnimationSpeed);
|
||||||
|
|
||||||
|
// Return cleanup function
|
||||||
|
return () => {
|
||||||
|
speedUnregister.unregister();
|
||||||
|
// Remove background elements
|
||||||
|
const backgrounds = document.getElementsByClassName('bg');
|
||||||
|
Array.from(backgrounds).forEach(element => element.remove());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateAnimationSpeed(speed: number) {
|
||||||
|
const bgElements = document.getElementsByClassName('bg');
|
||||||
|
Array.from(bgElements).forEach((element, index) => {
|
||||||
|
const baseSpeed = index === 0 ? 3 : index === 1 ? 4 : 5;
|
||||||
|
(element as HTMLElement).style.animationDuration = `${baseSpeed / speed}s`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default animatedBackgroundPlugin;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
.bg {
|
||||||
|
animation: slide 3s ease-in-out infinite alternate;
|
||||||
|
background: var(--better-main);
|
||||||
|
bottom: 0;
|
||||||
|
left: -50%;
|
||||||
|
opacity: 0.5;
|
||||||
|
position: fixed;
|
||||||
|
right: -50%;
|
||||||
|
top: 0;
|
||||||
|
z-index: 0 !important;
|
||||||
|
overflow: hidden;
|
||||||
|
scale: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg2 {
|
||||||
|
animation-direction: alternate-reverse;
|
||||||
|
animation-duration: 4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg3 {
|
||||||
|
animation-duration: 5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide {
|
||||||
|
0% {
|
||||||
|
transform: translate(50%) rotate(-60deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(5%) rotate(-60deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export function CreateBackground() {
|
||||||
|
const bkCheck = document.getElementsByClassName("bg");
|
||||||
|
if (bkCheck.length !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating and inserting 3 divs containing the background applied to the pages
|
||||||
|
const container = document.getElementById("container");
|
||||||
|
const menu = document.getElementById("menu");
|
||||||
|
|
||||||
|
if (!container || !menu) return;
|
||||||
|
|
||||||
|
const backgrounds = [
|
||||||
|
{ classes: ["bg"] },
|
||||||
|
{ classes: ["bg", "bg2"] },
|
||||||
|
{ classes: ["bg", "bg3"] }
|
||||||
|
];
|
||||||
|
|
||||||
|
backgrounds.forEach(({ classes }) => {
|
||||||
|
const bk = document.createElement("div");
|
||||||
|
classes.forEach(cls => bk.classList.add(cls));
|
||||||
|
container.insertBefore(bk, menu);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export function RemoveBackground() {
|
||||||
|
const backgrounds = document.getElementsByClassName("bg");
|
||||||
|
|
||||||
|
// Convert HTMLCollection to Array and remove each element
|
||||||
|
Array.from(backgrounds).forEach(element => element.remove());
|
||||||
|
}
|
||||||
@@ -41,15 +41,28 @@ function createSEQTAAPI(): SEQTAAPI {
|
|||||||
|
|
||||||
function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): SettingsAPI<T> & { loaded: Promise<void> } {
|
function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): SettingsAPI<T> & { loaded: Promise<void> } {
|
||||||
const storageKey = `plugin.${plugin.id}.settings`;
|
const storageKey = `plugin.${plugin.id}.settings`;
|
||||||
const listeners = new Map<keyof T, Set<(value: any) => void>>();
|
|
||||||
let settings: { [K in keyof T]: T[K]['default'] };
|
// Use SettingValue to properly type the listeners
|
||||||
|
// This ensures callbacks get correctly typed parameters
|
||||||
|
const listeners = new Map<keyof T, Set<(value: SettingValue<T[keyof T]>) => void>>();
|
||||||
|
|
||||||
|
let settings: { [K in keyof T]: SettingValue<T[K]> };
|
||||||
const storageListeners = new Set<(changes: { [key: string]: any }, area: string) => void>();
|
const storageListeners = new Set<(changes: { [key: string]: any }, area: string) => void>();
|
||||||
|
|
||||||
// Initialize settings with defaults
|
// Initialize settings with defaults and proper typing
|
||||||
settings = Object.entries(plugin.settings).reduce((acc, [key, setting]) => {
|
settings = Object.entries(plugin.settings).reduce((acc, [key, setting]) => {
|
||||||
acc[key as keyof T] = setting.default;
|
// Extract the value from the default based on the setting type
|
||||||
|
if (setting.type === 'boolean') {
|
||||||
|
acc[key as keyof T] = setting.default as SettingValue<T[keyof T]>;
|
||||||
|
} else if (setting.type === 'number') {
|
||||||
|
acc[key as keyof T] = setting.default as SettingValue<T[keyof T]>;
|
||||||
|
} else if (setting.type === 'string') {
|
||||||
|
acc[key as keyof T] = setting.default as SettingValue<T[keyof T]>;
|
||||||
|
} else if (setting.type === 'select') {
|
||||||
|
acc[key as keyof T] = setting.default as SettingValue<T[keyof T]>;
|
||||||
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as { [K in keyof T]: T[K]['default'] });
|
}, {} as { [K in keyof T]: SettingValue<T[K]> });
|
||||||
|
|
||||||
// Create a promise that resolves when settings are loaded
|
// Create a promise that resolves when settings are loaded
|
||||||
const loaded = (async () => {
|
const loaded = (async () => {
|
||||||
@@ -58,9 +71,22 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
|
|||||||
if (stored[storageKey]) {
|
if (stored[storageKey]) {
|
||||||
Object.entries(stored[storageKey]).forEach(([key, value]) => {
|
Object.entries(stored[storageKey]).forEach(([key, value]) => {
|
||||||
if (key in settings) {
|
if (key in settings) {
|
||||||
settings[key as keyof T] = value as any;
|
// Use proper type assertion based on the setting type
|
||||||
|
const settingType = plugin.settings[key as keyof T].type;
|
||||||
|
if (settingType === 'boolean' && typeof value === 'boolean') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'number' && typeof value === 'number') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'string' && typeof value === 'string') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'select' && typeof value === 'string') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
}
|
||||||
|
|
||||||
// Notify any listeners that might have been registered already
|
// Notify any listeners that might have been registered already
|
||||||
listeners.get(key as keyof T)?.forEach(callback => callback(value));
|
listeners.get(key as keyof T)?.forEach(callback =>
|
||||||
|
callback(settings[key as keyof T] as SettingValue<T[keyof T]>)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -76,8 +102,24 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
|
|||||||
if (newValue) {
|
if (newValue) {
|
||||||
// Update settings and notify listeners
|
// Update settings and notify listeners
|
||||||
Object.entries(newValue).forEach(([key, value]) => {
|
Object.entries(newValue).forEach(([key, value]) => {
|
||||||
settings[key as keyof T] = value as any;
|
if (key in settings) {
|
||||||
listeners.get(key as keyof T)?.forEach(callback => callback(value));
|
// Use proper type assertion based on the setting type
|
||||||
|
const settingType = plugin.settings[key as keyof T].type;
|
||||||
|
if (settingType === 'boolean' && typeof value === 'boolean') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'number' && typeof value === 'number') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'string' && typeof value === 'string') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'select' && typeof value === 'string') {
|
||||||
|
settings[key as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify listeners with the correctly typed value
|
||||||
|
listeners.get(key as keyof T)?.forEach(callback =>
|
||||||
|
callback(settings[key as keyof T] as SettingValue<T[keyof T]>)
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,14 +131,14 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
|
|||||||
const proxy = new Proxy(settings, {
|
const proxy = new Proxy(settings, {
|
||||||
get(target, prop: string) {
|
get(target, prop: string) {
|
||||||
if (prop === 'onChange') {
|
if (prop === 'onChange') {
|
||||||
return (key: keyof T, callback: (value: any) => void) => {
|
return <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => {
|
||||||
if (!listeners.has(key)) {
|
if (!listeners.has(key)) {
|
||||||
listeners.set(key, new Set());
|
listeners.set(key, new Set());
|
||||||
}
|
}
|
||||||
listeners.get(key)!.add(callback);
|
listeners.get(key)!.add(callback as (value: SettingValue<T[keyof T]>) => void);
|
||||||
return {
|
return {
|
||||||
unregister: () => {
|
unregister: () => {
|
||||||
listeners.get(key)?.delete(callback);
|
listeners.get(key)?.delete(callback as (value: SettingValue<T[keyof T]>) => void);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -108,7 +150,20 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
|
|||||||
},
|
},
|
||||||
set(target, prop: string, value: any) {
|
set(target, prop: string, value: any) {
|
||||||
if (prop === 'onChange' || prop === 'offChange' || prop === 'loaded') return false;
|
if (prop === 'onChange' || prop === 'offChange' || prop === 'loaded') return false;
|
||||||
target[prop as keyof T] = value;
|
|
||||||
|
// Try to apply the right type based on the setting definition
|
||||||
|
if (prop in plugin.settings) {
|
||||||
|
const settingType = plugin.settings[prop as keyof T].type;
|
||||||
|
if (settingType === 'boolean' && typeof value === 'boolean') {
|
||||||
|
target[prop as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'number' && typeof value === 'number') {
|
||||||
|
target[prop as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'string' && typeof value === 'string') {
|
||||||
|
target[prop as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
} else if (settingType === 'select' && typeof value === 'string') {
|
||||||
|
target[prop as keyof T] = value as SettingValue<T[keyof T]>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Store all settings under the plugin's settings key
|
// Store all settings under the plugin's settings key
|
||||||
browser.storage.local.set({
|
browser.storage.local.set({
|
||||||
@@ -116,7 +171,9 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Notify listeners
|
// Notify listeners
|
||||||
listeners.get(prop as keyof T)?.forEach(callback => callback(value));
|
listeners.get(prop as keyof T)?.forEach(callback =>
|
||||||
|
callback(target[prop as keyof T] as SettingValue<T[keyof T]>)
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
}) as SettingsAPI<T> & { loaded: Promise<void> };
|
}) as SettingsAPI<T> & { loaded: Promise<void> };
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class PluginManager {
|
|||||||
private eventBacklog: Map<string, any[]> = new Map();
|
private eventBacklog: Map<string, any[]> = new Map();
|
||||||
private cleanupFunctions: Map<string, () => void> = new Map();
|
private cleanupFunctions: Map<string, () => void> = new Map();
|
||||||
private listeners: Map<string, Set<(...args: any[]) => void>> = new Map();
|
private listeners: Map<string, Set<(...args: any[]) => void>> = new Map();
|
||||||
|
private styleElements: Map<string, HTMLStyleElement> = new Map();
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
this.setupPluginStateListener();
|
this.setupPluginStateListener();
|
||||||
@@ -89,6 +90,14 @@ export class PluginManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject plugin styles if provided
|
||||||
|
if (plugin.styles) {
|
||||||
|
const styleElement = document.createElement('style');
|
||||||
|
styleElement.textContent = plugin.styles;
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
|
this.styleElements.set(pluginId, styleElement);
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for both settings and storage to be loaded before starting the plugin
|
// Wait for both settings and storage to be loaded before starting the plugin
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -123,6 +132,13 @@ export class PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async stopPlugin(pluginId: string): Promise<void> {
|
public async stopPlugin(pluginId: string): Promise<void> {
|
||||||
|
// Remove plugin styles
|
||||||
|
const styleElement = this.styleElements.get(pluginId);
|
||||||
|
if (styleElement) {
|
||||||
|
styleElement.remove();
|
||||||
|
this.styleElements.delete(pluginId);
|
||||||
|
}
|
||||||
|
|
||||||
const cleanup = this.cleanupFunctions.get(pluginId);
|
const cleanup = this.cleanupFunctions.get(pluginId);
|
||||||
if (cleanup) {
|
if (cleanup) {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ export function BooleanSetting(options: BooleanSettingOptions): PropertyDecorato
|
|||||||
proto.settings = {};
|
proto.settings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the setting to the prototype's settings object
|
// Add the setting to the prototype's settings object with const assertion
|
||||||
proto.settings[propertyKey] = {
|
proto.settings[propertyKey] = {
|
||||||
type: 'boolean',
|
type: 'boolean' as const,
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -53,9 +53,9 @@ export function StringSetting(options: StringSettingOptions): PropertyDecorator
|
|||||||
proto.settings = {};
|
proto.settings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the setting to the prototype's settings object
|
// Add the setting to the prototype's settings object with const assertion
|
||||||
proto.settings[propertyKey] = {
|
proto.settings[propertyKey] = {
|
||||||
type: 'string',
|
type: 'string' as const,
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -69,9 +69,9 @@ export function NumberSetting(options: NumberSettingOptions): PropertyDecorator
|
|||||||
proto.settings = {};
|
proto.settings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the setting to the prototype's settings object
|
// Add the setting to the prototype's settings object with const assertion
|
||||||
proto.settings[propertyKey] = {
|
proto.settings[propertyKey] = {
|
||||||
type: 'number',
|
type: 'number' as const,
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -85,9 +85,9 @@ export function SelectSetting<T extends string>(options: SelectSettingOptions<T>
|
|||||||
proto.settings = {};
|
proto.settings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the setting to the prototype's settings object
|
// Add the setting to the prototype's settings object with const assertion
|
||||||
proto.settings[propertyKey] = {
|
proto.settings[propertyKey] = {
|
||||||
type: 'select',
|
type: 'select' as const,
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -96,13 +96,13 @@ export function SelectSetting<T extends string>(options: SelectSettingOptions<T>
|
|||||||
// Base plugin class that handles settings
|
// Base plugin class that handles settings
|
||||||
export abstract class BasePlugin<T extends PluginSettings = PluginSettings> {
|
export abstract class BasePlugin<T extends PluginSettings = PluginSettings> {
|
||||||
// The settings property will be populated by decorators
|
// The settings property will be populated by decorators
|
||||||
settings: T = {} as T;
|
settings!: T;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Copy settings from the prototype to the instance
|
// Copy settings from the prototype to the instance
|
||||||
// This ensures that each instance has its own settings object
|
// This ensures that each instance has its own settings object
|
||||||
if (this.constructor.prototype.settings) {
|
if (this.constructor.prototype.settings) {
|
||||||
this.settings = { ...this.constructor.prototype.settings };
|
this.settings = { ...this.constructor.prototype.settings } as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ export type PluginSettings = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper type to extract the actual value type from a setting
|
// Helper type to extract the actual value type from a setting
|
||||||
type SettingValue<T extends PluginSetting> = T extends BooleanSetting ? boolean :
|
export type SettingValue<T extends PluginSetting> = T extends BooleanSetting ? boolean :
|
||||||
T extends StringSetting ? string :
|
T extends StringSetting ? string :
|
||||||
T extends NumberSetting ? number :
|
T extends NumberSetting ? number :
|
||||||
T extends SelectSetting<infer O> ? O :
|
T extends SelectSetting<infer O> ? O :
|
||||||
@@ -50,7 +50,7 @@ type SettingValue<T extends PluginSetting> = T extends BooleanSetting ? boolean
|
|||||||
export type SettingsAPI<T extends PluginSettings> = {
|
export type SettingsAPI<T extends PluginSettings> = {
|
||||||
[K in keyof T]: SettingValue<T[K]>;
|
[K in keyof T]: SettingValue<T[K]>;
|
||||||
} & {
|
} & {
|
||||||
onChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => void;
|
onChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => { unregister: () => void };
|
||||||
offChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => void;
|
offChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => void;
|
||||||
loaded: Promise<void>; // Promise that resolves when settings are loaded
|
loaded: Promise<void>; // Promise that resolves when settings are loaded
|
||||||
}
|
}
|
||||||
@@ -96,6 +96,7 @@ export interface Plugin<T extends PluginSettings = PluginSettings, S = any> {
|
|||||||
description: string;
|
description: string;
|
||||||
version: string;
|
version: string;
|
||||||
settings: T;
|
settings: T;
|
||||||
|
styles?: string; // Optional CSS styles for the plugin
|
||||||
disableToggle?: boolean; // Optional flag to show/hide the plugin's enable/disable toggle in settings
|
disableToggle?: boolean; // Optional flag to show/hide the plugin's enable/disable toggle in settings
|
||||||
run: (api: PluginAPI<T, S>) => void | Promise<void> | (() => void) | Promise<(() => void)>;
|
run: (api: PluginAPI<T, S>) => void | Promise<void> | (() => void) | Promise<(() => void)>;
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import { PluginManager } from './core/manager';
|
|||||||
import timetablePlugin from './built-in/timetable';
|
import timetablePlugin from './built-in/timetable';
|
||||||
import notificationCollectorPlugin from './built-in/notificationCollector';
|
import notificationCollectorPlugin from './built-in/notificationCollector';
|
||||||
import themesPlugin from './built-in/themes';
|
import themesPlugin from './built-in/themes';
|
||||||
|
import animatedBackgroundPlugin from './built-in/animated-background';
|
||||||
|
|
||||||
// Initialize plugin manager
|
// Initialize plugin manager
|
||||||
const pluginManager = PluginManager.getInstance();
|
const pluginManager = PluginManager.getInstance();
|
||||||
@@ -12,6 +13,7 @@ const pluginManager = PluginManager.getInstance();
|
|||||||
pluginManager.registerPlugin(timetablePlugin);
|
pluginManager.registerPlugin(timetablePlugin);
|
||||||
pluginManager.registerPlugin(notificationCollectorPlugin);
|
pluginManager.registerPlugin(notificationCollectorPlugin);
|
||||||
pluginManager.registerPlugin(themesPlugin);
|
pluginManager.registerPlugin(themesPlugin);
|
||||||
|
pluginManager.registerPlugin(animatedBackgroundPlugin);
|
||||||
//pluginManager.registerPlugin(testPlugin);
|
//pluginManager.registerPlugin(testPlugin);
|
||||||
|
|
||||||
export { init as Monofile } from './monofile';
|
export { init as Monofile } from './monofile';
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { addExtensionSettings } from "@/seqta/utils/Adders/AddExtensionSettings";
|
import { addExtensionSettings } from "@/seqta/utils/Adders/AddExtensionSettings";
|
||||||
import { enableAnimatedBackground } from "@/seqta/utils/CreateEnable/EnableAnimatedBackground";
|
|
||||||
import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage";
|
import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage";
|
||||||
import { SendNewsPage } from "@/seqta/utils/SendNewsPage";
|
import { SendNewsPage } from "@/seqta/utils/SendNewsPage";
|
||||||
import { setupSettingsButton } from "@/seqta/utils/setupSettingsButton";
|
import { setupSettingsButton } from "@/seqta/utils/setupSettingsButton";
|
||||||
|
|
||||||
|
|
||||||
import { GetThresholdOfColor } from "@/seqta/ui/colors/getThresholdColour";
|
import { GetThresholdOfColor } from "@/seqta/ui/colors/getThresholdColour";
|
||||||
import { updateBgDurations } from "./Animation";
|
|
||||||
import { appendBackgroundToUI } from "./ImageBackgrounds";
|
import { appendBackgroundToUI } from "./ImageBackgrounds";
|
||||||
import stringToHTML from "@/seqta/utils/stringToHTML";
|
import stringToHTML from "@/seqta/utils/stringToHTML";
|
||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||||
@@ -42,7 +40,6 @@ async function getUserInfo() {
|
|||||||
|
|
||||||
export async function AddBetterSEQTAElements() {
|
export async function AddBetterSEQTAElements() {
|
||||||
if (settingsState.onoff) {
|
if (settingsState.onoff) {
|
||||||
initializeSettings();
|
|
||||||
if (settingsState.DarkMode) {
|
if (settingsState.DarkMode) {
|
||||||
document.documentElement.classList.add('dark');
|
document.documentElement.classList.add('dark');
|
||||||
}
|
}
|
||||||
@@ -76,11 +73,6 @@ export async function AddBetterSEQTAElements() {
|
|||||||
setupSettingsButton();
|
setupSettingsButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeSettings() {
|
|
||||||
enableAnimatedBackground();
|
|
||||||
updateBgDurations();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createHomeButton(fragment: DocumentFragment, menuList: HTMLElement) {
|
function createHomeButton(fragment: DocumentFragment, menuList: HTMLElement) {
|
||||||
const container = document.getElementById('content')!;
|
const container = document.getElementById('content')!;
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the background animation durations based on the slider input.
|
|
||||||
* @param {Object} item - The object containing the bksliderinput property.
|
|
||||||
* @param {number} [minDuration=1] - The minimum animation duration in seconds.
|
|
||||||
* @param {number} [maxDuration=10] - The maximum animation duration in seconds.
|
|
||||||
*/
|
|
||||||
export function updateBgDurations() {
|
|
||||||
// Class names to look for
|
|
||||||
const bgClasses = ['bg', 'bg2', 'bg3'];
|
|
||||||
|
|
||||||
// Function to calculate animation duration
|
|
||||||
const calcDuration = (
|
|
||||||
baseValue: number,
|
|
||||||
offset = 0,
|
|
||||||
minBase = 50,
|
|
||||||
maxBase = 150,
|
|
||||||
) => {
|
|
||||||
const scaledValue = 2 + ((maxBase - baseValue) / (maxBase - minBase)) ** 4;
|
|
||||||
return scaledValue + offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Iterate through each class name to update its animation duration
|
|
||||||
bgClasses.forEach((className, index) => {
|
|
||||||
const elements = document.getElementsByClassName(className);
|
|
||||||
|
|
||||||
if (elements.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset = index * 0.05;
|
|
||||||
const duration = calcDuration(parseInt(settingsState.bksliderinput), offset);
|
|
||||||
(elements[0] as HTMLElement).style.animationDuration = `${duration}s`;
|
|
||||||
(elements[0] as HTMLElement).style.animationDelay = `${offset * 5}s`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
export function CreateBackground() {
|
|
||||||
var bkCheck = document.getElementsByClassName("bg")
|
|
||||||
if (bkCheck.length !== 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Creating and inserting 3 divs containing the background applied to the pages
|
|
||||||
var bklocation = document.getElementById("container")
|
|
||||||
var menu = document.getElementById("menu")
|
|
||||||
var bk = document.createElement("div")
|
|
||||||
bk.classList.add("bg")
|
|
||||||
|
|
||||||
bklocation!.insertBefore(bk, menu)
|
|
||||||
|
|
||||||
var bk2 = document.createElement("div")
|
|
||||||
bk2.classList.add("bg")
|
|
||||||
bk2.classList.add("bg2")
|
|
||||||
bklocation!.insertBefore(bk2, menu)
|
|
||||||
|
|
||||||
var bk3 = document.createElement("div")
|
|
||||||
bk3.classList.add("bg")
|
|
||||||
bk3.classList.add("bg3")
|
|
||||||
bklocation!.insertBefore(bk3, menu)
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { settingsState } from "../listeners/SettingsState"
|
|
||||||
import { CreateBackground } from "./CreateBackground"
|
|
||||||
import { RemoveBackground } from "../DisableRemove/RemoveBackground"
|
|
||||||
|
|
||||||
export function enableAnimatedBackground() {
|
|
||||||
if (settingsState.animatedbk) {
|
|
||||||
CreateBackground()
|
|
||||||
} else {
|
|
||||||
RemoveBackground()
|
|
||||||
document.getElementById("container")!.style.background =
|
|
||||||
"var(--background-secondary)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
export function RemoveBackground() {
|
|
||||||
var bk = document.getElementsByClassName("bg")
|
|
||||||
var bk2 = document.getElementsByClassName("bg2")
|
|
||||||
var bk3 = document.getElementsByClassName("bg3")
|
|
||||||
|
|
||||||
if (bk.length == 0 || bk2.length == 0 || bk3.length == 0) return
|
|
||||||
bk[0].remove()
|
|
||||||
bk2[0].remove()
|
|
||||||
bk3[0].remove()
|
|
||||||
}
|
|
||||||
@@ -3,14 +3,11 @@ import { updateAllColors } from '@/seqta/ui/colors/Manager';
|
|||||||
|
|
||||||
|
|
||||||
import { addShortcuts } from "@/seqta/utils/Adders/AddShortcuts";
|
import { addShortcuts } from "@/seqta/utils/Adders/AddShortcuts";
|
||||||
import { CreateBackground } from "@/seqta/utils/CreateEnable/CreateBackground";
|
|
||||||
import { CreateCustomShortcutDiv } from "@/seqta/utils/CreateEnable/CreateCustomShortcutDiv";
|
import { CreateCustomShortcutDiv } from "@/seqta/utils/CreateEnable/CreateCustomShortcutDiv";
|
||||||
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
|
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
|
||||||
import { RemoveBackground } from "@/seqta/utils/DisableRemove/RemoveBackground";
|
|
||||||
import { RemoveShortcutDiv } from "@/seqta/utils/DisableRemove/RemoveShortcutDiv";
|
import { RemoveShortcutDiv } from "@/seqta/utils/DisableRemove/RemoveShortcutDiv";
|
||||||
|
|
||||||
|
|
||||||
import { updateBgDurations } from '@/seqta/ui/Animation';
|
|
||||||
import browser from 'webextension-polyfill';
|
import browser from 'webextension-polyfill';
|
||||||
import type { CustomShortcut } from '@/types/storage';
|
import type { CustomShortcut } from '@/types/storage';
|
||||||
|
|
||||||
@@ -25,8 +22,6 @@ export class StorageChangeHandler {
|
|||||||
settingsState.register('onoff', this.handleOnOffChange.bind(this));
|
settingsState.register('onoff', this.handleOnOffChange.bind(this));
|
||||||
settingsState.register('shortcuts', this.handleShortcutsChange.bind(this));
|
settingsState.register('shortcuts', this.handleShortcutsChange.bind(this));
|
||||||
settingsState.register('customshortcuts', this.handleCustomShortcutsChange.bind(this));
|
settingsState.register('customshortcuts', this.handleCustomShortcutsChange.bind(this));
|
||||||
settingsState.register('bksliderinput', updateBgDurations.bind(this));
|
|
||||||
settingsState.register('animatedbk', this.handleAnimatedBkChange.bind(this));
|
|
||||||
settingsState.register('transparencyEffects', this.handleTransparencyEffectsChange.bind(this));
|
settingsState.register('transparencyEffects', this.handleTransparencyEffectsChange.bind(this));
|
||||||
settingsState.register('subjectfilters', FilterUpcomingAssessments.bind(this));
|
settingsState.register('subjectfilters', FilterUpcomingAssessments.bind(this));
|
||||||
}
|
}
|
||||||
@@ -83,15 +78,6 @@ export class StorageChangeHandler {
|
|||||||
RemoveShortcutDiv(removedShortcuts);
|
RemoveShortcutDiv(removedShortcuts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAnimatedBkChange(newValue: boolean) {
|
|
||||||
if (newValue) {
|
|
||||||
CreateBackground();
|
|
||||||
} else {
|
|
||||||
RemoveBackground();
|
|
||||||
document.getElementById('container')!.style.background = 'var(--background-secondary)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleTransparencyEffectsChange(newValue: boolean) {
|
private handleTransparencyEffectsChange(newValue: boolean) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
document.documentElement.classList.add('transparencyEffects');
|
document.documentElement.classList.add('transparencyEffects');
|
||||||
|
|||||||
Reference in New Issue
Block a user