mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat: add docs and dev plugins
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# BetterSEQTA+ Plugin System
|
||||
|
||||
BetterSEQTA+ features a powerful plugin system that allows developers to extend and customize the functionality of SEQTA Learn. This document provides an overview of how the plugin system works and how to get started with creating your own plugins.
|
||||
|
||||
## What is a Plugin?
|
||||
|
||||
A plugin is a self-contained piece of code that adds functionality to BetterSEQTA+. Plugins can:
|
||||
|
||||
- Add new UI elements to SEQTA Learn
|
||||
- Modify existing UI elements
|
||||
- Add new features to SEQTA Learn
|
||||
- Modify or extend existing features
|
||||
- Store and retrieve user data
|
||||
- Respond to events in SEQTA Learn
|
||||
|
||||
Each plugin is isolated from other plugins, with its own settings, storage, and lifecycle. This ensures that plugins can be enabled, disabled, or removed without affecting other parts of the system.
|
||||
|
||||
## Plugin Architecture
|
||||
|
||||
The BetterSEQTA+ plugin system consists of several key components:
|
||||
|
||||
### 1. Plugin Interface
|
||||
|
||||
All plugins implement the `Plugin` interface, which defines the structure and lifecycle methods of a plugin:
|
||||
|
||||
```typescript
|
||||
export interface Plugin<T extends PluginSettings = PluginSettings, S = any> {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
settings: T;
|
||||
run: (api: PluginAPI<T, S>) => void | Promise<void> | (() => void) | Promise<(() => void)>;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Plugin API
|
||||
|
||||
When a plugin is run, it receives an instance of the `PluginAPI`, which provides access to various services and utilities:
|
||||
|
||||
```typescript
|
||||
export interface PluginAPI<T extends PluginSettings, S = any> {
|
||||
seqta: SEQTAAPI;
|
||||
settings: SettingsAPI<T>;
|
||||
storage: TypedStorageAPI<S>;
|
||||
events: EventsAPI;
|
||||
}
|
||||
```
|
||||
|
||||
- **SEQTA API**: Provides methods for interacting with the SEQTA Learn UI
|
||||
- **Settings API**: Provides type-safe access to plugin settings
|
||||
- **Storage API**: Provides type-safe persistent storage for plugin data
|
||||
- **Events API**: Allows plugins to emit and listen for events
|
||||
|
||||
### 3. Plugin Manager
|
||||
|
||||
The Plugin Manager is responsible for loading, starting, stopping, and managing plugins. It handles the lifecycle of each plugin and ensures that plugins have access to the resources they need.
|
||||
|
||||
### 4. Plugin Registry
|
||||
|
||||
The Plugin Registry is a central repository of all available plugins. Built-in plugins are automatically registered, and additional plugins can be registered dynamically.
|
||||
|
||||
## Plugin Lifecycle
|
||||
|
||||
Plugins follow a simple lifecycle:
|
||||
|
||||
1. **Registration**: The plugin is registered with the Plugin Manager
|
||||
2. **Loading**: The plugin's settings and storage are loaded
|
||||
3. **Running**: The plugin's `run` method is called with the Plugin API
|
||||
4. **Cleanup**: If the plugin returns a cleanup function, it is called when the plugin is stopped
|
||||
|
||||
## Creating a Plugin
|
||||
|
||||
Creating a plugin for BetterSEQTA+ involves a few simple steps:
|
||||
|
||||
1. Define your plugin's interface
|
||||
2. Implement the Plugin interface
|
||||
3. Register your plugin with the Plugin Manager
|
||||
|
||||
For a detailed guide on creating plugins, see [Creating Your First Plugin](./creating-plugins.md).
|
||||
|
||||
## Built-in Plugins
|
||||
|
||||
BetterSEQTA+ comes with several built-in plugins that provide core functionality:
|
||||
|
||||
- **Timetable**: Enhances the SEQTA timetable view
|
||||
- **Notification Collector**: Improves the notification system
|
||||
- **Theme Customizer**: Allows customization of the SEQTA theme
|
||||
- **Assessment Enhancer**: Adds features to the assessment view
|
||||
|
||||
These plugins serve as good examples of how to use the plugin system effectively.
|
||||
|
||||
## Type-Safe Settings and Storage
|
||||
|
||||
One of the key features of the BetterSEQTA+ plugin system is its type-safe settings and storage. Using TypeScript generics, plugins can define the structure of their settings and storage, ensuring that they are used correctly throughout the codebase.
|
||||
|
||||
### Settings Example
|
||||
|
||||
```typescript
|
||||
interface MyPluginSettings extends PluginSettings {
|
||||
enabled: {
|
||||
type: 'boolean';
|
||||
default: boolean;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
refreshInterval: {
|
||||
type: 'number';
|
||||
default: number;
|
||||
title: string;
|
||||
description: string;
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Storage Example
|
||||
|
||||
```typescript
|
||||
interface MyPluginStorage {
|
||||
lastRefresh: string;
|
||||
savedItems: string[];
|
||||
userPreferences: {
|
||||
theme: 'light' | 'dark';
|
||||
fontSize: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Decorator-Based Settings
|
||||
|
||||
BetterSEQTA+ also offers a more modern, decorator-based approach to defining settings. For more information, see [Creating Plugins with Settings](../settings/creating-plugins.md).
|
||||
|
||||
## Plugin API Reference
|
||||
|
||||
The Plugin API provides a rich set of features for interacting with SEQTA Learn. For a complete reference, see [Plugin API Reference](../advanced/plugin-api.md).
|
||||
|
||||
## Best Practices
|
||||
|
||||
When creating plugins for BetterSEQTA+, consider these best practices:
|
||||
|
||||
1. **Use TypeScript**: Take advantage of TypeScript's type system to ensure type safety in your plugins.
|
||||
2. **Keep Plugins Focused**: Each plugin should do one thing well.
|
||||
3. **Handle Cleanup**: Always return a cleanup function from your plugin's `run` method to ensure proper resource management.
|
||||
4. **Document Your Code**: Add clear documentation to your code, especially for public APIs.
|
||||
5. **Test Thoroughly**: Test your plugins in different environments and with different configurations.
|
||||
6. **Follow UI Guidelines**: When adding UI elements, follow the SEQTA Learn UI guidelines to maintain a consistent experience.
|
||||
7. **Optimize Performance**: Be mindful of performance impact, especially for plugins that run on every page.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating Your First Plugin](./creating-plugins.md)
|
||||
- [Plugin API Reference](../advanced/plugin-api.md)
|
||||
- [Typed Storage API](../advanced/storage-api.md)
|
||||
@@ -0,0 +1,269 @@
|
||||
# Creating Your First Plugin
|
||||
|
||||
This guide will walk you through the process of creating a plugin for BetterSEQTA+, from setup to implementation to testing.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you start creating a plugin, make sure you have:
|
||||
|
||||
- Basic knowledge of TypeScript
|
||||
- Familiarity with the BetterSEQTA+ codebase
|
||||
- A development environment set up according to the [Installation Guide](../installation.md)
|
||||
|
||||
## Plugin Structure
|
||||
|
||||
A typical BetterSEQTA+ plugin consists of:
|
||||
|
||||
1. **Plugin Definition**: A TypeScript file that defines the plugin's metadata and functionality
|
||||
2. **Settings Interface**: (Optional) A TypeScript interface that defines the plugin's settings
|
||||
3. **Storage Interface**: (Optional) A TypeScript interface that defines the plugin's storage structure
|
||||
|
||||
## Step 1: Planning Your Plugin
|
||||
|
||||
Before you start coding, take some time to plan your plugin:
|
||||
|
||||
1. **Identify the Problem**: What issue or need does your plugin address?
|
||||
2. **Define the Scope**: What specific features will your plugin include?
|
||||
3. **Consider the User Experience**: How will users interact with your plugin?
|
||||
|
||||
## Step 2: Creating the Plugin File
|
||||
|
||||
Create a new TypeScript file for your plugin. The convention is to place it in the `src/plugins/` directory, either in the `built-in` folder or a new folder if it's a third-party plugin.
|
||||
|
||||
```typescript
|
||||
// src/plugins/my-plugin/index.ts
|
||||
|
||||
import { Plugin, PluginAPI, PluginSettings } from '../../core/types';
|
||||
|
||||
export interface MyPluginSettings extends PluginSettings {
|
||||
enabled: {
|
||||
type: 'boolean';
|
||||
default: true;
|
||||
title: 'Enable My Plugin';
|
||||
description: 'Turn my plugin on or off';
|
||||
};
|
||||
// Add more settings as needed
|
||||
}
|
||||
|
||||
export interface MyPluginStorage {
|
||||
lastRun: string;
|
||||
// Add more storage fields as needed
|
||||
}
|
||||
|
||||
const myPlugin: Plugin<MyPluginSettings, MyPluginStorage> = {
|
||||
id: 'my-plugin',
|
||||
name: 'My Plugin',
|
||||
description: 'A simple plugin for BetterSEQTA+',
|
||||
version: '1.0.0',
|
||||
settings: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
title: 'Enable My Plugin',
|
||||
description: 'Turn my plugin on or off',
|
||||
},
|
||||
// Initialize your settings here
|
||||
},
|
||||
run: (api) => {
|
||||
if (!api.settings.get('enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize storage with default values if needed
|
||||
if (api.storage.get('lastRun') === undefined) {
|
||||
api.storage.set('lastRun', new Date().toISOString());
|
||||
}
|
||||
|
||||
// Your plugin logic goes here
|
||||
console.log('My Plugin is running!');
|
||||
|
||||
// Access the SEQTA API
|
||||
api.seqta.onPageLoad('/timetable', () => {
|
||||
// Code to run when the timetable page loads
|
||||
});
|
||||
|
||||
// Return a cleanup function (optional but recommended)
|
||||
return () => {
|
||||
console.log('My Plugin is cleaning up!');
|
||||
// Cleanup logic goes here
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default myPlugin;
|
||||
```
|
||||
|
||||
## Step 3: Registering Your Plugin
|
||||
|
||||
To make your plugin available to BetterSEQTA+, you need to register it with the Plugin Manager. For built-in plugins, you can add your plugin to the `src/plugins/built-in/index.ts` file:
|
||||
|
||||
```typescript
|
||||
// src/plugins/built-in/index.ts
|
||||
|
||||
import myPlugin from './my-plugin';
|
||||
// Other imports...
|
||||
|
||||
export const builtInPlugins = [
|
||||
myPlugin,
|
||||
// Other plugins...
|
||||
];
|
||||
```
|
||||
|
||||
For third-party plugins, you'll need to follow a different approach, as detailed in [Third-Party Plugins](../advanced/third-party-plugins.md).
|
||||
|
||||
## Step 4: Implementing Your Plugin Logic
|
||||
|
||||
The main functionality of your plugin goes in the `run` method. Here are some common patterns:
|
||||
|
||||
### Responding to Page Loads
|
||||
|
||||
```typescript
|
||||
api.seqta.onPageLoad('/timetable', () => {
|
||||
// Code to run when the timetable page loads
|
||||
});
|
||||
```
|
||||
|
||||
### Modifying the UI
|
||||
|
||||
```typescript
|
||||
api.seqta.onPageLoad('/timetable', () => {
|
||||
const timetableElement = document.querySelector('.timetable');
|
||||
if (timetableElement) {
|
||||
// Modify the timetable element
|
||||
const controlsDiv = document.createElement('div');
|
||||
controlsDiv.className = 'my-plugin-controls';
|
||||
controlsDiv.innerHTML = '<button>Zoom In</button><button>Zoom Out</button>';
|
||||
timetableElement.appendChild(controlsDiv);
|
||||
|
||||
// Add event listeners
|
||||
controlsDiv.querySelector('button:first-child').addEventListener('click', () => {
|
||||
// Zoom in logic
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Working with Settings
|
||||
|
||||
```typescript
|
||||
// Get a setting value
|
||||
const isEnabled = api.settings.get('enabled');
|
||||
|
||||
// Listen for settings changes
|
||||
api.settings.onChange('enabled', (newValue) => {
|
||||
if (newValue) {
|
||||
// Enable functionality
|
||||
} else {
|
||||
// Disable functionality
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Working with Storage
|
||||
|
||||
```typescript
|
||||
// Get a stored value
|
||||
const lastRun = api.storage.get('lastRun');
|
||||
|
||||
// Set a stored value
|
||||
api.storage.set('lastRun', new Date().toISOString());
|
||||
|
||||
// Listen for storage changes
|
||||
api.storage.onChange('lastRun', (newValue) => {
|
||||
console.log(`Last run updated to: ${newValue}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Working with Events
|
||||
|
||||
```typescript
|
||||
// Listen for events
|
||||
api.events.on('assessmentLoaded', (data) => {
|
||||
console.log(`Assessment loaded: ${data.id}`);
|
||||
});
|
||||
|
||||
// Emit an event
|
||||
api.events.emit('myPluginEvent', { message: 'Hello from My Plugin!' });
|
||||
```
|
||||
|
||||
## Step 5: Testing Your Plugin
|
||||
|
||||
To test your plugin:
|
||||
|
||||
1. Run the development server:
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. Open SEQTA Learn in your browser with BetterSEQTA+ enabled.
|
||||
|
||||
3. Check the console for any error messages.
|
||||
|
||||
4. Verify that your plugin works as expected.
|
||||
|
||||
## Step 6: Adding Plugin Settings UI
|
||||
|
||||
If your plugin has settings, they will automatically appear in the BetterSEQTA+ settings panel. The UI is generated based on the settings interface you defined.
|
||||
|
||||
For more control over the settings UI, you can use the decorator-based settings system. See [Creating Plugins with Settings](../settings/creating-plugins.md) for more information.
|
||||
|
||||
## Best Practices for Plugin Development
|
||||
|
||||
1. **Follow TypeScript Best Practices**: Use proper typing for all variables and functions.
|
||||
|
||||
2. **Handle Errors Gracefully**: Wrap your code in try-catch blocks to prevent crashes.
|
||||
```typescript
|
||||
try {
|
||||
// Your code
|
||||
} catch (error) {
|
||||
console.error('My Plugin Error:', error);
|
||||
}
|
||||
```
|
||||
|
||||
3. **Clean Up After Yourself**: Always return a cleanup function from your plugin's `run` method.
|
||||
```typescript
|
||||
const cleanup = () => {
|
||||
// Remove event listeners, DOM elements, etc.
|
||||
};
|
||||
return cleanup;
|
||||
```
|
||||
|
||||
4. **Document Your Code**: Add comments to explain complex logic or unusual patterns.
|
||||
|
||||
5. **Keep It Simple**: Start with a simple plugin and add features incrementally.
|
||||
|
||||
## Example Plugins
|
||||
|
||||
For inspiration, check out these example plugins in the BetterSEQTA+ codebase:
|
||||
|
||||
1. **Timetable Plugin**: Enhances the SEQTA timetable view with zoom controls and filtering options.
|
||||
- Location: `src/plugins/built-in/timetable/index.ts`
|
||||
|
||||
2. **Notification Collector**: Improves the notification system in SEQTA Learn.
|
||||
- Location: `src/plugins/built-in/notification-collector/index.ts`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Plugin Not Loading
|
||||
|
||||
- Check that your plugin is properly registered
|
||||
- Verify that there are no TypeScript errors
|
||||
- Look for error messages in the console
|
||||
|
||||
### Plugin Not Working as Expected
|
||||
|
||||
- Ensure that your plugin's `enabled` setting is true
|
||||
- Check that your selectors match the SEQTA DOM structure
|
||||
- Use `console.log` statements to debug your code
|
||||
|
||||
### TypeScript Errors
|
||||
|
||||
- Make sure your interfaces are properly defined
|
||||
- Check that you're using the correct types for the plugin API
|
||||
- Verify that your plugin implements the `Plugin` interface correctly
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Learn About Type-Safe Settings](../settings/creating-plugins.md)
|
||||
- [Explore the Plugin API](../advanced/plugin-api.md)
|
||||
- [Contribute to BetterSEQTA+](../contributing.md)
|
||||
Reference in New Issue
Block a user