# Plugin API Reference This document provides detailed technical information about BetterSEQTA+'s plugin APIs. For a beginner-friendly introduction, see [Creating Your First Plugin](./README.md). ## Plugin Structure Here's how a plugin is structured: ```typescript import type { Plugin } from '@/plugins/core/types'; import { BasePlugin } from '@/plugins/core/settings'; import { booleanSetting, defineSettings, Setting } from '@/plugins/core/settingsHelpers'; // First, define your settings const settings = defineSettings({ enabled: booleanSetting({ default: true, title: "Enable Feature", description: "Turn this feature on or off", }) }); // Create a class to handle your settings class MyPluginClass extends BasePlugin { @Setting(settings.enabled) enabled!: boolean; } // Create an instance of your settings const settingsInstance = new MyPluginClass(); // Create your plugin const myPlugin: Plugin = { id: 'my-plugin', name: 'My Plugin', description: 'A cool plugin that does things', version: '1.0.0', settings: settingsInstance.settings, disableToggle: true, run: async (api) => { console.log('Plugin is running!'); // Do stuff when settings change api.settings.onChange('enabled', (enabled) => { if (enabled) { console.log('Feature enabled!'); } }); // Return a cleanup function return () => { console.log('Plugin cleanup'); }; } }; export default myPlugin; ``` ## SEQTA API The SEQTA API helps you interact with SEQTA's pages: ```typescript import type { Plugin } from '@/plugins/core/types'; const seqtaPlugin: Plugin = { id: 'seqta-example', name: 'SEQTA Example', description: 'Shows how to use the SEQTA API', version: '1.0.0', settings: {}, disableToggle: true, run: async (api) => { // Wait for elements to appear const { unregister: timetableUnregister } = api.seqta.onMount('.timetable', (timetable) => { const button = document.createElement('button'); button.textContent = 'Export'; timetable.appendChild(button); }); // Track page changes const { unregister: pageUnregister } = api.seqta.onPageChange((page) => { console.log('User went to:', page); }); // Clean up when disabled return () => { timetableUnregister(); pageUnregister(); }; } }; export default seqtaPlugin; ``` ## Settings API Here's how to add settings to your plugin: ```typescript import type { Plugin } from '@/plugins/core/types'; import { BasePlugin } from '@/plugins/core/settings'; import { booleanSetting, stringSetting, numberSetting, selectSetting, defineSettings, Setting } from '@/plugins/core/settingsHelpers'; // Define your settings const settings = defineSettings({ darkMode: booleanSetting({ default: false, title: "Dark Mode", description: "Enable dark mode" }), userName: stringSetting({ default: "", title: "User Name", description: "Your display name", placeholder: "Enter your name..." }), theme: selectSetting({ default: "light", title: "Theme", description: "Choose your theme", options: [ { value: "light", label: "Light" }, { value: "dark", label: "Dark" } ] }) }); // Create your settings class class ThemePluginClass extends BasePlugin { @Setting(settings.darkMode) darkMode!: boolean; @Setting(settings.userName) userName!: string; @Setting(settings.theme) theme!: string; } // Create the plugin const themePlugin: Plugin = { id: 'theme-example', name: 'Theme Example', description: 'Shows how to use settings', version: '1.0.0', settings: new ThemePluginClass().settings, disableToggle: true, run: async (api) => { // Apply initial settings if (api.settings.darkMode) { document.body.classList.add('dark'); } // Listen for changes const { unregister } = api.settings.onChange('darkMode', (enabled) => { document.body.classList.toggle('dark', enabled); }); return () => { unregister(); document.body.classList.remove('dark'); }; } }; export default themePlugin; ``` ## Storage API Here's how to use storage in your plugin: ```typescript import type { Plugin } from '@/plugins/core/types'; const storagePlugin: Plugin = { id: 'storage-example', name: 'Storage Example', description: 'Shows how to use storage', version: '1.0.0', settings: {}, disableToggle: true, run: async (api) => { // Wait for storage to be ready await api.storage.loaded; // Save some data await api.storage.set('lastVisit', new Date().toISOString()); // Get saved data const lastVisit = await api.storage.get('lastVisit'); console.log('Last visit:', lastVisit); // Listen for changes const { unregister } = api.storage.onChange('lastVisit', (newValue) => { console.log('Last visit updated:', newValue); }); return () => { unregister(); }; } }; export default storagePlugin; ``` ## Events API Here's how to use events in your plugin: ```typescript import type { Plugin } from '@/plugins/core/types'; const eventsPlugin: Plugin = { id: 'events-example', name: 'Events Example', description: 'Shows how to use events', version: '1.0.0', settings: {}, disableToggle: true, run: async (api) => { // Listen for theme changes const { unregister: themeListener } = api.events.on('theme.changed', (theme) => { console.log('Theme changed to:', theme); }); // Listen for notifications const { unregister: notifyListener } = api.events.on('notification.new', (notification) => { console.log('New notification:', notification); }); // Clean up listeners return () => { themeListener(); notifyListener(); }; } }; export default eventsPlugin; ``` ## Performance Tips Here's how to write efficient plugins: ```typescript import type { Plugin } from '@/plugins/core/types'; const efficientPlugin: Plugin = { id: 'efficient-example', name: 'Efficient Example', description: 'Shows performance best practices', version: '1.0.0', settings: {}, disableToggle: true, run: async (api) => { // ✅ Good: Use onMount const { unregister } = api.seqta.onMount('.timetable', (el) => { el.classList.add('enhanced'); }); // ❌ Bad: Don't use intervals // const interval = setInterval(() => { // const el = document.querySelector('.timetable'); // if (el) el.classList.add('enhanced'); // }, 100); // ✅ Good: Cache DOM elements const header = document.querySelector('.header'); if (header) { // Reuse header instead of querying again } // ✅ Good: Batch DOM updates const fragment = document.createDocumentFragment(); for (let i = 0; i < 10; i++) { const div = document.createElement('div'); fragment.appendChild(div); } document.body.appendChild(fragment); return () => { unregister(); // clearInterval(interval); // If you used the bad approach }; } }; export default efficientPlugin; ``` Each plugin should be in its own file and exported as the default export. The plugin should: 1. Import necessary types and helpers 2. Define settings if needed 3. Create a settings class if using settings 4. Create the plugin object with proper type annotation 5. Export the plugin as default Remember to always: - Use proper TypeScript types - Clean up when your plugin is disabled - Handle errors gracefully - Follow the plugin structure shown above