Skip to content

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:

  1. First, we import the Plugin type that tells TypeScript what a plugin should look like
  2. We create our plugin object with some basic information:
  3. id: A unique name for your plugin (use lowercase and dashes)
  4. name: A friendly name that users will see
  5. description: Explain what your plugin does
  6. version: Your plugin's version number
  7. We set disableToggle: true so users can turn our plugin on/off in settings
  8. We set beta: true to mark the plugin as beta
  9. The run function is where we put our plugin's code
  10. We use api.seqta.onMount to wait for the homepage to load
  11. We create and style a message element
  12. 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:

  1. 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();
  };
};
  1. Use TypeScript: It helps catch errors before they happen and makes your code easier to understand.

  2. Test Your Plugin: Make sure it works in different situations:

  3. When SEQTA is loading

  4. When the user switches pages
  5. When the plugin is enabled/disabled
  6. When settings are changed

  7. Keep It Fast: Don't slow down SEQTA:

  8. Use onMount instead of intervals or timeouts

  9. Clean up event listeners when they're not needed
  10. Don't do heavy calculations on the main thread

  11. Make It User-Friendly:

  12. Add clear settings with good descriptions
  13. Use disableToggle: true so users can turn it off if needed
  14. Add helpful error messages if something goes wrong
  15. Use beta: true for 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: When true, users can enable/disable your plugin in settings
  • defaultEnabled: When false, your plugin starts disabled (only works with disableToggle: true)
  • beta: When true, shows an orange "Beta" tag next to your plugin name in settings

Examples

Want to see more examples? Check out our built-in plugins:

Need Help?

Got stuck? No worries! Here's where you can get help:

Happy coding and feel free to checkout the api reference here