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:
@@ -0,0 +1,335 @@
|
||||
# Creating Plugins with Decorator-Based Settings
|
||||
|
||||
This guide will walk you through creating a BetterSEQTA+ plugin using the new decorator-based settings system.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Understand basic TypeScript concepts (classes, interfaces, decorators)
|
||||
- Familiarity with the BetterSEQTA+ plugin system
|
||||
|
||||
## Plugin Structure
|
||||
|
||||
A typical plugin consists of:
|
||||
|
||||
1. A settings class that defines the plugin's settings using decorators
|
||||
2. The plugin definition object
|
||||
3. The actual plugin functionality
|
||||
|
||||
## Step by Step Guide
|
||||
|
||||
### 1. Create a Plugin File
|
||||
|
||||
Start by creating a new file in the `src/plugins/built-in` directory. For example, `myFeature/index.ts`.
|
||||
|
||||
### 2. Define Storage Type (Optional)
|
||||
|
||||
If your plugin needs to store data, define a storage interface:
|
||||
|
||||
```typescript
|
||||
interface MyFeatureStorage {
|
||||
lastUsed: string;
|
||||
favoriteItems: string[];
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Create a Settings Class
|
||||
|
||||
Create a class that extends `BasePlugin` and use decorators to define settings:
|
||||
|
||||
```typescript
|
||||
import { BasePlugin, BooleanSetting, StringSetting, NumberSetting, SelectSetting } from '../../core/settings';
|
||||
|
||||
class MyFeaturePluginClass extends BasePlugin {
|
||||
@BooleanSetting({
|
||||
default: true,
|
||||
title: "Enable My Feature",
|
||||
description: "Enables the awesome new feature."
|
||||
})
|
||||
enabled!: boolean;
|
||||
|
||||
@StringSetting({
|
||||
default: "Default text",
|
||||
title: "Custom Message",
|
||||
description: "Sets a custom message for the feature",
|
||||
maxLength: 100
|
||||
})
|
||||
message!: string;
|
||||
|
||||
@NumberSetting({
|
||||
default: 5,
|
||||
title: "Refresh Interval",
|
||||
description: "How often to refresh the data (in seconds)",
|
||||
min: 1,
|
||||
max: 60,
|
||||
step: 1
|
||||
})
|
||||
refreshInterval!: number;
|
||||
|
||||
@SelectSetting({
|
||||
default: "small",
|
||||
options: ["small", "medium", "large"] as const,
|
||||
title: "Display Size",
|
||||
description: "Control how large the feature appears"
|
||||
})
|
||||
displaySize!: "small" | "medium" | "large";
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Create a Plugin Instance
|
||||
|
||||
Create an instance of your settings class and define the plugin object:
|
||||
|
||||
```typescript
|
||||
// Create an instance to extract settings
|
||||
const settingsInstance = new MyFeaturePluginClass();
|
||||
|
||||
const myFeaturePlugin: Plugin<typeof settingsInstance.settings, MyFeatureStorage> = {
|
||||
id: 'myFeature',
|
||||
name: 'My Awesome Feature',
|
||||
description: 'Adds an awesome new feature to SEQTA',
|
||||
version: '1.0.0',
|
||||
settings: settingsInstance.settings,
|
||||
run: async (api) => {
|
||||
// Plugin implementation goes here
|
||||
}
|
||||
};
|
||||
|
||||
export default myFeaturePlugin;
|
||||
```
|
||||
|
||||
### 5. Implement Plugin Functionality
|
||||
|
||||
Implement your plugin's functionality in the `run` function:
|
||||
|
||||
```typescript
|
||||
run: async (api) => {
|
||||
// Initialize storage with defaults if needed
|
||||
if (api.storage.lastUsed === undefined) {
|
||||
api.storage.lastUsed = new Date().toISOString();
|
||||
}
|
||||
|
||||
if (api.storage.favoriteItems === undefined) {
|
||||
api.storage.favoriteItems = [];
|
||||
}
|
||||
|
||||
// Only run if enabled
|
||||
if (!api.settings.enabled) return;
|
||||
|
||||
// Main plugin logic
|
||||
const initializeFeature = () => {
|
||||
console.log(`Initializing feature with message: ${api.settings.message}`);
|
||||
console.log(`Using display size: ${api.settings.displaySize}`);
|
||||
|
||||
// Set up refreshing
|
||||
const intervalId = setInterval(() => {
|
||||
refreshData();
|
||||
}, api.settings.refreshInterval * 1000);
|
||||
|
||||
// Clean up function returned here
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
console.log('Feature cleaned up');
|
||||
};
|
||||
};
|
||||
|
||||
const refreshData = () => {
|
||||
console.log('Refreshing data...');
|
||||
api.storage.lastUsed = new Date().toISOString();
|
||||
};
|
||||
|
||||
// Listen for elements we need
|
||||
api.seqta.onMount('.some-element', (element) => {
|
||||
// Do something when element appears
|
||||
});
|
||||
|
||||
// Listen for settings changes
|
||||
api.settings.onChange('refreshInterval', (newValue) => {
|
||||
console.log(`Refresh interval changed to ${newValue} seconds`);
|
||||
});
|
||||
|
||||
// Return cleanup function
|
||||
return initializeFeature();
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Register the Plugin
|
||||
|
||||
Make sure your plugin is registered in the plugin system. In the `src/plugins/index.ts` file, add your plugin to the list of built-in plugins:
|
||||
|
||||
```typescript
|
||||
import myFeaturePlugin from './built-in/myFeature';
|
||||
|
||||
// Add your plugin to this array
|
||||
const builtInPlugins = [
|
||||
// ... other plugins
|
||||
myFeaturePlugin,
|
||||
];
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Reacting to Settings Changes
|
||||
|
||||
You can listen for settings changes with the `onChange` method:
|
||||
|
||||
```typescript
|
||||
api.settings.onChange('enabled', (value) => {
|
||||
if (value) {
|
||||
// Setting was turned on
|
||||
initialize();
|
||||
} else {
|
||||
// Setting was turned off
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Using Storage
|
||||
|
||||
The storage API lets you persist data between sessions:
|
||||
|
||||
```typescript
|
||||
// Read from storage
|
||||
const favorites = api.storage.favoriteItems;
|
||||
|
||||
// Write to storage
|
||||
api.storage.favoriteItems = [...favorites, 'new item'];
|
||||
|
||||
// Listen for storage changes
|
||||
api.storage.onChange('favoriteItems', (newValue) => {
|
||||
console.log('Favorites updated:', newValue);
|
||||
});
|
||||
```
|
||||
|
||||
### Cleaning Up
|
||||
|
||||
Always return a cleanup function from your plugin's `run` method if you have any resources to clean up:
|
||||
|
||||
```typescript
|
||||
run: async (api) => {
|
||||
// Set up resources
|
||||
const intervalId = setInterval(() => {
|
||||
// Do something
|
||||
}, 1000);
|
||||
|
||||
// Return cleanup function
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
// Clean up any other resources
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Initialize Storage Values**: Always check if storage values are undefined and set defaults
|
||||
2. **Handle Enabled State**: Check if your plugin is enabled before running main functionality
|
||||
3. **Use TypeScript**: Take advantage of TypeScript's type system to ensure type safety
|
||||
4. **Clean Up Resources**: Always clean up resources when a plugin is disabled
|
||||
5. **Document Settings**: Use clear titles and descriptions for your settings
|
||||
|
||||
## Complete Example
|
||||
|
||||
Here's a complete example of a simple plugin that changes the color of elements:
|
||||
|
||||
```typescript
|
||||
import { BasePlugin, BooleanSetting, ColorSetting } from '../../core/settings';
|
||||
import type { Plugin } from '../../core/types';
|
||||
|
||||
interface ColorChangerStorage {
|
||||
lastApplied: string;
|
||||
}
|
||||
|
||||
class ColorChangerPluginClass extends BasePlugin {
|
||||
@BooleanSetting({
|
||||
default: true,
|
||||
title: "Enable Color Changer",
|
||||
description: "Applies custom colors to elements on the page."
|
||||
})
|
||||
enabled!: boolean;
|
||||
|
||||
@ColorSetting({
|
||||
default: "#4285f4",
|
||||
title: "Heading Color",
|
||||
description: "Color for headings on the page",
|
||||
presets: ["#4285f4", "#ea4335", "#fbbc05", "#34a853"]
|
||||
})
|
||||
headingColor!: string;
|
||||
|
||||
@ColorSetting({
|
||||
default: "#34a853",
|
||||
title: "Button Color",
|
||||
description: "Color for buttons on the page",
|
||||
presets: ["#4285f4", "#ea4335", "#fbbc05", "#34a853"]
|
||||
})
|
||||
buttonColor!: string;
|
||||
}
|
||||
|
||||
const settingsInstance = new ColorChangerPluginClass();
|
||||
|
||||
const colorChangerPlugin: Plugin<typeof settingsInstance.settings, ColorChangerStorage> = {
|
||||
id: 'colorChanger',
|
||||
name: 'Color Changer',
|
||||
description: 'Changes colors of various elements on the page',
|
||||
version: '1.0.0',
|
||||
settings: settingsInstance.settings,
|
||||
run: async (api) => {
|
||||
if (api.storage.lastApplied === undefined) {
|
||||
api.storage.lastApplied = new Date().toISOString();
|
||||
}
|
||||
|
||||
const applyColors = () => {
|
||||
if (!api.settings.enabled) return;
|
||||
|
||||
// Apply heading color
|
||||
document.querySelectorAll('h1, h2, h3').forEach(heading => {
|
||||
(heading as HTMLElement).style.color = api.settings.headingColor;
|
||||
});
|
||||
|
||||
// Apply button color
|
||||
document.querySelectorAll('button').forEach(button => {
|
||||
(button as HTMLElement).style.backgroundColor = api.settings.buttonColor;
|
||||
});
|
||||
|
||||
api.storage.lastApplied = new Date().toISOString();
|
||||
};
|
||||
|
||||
// Apply colors initially
|
||||
applyColors();
|
||||
|
||||
// Apply colors when DOM changes
|
||||
api.seqta.onMount('h1, h2, h3, button', applyColors);
|
||||
|
||||
// Listen for color changes
|
||||
api.settings.onChange('headingColor', applyColors);
|
||||
api.settings.onChange('buttonColor', applyColors);
|
||||
api.settings.onChange('enabled', (enabled) => {
|
||||
if (enabled) {
|
||||
applyColors();
|
||||
} else {
|
||||
// Reset colors
|
||||
document.querySelectorAll('h1, h2, h3').forEach(heading => {
|
||||
(heading as HTMLElement).style.color = '';
|
||||
});
|
||||
|
||||
document.querySelectorAll('button').forEach(button => {
|
||||
(button as HTMLElement).style.backgroundColor = '';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// No cleanup needed for this plugin
|
||||
return () => {};
|
||||
}
|
||||
};
|
||||
|
||||
export default colorChangerPlugin;
|
||||
```
|
||||
|
||||
This plugin demonstrates:
|
||||
- Using multiple setting types including a custom color setting
|
||||
- Handling the enabled state
|
||||
- Initializing storage
|
||||
- Listening for setting changes
|
||||
- Applying and resetting styles based on settings
|
||||
- Proper cleanup when disabled
|
||||
Reference in New Issue
Block a user