Creating Plugins for BetterSEQTA+¶
Hey there! 👋 So you want to create a plugin for BetterSEQTA+? That's awesome! This guide will walk you through everything you need to know, from the very basics to more advanced features. Don't worry if you're new to this - we'll explain everything step by step.
What is a Plugin?¶
In BetterSEQTA+, a plugin is like a mini-app that adds new features to SEQTA. Think of it as a piece of LEGO that you can snap onto SEQTA to make it do new things. For example, you could create a plugin that:
- Changes how SEQTA looks
- Adds new buttons or features
- Shows extra information on your timetable
- Collects notifications in a better way
- Really, anything you can imagine!
Your First Plugin¶
Let's create a super simple plugin together. We'll make one that adds a friendly message to the SEQTA homepage. Here's what we'll need:
import type { Plugin } from "@/plugins/core/types";
const myFirstPlugin: Plugin = {
// Every plugin needs these basic details
id: "my-first-plugin",
name: "My First Plugin",
description: "Adds a friendly message to SEQTA",
version: "1.0.0",
// This tells BetterSEQTA+ that users can turn our plugin on/off
disableToggle: true,
// Optional: Mark your plugin as beta to show a "Beta" tag in settings
beta: true,
// This is where the magic happens!
run: async (api) => {
// Wait for the homepage to load
api.seqta.onMount(".home-page", (homePage) => {
// Create our message
const message = document.createElement("div");
message.textContent = "Hello from my first plugin! 🎉";
message.style.padding = "20px";
message.style.backgroundColor = "#e9f5ff";
message.style.borderRadius = "8px";
message.style.margin = "20px";
// Add it to the page
homePage.prepend(message);
});
// Return a cleanup function that removes our message when the plugin is disabled
return () => {
const message = document.querySelector(".home-page > div");
message?.remove();
};
},
};
export default myFirstPlugin;
Let's break down what's happening here:
- First, we import the
Plugintype that tells TypeScript what a plugin should look like - We create our plugin object with some basic information:
id: A unique name for your plugin (use lowercase and dashes)name: A friendly name that users will seedescription: Explain what your plugin doesversion: Your plugin's version number- We set
disableToggle: trueso users can turn our plugin on/off in settings - We set
beta: trueto mark the plugin as beta - The
runfunction is where we put our plugin's code - We use
api.seqta.onMountto wait for the homepage to load - We create and style a message element
- We return a cleanup function that removes our changes when the plugin is disabled
The Plugin API¶
When your plugin runs, it gets access to a powerful API that lets you do all sorts of things. Let's look at what you can do:
SEQTA API (api.seqta)¶
This helps you interact with SEQTA's pages:
// Wait for an element to appear on the page
api.seqta.onMount(".some-class", (element) => {
// Do something with the element
});
// Know when the user changes pages
api.seqta.onPageChange((page) => {
console.log("User went to:", page);
});
// Get the current page
const currentPage = api.seqta.getCurrentPage();
Settings API (api.settings)¶
Want to let users customize your plugin? Use settings!
import { BasePlugin } from "@/plugins/core/settings";
import {
booleanSetting,
defineSettings,
Setting,
} from "@/plugins/core/settingsHelpers";
// Define your settings
const settings = defineSettings({
showMessage: booleanSetting({
default: true,
title: "Show Welcome Message",
description: "Show a friendly message on the homepage",
}),
});
// Create a class for your plugin
class MyPluginClass extends BasePlugin<typeof settings> {
@Setting(settings.showMessage)
showMessage!: boolean;
}
// Create your plugin
const settingsInstance = new MyPluginClass();
const myPlugin: Plugin<typeof settings> = {
// ... other plugin details ...
settings: settingsInstance.settings,
run: async (api) => {
// Use the setting
if (api.settings.showMessage) {
// Show the message
}
// Listen for setting changes
api.settings.onChange("showMessage", (newValue) => {
if (newValue) {
// Show the message
} else {
// Hide the message
}
});
},
};
Storage API (api.storage)¶
Need to save some data? The storage API has got you covered:
// Save some data
await api.storage.set("lastVisit", new Date().toISOString());
// Get it back later
const lastVisit = await api.storage.get("lastVisit");
// Listen for changes
api.storage.onChange("lastVisit", (newValue) => {
console.log("Last visit updated:", newValue);
});
Events API (api.events)¶
Want your plugin to be able to interface with other plugins? Then use events!
// Listen for an event
api.events.on("myCustomEvent", (data) => {
console.log("Got event:", data);
});
// Send an event
api.events.emit("myCustomEvent", { some: "data" });
Adding Styles¶
Want to make your plugin look pretty? You can add CSS styles:
const myPlugin: Plugin = {
// ... other plugin details ...
// Add your CSS here
styles: `
.my-plugin-message {
background: linear-gradient(135deg, #6e8efb, #a777e3);
color: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin: 20px;
animation: slide-in 0.3s ease-out;
}
@keyframes slide-in {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
`,
run: async (api) => {
// Your plugin code here
},
};
Best Practices¶
Here are some tips to make your plugin awesome:
- Always Clean Up: When your plugin is disabled, clean up any changes you made:
run: async (api) => {
// Add stuff to the page
const element = document.createElement("div");
document.body.appendChild(element);
// Return a cleanup function
return () => {
element.remove();
};
};
-
Use TypeScript: It helps catch errors before they happen and makes your code easier to understand.
-
Test Your Plugin: Make sure it works in different situations:
-
When SEQTA is loading
- When the user switches pages
- When the plugin is enabled/disabled
-
When settings are changed
-
Keep It Fast: Don't slow down SEQTA:
-
Use
onMountinstead of intervals or timeouts - Clean up event listeners when they're not needed
-
Don't do heavy calculations on the main thread
-
Make It User-Friendly:
- Add clear settings with good descriptions
- Use
disableToggle: trueso users can turn it off if needed - Add helpful error messages if something goes wrong
- Use
beta: truefor experimental features to let users know they're trying something new
Plugin Metadata Options¶
Your plugin object supports several optional flags to customize how it appears and behaves:
const myPlugin: Plugin = {
id: "my-plugin",
name: "My Plugin",
description: "What my plugin does",
version: "1.0.0",
// Optional flags:
disableToggle: true, // Show enable/disable toggle in settings
defaultEnabled: false, // Start disabled by default (requires disableToggle: true)
beta: true, // Show "Beta" tag in settings UI
// Your plugin code...
run: async (api) => { /* ... */ },
};
disableToggle: Whentrue, users can enable/disable your plugin in settingsdefaultEnabled: Whenfalse, your plugin starts disabled (only works withdisableToggle: true)beta: Whentrue, shows an orange "Beta" tag next to your plugin name in settings
Examples¶
Want to see more examples? Check out our built-in plugins:
- themes: Shows how to change SEQTA's appearance
- notificationCollector: Shows how to work with SEQTA's notifications
- timetable: Shows how to modify SEQTA's timetable view
- assessmentsAverage: Shows how to add new features to existing pages
Need Help?¶
Got stuck? No worries! Here's where you can get help:
- Join our Discord server
- Check out the built-in plugins in the
src/plugins/built-infolder on GitHub - Open an issue on our GitHub page
Happy coding and feel free to checkout the api reference here