Adding Features

How to add new features to BetterSEQTA-Plus

Adding Features Overview

This guide explains how to add new features to BetterSEQTA-Plus, from initial setup to deployment.

Feature Development Workflow

1. Plan Your Feature

Before coding:

  • Define the feature - What does it do?
  • Identify requirements - What's needed?
  • Consider impact - How does it affect users?
  • Plan implementation - How will it work?

2. Create Feature Module

Create a new feature file:

// src/content/features/my-feature.ts
export class MyFeature {
  private enabled = false;
  
  init() {
    this.enabled = true;
    this.setup();
  }
  
  destroy() {
    this.enabled = false;
    this.cleanup();
  }
  
  private setup() {
    // Initialize feature
  }
  
  private cleanup() {
    // Clean up feature
  }
}

3. Register Feature

Register in feature manager:

// src/content/main.ts
import { MyFeature } from './features/my-feature';

featureManager.register({
  id: 'myFeature',
  name: 'My Feature',
  module: MyFeature
});

4. Add Settings

Add feature settings:

// src/background/settings.ts
interface Settings {
  myFeature: {
    enabled: boolean;
    option1: string;
    option2: number;
  };
}

5. Add UI Controls

Add to options page:

<!-- src/options/options.html -->
<div class="feature-toggle">
  <label>
    <input type="checkbox" data-feature="myFeature">
    My Feature
  </label>
</div>

Feature Template

Complete Feature Example

// src/content/features/example-feature.ts
export class ExampleFeature {
  private enabled = false;
  private observer?: MutationObserver;
  
  init() {
    if (this.enabled) return;
    
    this.enabled = true;
    this.setup();
    this.listenForChanges();
  }
  
  destroy() {
    if (!this.enabled) return;
    
    this.enabled = false;
    this.cleanup();
  }
  
  private setup() {
    // Wait for page to be ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => this.enhance());
    } else {
      this.enhance();
    }
  }
  
  private enhance() {
    // Find elements to enhance
    const elements = document.querySelectorAll('.target-element');
    elements.forEach(element => {
      this.enhanceElement(element);
    });
  }
  
  private enhanceElement(element: Element) {
    // Add enhancement
    element.classList.add('betterseqta-enhanced');
  }
  
  private listenForChanges() {
    // Watch for new elements
    this.observer = new MutationObserver(() => {
      this.enhance();
    });
    
    this.observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }
  
  private cleanup() {
    // Remove enhancements
    document.querySelectorAll('.betterseqta-enhanced').forEach(element => {
      element.classList.remove('betterseqta-enhanced');
    });
    
    // Disconnect observer
    if (this.observer) {
      this.observer.disconnect();
    }
  }
}

Feature Configuration

Feature Manifest

// src/shared/features.ts
export interface FeatureConfig {
  id: string;
  name: string;
  description: string;
  category: 'ui' | 'functionality' | 'performance';
  defaultEnabled: boolean;
  dependencies?: string[];
  settings?: Record<string, any>;
}

export const features: FeatureConfig[] = [
  {
    id: 'myFeature',
    name: 'My Feature',
    description: 'Description of my feature',
    category: 'ui',
    defaultEnabled: false,
    settings: {
      option1: 'default',
      option2: 10
    }
  }
];

Testing Features

Manual Testing

  1. Load extension in browser
  2. Enable feature in settings
  3. Navigate to SEQTA Learn
  4. Verify feature works
  5. Test edge cases

Automated Testing

// tests/features/my-feature.test.ts
describe('MyFeature', () => {
  let feature: MyFeature;
  
  beforeEach(() => {
    feature = new MyFeature();
  });
  
  test('initializes correctly', () => {
    feature.init();
    expect(feature.enabled).toBe(true);
  });
  
  test('cleans up on destroy', () => {
    feature.init();
    feature.destroy();
    expect(feature.enabled).toBe(false);
  });
});

Best Practices

1. Feature Isolation

// ✅ Good - Isolated feature
class MyFeature {
  private namespace = 'myFeature';
  
  enhance() {
    // Use namespace for classes/IDs
    element.classList.add(`${this.namespace}-enhanced`);
  }
}

// ❌ Avoid - Global names
element.classList.add('enhanced');

2. Error Handling

init() {
  try {
    this.setup();
  } catch (error) {
    console.error('MyFeature: Failed to initialize', error);
    // Report to background
    chrome.runtime.sendMessage({
      action: 'error',
      feature: 'myFeature',
      error: error.message
    });
  }
}

3. Performance

// ✅ Good - Debounce expensive operations
const debouncedEnhance = debounce(() => {
  this.enhance();
}, 100);

// ❌ Avoid - Run on every mutation
observer.observe(document.body, {
  callback: this.enhance // Too frequent!
});

4. Settings Integration

class MyFeature {
  private settings: any = {};
  
  async loadSettings() {
    const stored = await chrome.storage.sync.get('myFeature');
    this.settings = stored.myFeature || {};
    this.applySettings();
  }
  
  private applySettings() {
    // Apply settings to feature
  }
}

Feature Documentation

Document Your Feature

/**
 * My Feature
 * 
 * Enhances SEQTA Learn with [description].
 * 
 * Features:
 * - Feature 1
 * - Feature 2
 * 
 * Settings:
 * - option1: Description
 * - option2: Description
 * 
 * Usage:
 * ```typescript
 * const feature = new MyFeature();
 * feature.init();
 * ```
 */
export class MyFeature {
  // ...
}

Submitting Features

Pull Request Checklist

  • Feature implemented
  • Tests written
  • Documentation updated
  • Settings added
  • UI controls added
  • Error handling included
  • Performance considered
  • No conflicts with existing features

Next Steps