MOOC Utils

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

State management

Introduction#

Side panel state: the state manager factory, how state is initialized, and what triggers updates.

Project structure#

The state management lives in the assignment-solver UI module. The primary state container is created in a dedicated module and consumed by controllers that orchestrate user interactions, progress updates, settings, and detection. Persistence is handled by a storage service backed by a browser storage adapter. Communication with the extension runtime is abstracted behind a runtime adapter.

Core components#

  • State Manager Factory: Creates a private state object and exposes typed getters/setters plus a reset method. This is the single source of truth for UI state.
  • Controllers: Encapsulate UI logic and side effects. They mutate state via the state manager and update the DOM through the elements registry.
  • Elements Registry: Centralized access to DOM nodes used by controllers.
  • Storage Service: Provides persistence for API keys, model preferences, cached extractions, and user answers.
  • Platform Adapters: Abstract browser APIs for runtime messaging and storage.

Key responsibilities:

  • State Manager: Immutable-like updates via setters; no direct DOM manipulation.
  • Controllers: Orchestrate flows, update progress, and mutate state.
  • Elements: Provide DOM handles; controllers update attributes/classes/textContent.
  • Storage: Persist and retrieve structured data with timestamps and metadata.
  • Adapters: Enable cross-browser compatibility and testability.

Architecture overview#

The system follows a unidirectional data flow:

  • Initialization: The UI entry point creates adapters, services, state, and controllers. It wires event listeners and starts detection.
  • Mutations: Controllers call state setters and storage methods to change state and persist data.
  • Re-rendering: Controllers update DOM nodes directly based on state and progress. There is no framework-level re-render mechanism; DOM updates are imperative.
  • External synchronization: Runtime adapter sends messages to background scripts; storage adapter persists/retrieves data.

Detailed component analysis#

State manager factory#

The state manager is a factory that returns a small API over a private state object. It exposes:

  • Getters for isProcessing, extraction, and currentStep
  • Setters for the same fields
  • A reset method that reinitializes the state to defaults

Implementation pattern:

  • Private state object holds UI state
  • Public methods mutate the private state
  • No external mutation of the state object occurs outside the factory

State initialization patterns#

Initialization occurs in the UI entry point:

  • Create adapters (storage and runtime)
  • Create services (storage and Gemini)
  • Create state manager
  • Create controllers (progress, settings, solve, detection)
  • Load persisted API key into UI
  • Wait for background readiness
  • Wire event listeners and start detection

State update triggers and propagation#

State updates are triggered by:

  • User actions (solve button click)
  • Background messages (tab updates)
  • Settings saves

Propagation:

  • Controllers mutate state via setters
  • Controllers update DOM directly (no framework re-render)
  • Progress controller updates status bars, steps, and progress bars
  • Detection controller toggles visibility based on page info
  • Settings controller reads/writes storage and updates UI fields

State observers and reactivity#

There is no explicit observer pattern. Reactivity is achieved by:

  • Direct DOM updates in controllers
  • State setters that replace the internal state object reference
  • Event-driven updates from runtime messages

Controllers observe state changes indirectly by:

  • Reading state via getters
  • Updating UI based on state transitions
  • Listening to runtime events to refresh UI state

State mutations#

Common mutation patterns:

  • Toggle processing flag: setIsProcessing(true/false)
  • Replace extraction payload: setExtraction(result)
  • Update current step: setCurrentStep(step)
  • Reset state: reset()

These mutations are performed by controllers in response to user actions or external events.

State synchronization with external data sources#

  • Storage synchronization:
  • Save API key and preferences via storage service
  • Cache extractions with timestamps and URL metadata
  • Retrieve and clear cached data as needed
  • Runtime synchronization:
  • Send messages to background scripts for page extraction, screenshots, answer application, and submission
  • Listen for tab updates to refresh detection UI

State persistence strategies#

  • API key and preferences: stored under dedicated keys with defaults when missing
  • Extraction cache: stores last extraction with URL and timestamp for quick retrieval
  • User answers: stored separately and combined with extractions for export
  • Export formats: answer-only export merges extraction and answers

Best practices:

  • Always check for existence before reading from storage
  • Clear caches when appropriate (e.g., after submission)
  • Use structured keys to avoid collisions

Dependency analysis#

The UI entry point composes all collaborators and passes them to controllers. Controllers depend on:

  • State manager for UI state
  • Storage service for persistence
  • Runtime adapter for messaging
  • Elements registry for DOM access

Performance considerations#

  • Minimize DOM updates: Controllers update DOM directly; batch related updates (e.g., progress count and fill width) together.
  • Debounce or throttle frequent UI updates: For long-running loops (e.g., filling answers), consider increasing delays slightly to reduce churn.
  • Avoid unnecessary state churn: Only call setters when values change to prevent redundant UI work.
  • Cache and reuse DOM nodes: Elements registry centralizes DOM access to avoid repeated queries.
  • Favor immutable-like updates: State manager replaces the state object reference on change, simplifying change detection and avoiding accidental shared mutable state.
  • Memory management:
  • Clear caches (e.g., extraction) when no longer needed
  • Remove event listeners when components unmount (implicit via controller lifecycle)
  • Avoid retaining large payloads in state beyond their lifetime
  • External calls:
  • Use retry helpers for runtime messages
  • Limit concurrent heavy operations (e.g., screenshot capture)

Troubleshooting guide#

Common issues and remedies:

  • Background not ready: The UI waits for a readiness signal before proceeding. If it fails, the app continues with warnings. Verify background script availability and message routing.
  • API key missing: The solve flow checks for an API key and prompts the settings controller if absent.
  • MAX_TOKENS errors: The solve controller splits HTML or questions recursively until successful or depth limit reached. Review model preferences and consider reducing input size.
  • Extraction failures: Ensure the page is an assignment page; verify extraction results and debug payloads sent to the background.
  • Progress stuck: Ensure progress controller steps are marked done and progress is reset appropriately.

Conclusion#

One state manager, explicit updates. Persist only settings the user expects to survive a reload.