mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
feat: add docs and dev plugins
This commit is contained in:
@@ -1,33 +1,29 @@
|
||||
import type { Plugin, PluginSettings } from '../../core/types';
|
||||
|
||||
interface NotificationCollectorSettings extends PluginSettings {
|
||||
enabled: {
|
||||
type: 'boolean';
|
||||
default: boolean;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
}
|
||||
import type { Plugin } from '../../core/types';
|
||||
import { BasePlugin, BooleanSetting } from '../../core/settings';
|
||||
|
||||
interface NotificationCollectorStorage {
|
||||
lastNotificationCount: number;
|
||||
lastCheckedTime: string;
|
||||
}
|
||||
|
||||
const notificationCollectorPlugin: Plugin<NotificationCollectorSettings, NotificationCollectorStorage> = {
|
||||
class NotificationCollectorPluginClass extends BasePlugin {
|
||||
@BooleanSetting({
|
||||
default: true,
|
||||
title: "Notification Collector",
|
||||
description: "Uncaps the 9+ limit for notifications, showing the real number.",
|
||||
})
|
||||
enabled!: boolean;
|
||||
}
|
||||
|
||||
// Create an instance to extract settings
|
||||
const settingsInstance = new NotificationCollectorPluginClass();
|
||||
|
||||
const notificationCollectorPlugin: Plugin<typeof settingsInstance.settings, NotificationCollectorStorage> = {
|
||||
id: 'notificationCollector',
|
||||
name: 'Notification Collector',
|
||||
description: 'Collects and displays SEQTA notifications',
|
||||
version: '1.0.0',
|
||||
settings: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
title: 'Notification Collector',
|
||||
description: 'Uncaps the 9+ limit for notifications, showing the real number.',
|
||||
}
|
||||
},
|
||||
|
||||
settings: settingsInstance.settings,
|
||||
run: async (api) => {
|
||||
let pollInterval: number | null = null;
|
||||
|
||||
@@ -95,8 +91,8 @@ const notificationCollectorPlugin: Plugin<NotificationCollectorSettings, Notific
|
||||
});
|
||||
}
|
||||
|
||||
const enabledCallback = (enabled: boolean) => {
|
||||
if (enabled) {
|
||||
const enabledCallback = (value: any) => {
|
||||
if (value) {
|
||||
startPolling();
|
||||
} else {
|
||||
stopPolling();
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { Plugin } from '../../core/types';
|
||||
import { BasePlugin, BooleanSetting } from '../../core/settings';
|
||||
|
||||
class TestPluginClass extends BasePlugin {
|
||||
@BooleanSetting({
|
||||
default: true,
|
||||
title: "Test Plugin",
|
||||
description: "A test plugin for BetterSEQTA+",
|
||||
})
|
||||
enabled!: boolean;
|
||||
}
|
||||
|
||||
const settingsInstance = new TestPluginClass();
|
||||
|
||||
const testPlugin: Plugin<typeof settingsInstance.settings> = {
|
||||
id: 'test',
|
||||
name: 'Test Plugin',
|
||||
description: 'A test plugin for BetterSEQTA+',
|
||||
version: '1.0.0',
|
||||
settings: settingsInstance.settings,
|
||||
|
||||
run: async (api) => {
|
||||
console.log('Test plugin running');
|
||||
|
||||
api.seqta.onPageChange((page) => {
|
||||
console.log('Page changed to', page);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default testPlugin;
|
||||
@@ -1,38 +1,35 @@
|
||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState';
|
||||
import type { Plugin, PluginSettings } from '../../core/types';
|
||||
import type { Plugin } from '../../core/types';
|
||||
import { convertTo12HourFormat } from '@/seqta/utils/convertTo12HourFormat';
|
||||
import { waitForElm } from '@/seqta/utils/waitForElm';
|
||||
import { BasePlugin, BooleanSetting } from '../../core/settings';
|
||||
|
||||
interface TimetableSettings extends PluginSettings {
|
||||
enabled: {
|
||||
type: 'boolean';
|
||||
default: boolean;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
// Define only the typed settings - no need for redundant interface
|
||||
class TimetablePluginClass extends BasePlugin {
|
||||
@BooleanSetting({
|
||||
default: true,
|
||||
title: "Timetable Enhancer",
|
||||
description: "Adds extra features to the timetable view."
|
||||
})
|
||||
enabled!: boolean;
|
||||
}
|
||||
|
||||
const timetablePlugin: Plugin<TimetableSettings> = {
|
||||
// Create an instance to extract settings
|
||||
const settingsInstance = new TimetablePluginClass();
|
||||
|
||||
const timetablePlugin: Plugin<typeof settingsInstance.settings> = {
|
||||
id: 'timetable',
|
||||
name: 'Timetable Enhancer',
|
||||
description: 'Adds extra features to the timetable view',
|
||||
version: '1.0.0',
|
||||
settings: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
title: 'Timetable Enhancer',
|
||||
description: 'Adds extra features to the timetable view.',
|
||||
}
|
||||
},
|
||||
|
||||
settings: settingsInstance.settings,
|
||||
run: async (api) => {
|
||||
if (api.settings.enabled) {
|
||||
api.seqta.onMount('.timetablepage', handleTimetable)
|
||||
}
|
||||
|
||||
const enabledCallback = (enabled: boolean) => {
|
||||
if (enabled) {
|
||||
const enabledCallback = (value: any) => {
|
||||
if (value) {
|
||||
api.seqta.onMount('.timetablepage', handleTimetable)
|
||||
} else {
|
||||
const timetablePage = document.querySelector('.timetablepage')
|
||||
@@ -277,19 +274,16 @@ function handleTimetableAssessmentHide(): void {
|
||||
|
||||
function hideElements(): void {
|
||||
const entries = document.querySelectorAll(".entry")
|
||||
|
||||
entries.forEach((entry: Element) => {
|
||||
const entryEl = entry as HTMLElement
|
||||
if (!entryEl.classList.contains("assessment") && !(entryEl.style.opacity === "0.3")) {
|
||||
entryEl.style.opacity = "0.3"
|
||||
} else {
|
||||
entryEl.style.opacity = "1"
|
||||
if (!entryEl.classList.contains("assessment")) {
|
||||
entryEl.style.opacity = entryEl.style.opacity === "0.3" ? "1" : "0.3"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
hideOn.addEventListener("click", () => {
|
||||
hideElements()
|
||||
})
|
||||
hideOn.addEventListener("click", hideElements)
|
||||
}
|
||||
|
||||
export default timetablePlugin;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import type { PluginSettings } from './types';
|
||||
|
||||
// Base interfaces for our settings
|
||||
interface BaseSettingOptions {
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface BooleanSettingOptions extends BaseSettingOptions {
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
interface StringSettingOptions extends BaseSettingOptions {
|
||||
default: string;
|
||||
maxLength?: number;
|
||||
pattern?: string;
|
||||
}
|
||||
|
||||
interface NumberSettingOptions extends BaseSettingOptions {
|
||||
default: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
interface SelectSettingOptions<T extends string> extends BaseSettingOptions {
|
||||
default: T;
|
||||
options: readonly T[];
|
||||
}
|
||||
|
||||
// The actual decorators
|
||||
export function BooleanSetting(options: BooleanSettingOptions): 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
|
||||
proto.settings[propertyKey] = {
|
||||
type: 'boolean',
|
||||
...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
|
||||
proto.settings[propertyKey] = {
|
||||
type: 'string',
|
||||
...options
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function NumberSetting(options: NumberSettingOptions): 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
|
||||
proto.settings[propertyKey] = {
|
||||
type: 'number',
|
||||
...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
|
||||
proto.settings[propertyKey] = {
|
||||
type: 'select',
|
||||
...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 = {} as T;
|
||||
|
||||
constructor() {
|
||||
// Copy settings from the prototype to the instance
|
||||
// This ensures that each instance has its own settings object
|
||||
if (this.constructor.prototype.settings) {
|
||||
this.settings = { ...this.constructor.prototype.settings };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,32 @@
|
||||
import ReactFiber from '@/seqta/utils/ReactFiber';
|
||||
|
||||
interface BooleanSetting {
|
||||
export interface BooleanSetting {
|
||||
type: 'boolean';
|
||||
default: boolean;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface StringSetting {
|
||||
export interface StringSetting {
|
||||
type: 'string';
|
||||
default: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
maxLength?: number;
|
||||
pattern?: string;
|
||||
}
|
||||
|
||||
interface NumberSetting {
|
||||
export interface NumberSetting {
|
||||
type: 'number';
|
||||
default: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
interface SelectSetting<T extends string> {
|
||||
export interface SelectSetting<T extends string> {
|
||||
type: 'select';
|
||||
options: readonly T[];
|
||||
default: T;
|
||||
@@ -29,7 +34,7 @@ interface SelectSetting<T extends string> {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
type PluginSetting = BooleanSetting | StringSetting | NumberSetting | SelectSetting<string>;
|
||||
export type PluginSetting = BooleanSetting | StringSetting | NumberSetting | SelectSetting<string>;
|
||||
|
||||
export type PluginSettings = {
|
||||
[key: string]: PluginSetting;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { PluginManager } from './core/manager';
|
||||
|
||||
// plugins
|
||||
import timetablePlugin from './built-in/timetable';
|
||||
import notificationCollectorPlugin from './built-in/notificationCollector';
|
||||
import testPlugin from './built-in/test';
|
||||
|
||||
// Initialize plugin manager
|
||||
const pluginManager = PluginManager.getInstance();
|
||||
@@ -8,6 +11,7 @@ const pluginManager = PluginManager.getInstance();
|
||||
// Register built-in plugins
|
||||
pluginManager.registerPlugin(timetablePlugin);
|
||||
pluginManager.registerPlugin(notificationCollectorPlugin);
|
||||
pluginManager.registerPlugin(testPlugin);
|
||||
|
||||
// Legacy plugin exports
|
||||
export { init as Monofile } from './monofile';
|
||||
|
||||
Reference in New Issue
Block a user