MOOC Utils

Utils like auto assignment solver extension and course notice announcment notification for MOOC courses

Controller system

Introduction#

UI controllers for detection, progress, settings, and results. How each controller talks to state and to the background worker.

Project structure#

The UI controllers live under the assignment-solver extension’s frontend. The entry point initializes adapters, services, state, and controllers, wiring them together for a connected user experience.

Core components#

  • Detection Controller: Checks current page for assignments, displays assignment info or empty state, and listens for tab updates.
  • Progress Controller: Manages status messages, progress bars, step indicators, and visibility of progress sections.
  • Settings Controller: Loads and saves API keys and model preferences, manages settings modal UI, and wires event listeners.
  • Solve Controller: Orchestrates the full solving flow, including extraction, AI processing, answer filling, optional submission, and results display.

Architecture overview#

The UI entry point initializes adapters and services, then constructs controllers with dependency injection. Controllers communicate via shared state, DOM elements, and runtime messaging.

Detailed component analysis#

Detection controller#

Responsibilities:

  • Detect assignment presence on the current page.
  • Update UI to show assignment info or empty state.
  • Listen for tab update events and re-check automatically.

Initialization pattern:

  • Receives elements and runtime adapter.
  • On init, checks current page immediately and subscribes to runtime messages for tab updates.

Inter-controller communication:

  • Listens to runtime messages with type indicating tab updates.
  • Uses sendMessageWithRetry to fetch page info from background.

Error handling:

  • Catches errors during page info retrieval and falls back to empty state.

Lifecycle:

  • checkCurrentPage -> showAssignmentInfo/showEmptyState.
  • init triggers initial check and event subscription.

Progress controller#

Responsibilities:

  • Update status text and status bar visuals (loading/error states).
  • Manage step indicators with active/done/error states.
  • Control progress bar count and determinate/indeterminate modes.
  • Toggle visibility of progress and results sections.

Initialization pattern:

  • Receives elements and optional logger.
  • Exposes methods to manipulate DOM elements bound in elements.js.

Inter-controller communication:

  • Used by solve controller to reflect stages and progress.

Lifecycle:

  • setStatus/setStep/resetSteps/markStepDone/setProgress/setIndeterminate/resetProgress/showProgress/hideProgress.

Settings controller#

Responsibilities:

  • Load stored API key and model preferences into form inputs.
  • Save API key and preferences to storage.
  • Manage settings modal visibility and event listeners.
  • Notify parent callback on successful save.

Initialization pattern:

  • Receives elements, storage, and optional logger.
  • initEventListeners wires click handlers for settings open/close/save.

Inter-controller communication:

  • Calls storage service for persistence.
  • Invokes callbacks on save completion.

Lifecycle:

  • show -> populate inputs -> display modal.
  • save -> validate API key -> persist preferences -> return success.
  • hide -> animate out and hide.

Solve controller#

Responsibilities:

  • Orchestrate the full solving pipeline: extraction, screenshots, AI processing, answer filling, optional submission, and results display.
  • Implement recursive splitting to handle MAX_TOKENS errors.
  • Integrate with state, progress, settings, storage, runtime, and Gemini service.

Initialization pattern:

  • Receives elements, state, storage, gemini, runtime, progress, settings, and optional logger.
  • initEventListeners binds solve button to handleSolve.

Inter-controller communication:

  • Uses progress controller to reflect stages and progress.
  • Uses settings controller to prompt for API key if missing.
  • Uses state manager to guard concurrent runs and store extraction results.
  • Uses storage service for API key and preferences.
  • Uses runtime adapter and messages utility for background communication.
  • Uses Gemini service for extraction and solving.

Lifecycle:

  • handleSolve orchestrates the full flow with explicit steps and error handling.
  • Recursive splitting keeps reliability against token limits.
  • Results are rendered into results section with formatted HTML.

Controller factory functions and dependency injection#

  • createDetectionController: elements, runtime, logger.
  • createProgressController: elements, logger.
  • createSettingsController: elements, storage, logger.
  • createSolveController: elements, state, storage, gemini, runtime, progress, settings, logger.
  • createRuntimeAdapter: wraps browser runtime APIs for messaging and listening.
  • createStorageService: wraps storage adapter for API key, preferences, and caches.
  • createGeminiService: wraps runtime and API endpoint for extraction and solving.

Dependency injection pattern:

  • All controllers accept a deps object with required collaborators.
  • The UI entry point constructs adapters/services and injects them into controllers.
  • This promotes testability and modularity.

Event handling mechanisms#

  • Runtime messaging: sendMessageWithRetry handles transient connection failures and retries.
  • DOM events: Settings controller wires click handlers for open/close/save and reasoning selection updates.
  • Tab updates: Detection controller listens for runtime messages indicating tab changes.

Controller lifecycle, state integration, and error handling#

  • Lifecycle:
  • Initialization: UI entry point constructs and wires controllers.
  • Interaction: Users trigger actions (solve, settings open/save).
  • Completion: Results displayed; state reset on next run.
  • State integration:
  • State manager tracks isProcessing flag and extraction data.
  • Solve controller guards concurrent runs and stores extraction results.
  • Error handling:
  • sendMessageWithRetry retries on connection errors.
  • Solve controller splits work on MAX_TOKENS and aggregates results.
  • Errors are surfaced to UI via progress status and debug logging.

Testing patterns and mock implementations#

Recommended patterns:

  • Mock runtime adapter to stub sendMessage and onMessage for controller tests.
  • Mock storage service to simulate API key and preferences retrieval/persistence.
  • Mock Gemini service to return controlled extraction/solve results.
  • Mock DOM elements via elements.js to isolate UI logic.
  • Use state manager to assert state transitions during tests.

Mock examples (descriptive):

  • Runtime adapter mock: replace sendMessage with a spy that resolves to desired responses; onMessage adds a listener that triggers test scenarios.
  • Storage service mock: provide getApiKey/getModelPreferences returning known values; save methods record arguments.
  • Gemini service mock: resolve extract/solve with deterministic results; optionally reject to simulate errors.
  • Elements mock: return element references with innerHTML/className toggles for assertions.

Dependency analysis#

Controllers depend on shared services and adapters. The solve controller has the most dependencies, reflecting its central role.

Performance considerations#

  • Retry logic: sendMessageWithRetry reduces flakiness on slower browsers by retrying transient connection errors.
  • Recursive splitting: solve controller splits HTML and question sets to avoid MAX_TOKENS limits, trading latency for reliability.
  • Determinate progress: Progress controller uses determinate progress bars when counts are known; indeterminate mode otherwise.
  • Debouncing and pacing: Solve controller introduces small delays between answer fills to avoid overwhelming the target page.

Troubleshooting guide#

Common issues and strategies:

  • Background not ready: UI entry point waits for background readiness with exponential backoff; ensure extension reloads if background fails to initialize.
  • Connection errors: sendMessageWithRetry detects connection-related errors and retries; inspect last error message for guidance.
  • MAX_TOKENS errors: Solve controller splits work recursively; verify model preferences and reduce input size if necessary.
  • API key missing: Settings controller prompts for API key; ensure storage contains a valid key before solving.
  • Tab switching: Detection controller listens for tab updates; ensure runtime messages are flowing for accurate detection.

Conclusion#

Controllers are orchestration, not DOM. Keep Gemini and storage calls behind services so the UI stays testable.