UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

206 lines (190 loc) • 7.08 kB
/** * @moduleName: Settings Service (MVP Simplified) * @version: 1.0.0 * @since: 2025-07-24 * @lastUpdated: 2025-07-24 * @projectSummary: Local browser storage for user preferences - simplified for MVP * @techStack: TypeScript, LocalStorage API, Browser APIs * @dependency: Browser localStorage * @interModuleDependency: None * @requirementsTraceability: * {@link Requirements.REQ_CONFIG_001} (Local storage only) * @briefDescription: Basic settings management for MVP deployment * @methods: getSettings, updateSettings, resetToDefaults * @contributors: GitHub Copilot * @examples: const service = new SettingsService(); service.getSettings(); * @vulnerabilitiesAssessment: Local storage only, no sensitive data */ export interface AppSettings { ui: { hideDisabledNavButtons: boolean; hideAllNavButtons: boolean; autoAdvanceQuestions: boolean; clickToAdvance: boolean; showContinueButton: boolean; announceSelections: boolean; selectionFeedbackDuration: number; showQuestionNumbers: boolean; enableAnimations: boolean; defaultViewMode: 'single' | 'list'; sidebarExpanded: boolean; theme: 'light' | 'dark' | 'auto'; accessibilityMode: boolean; // ISSUE_020: Click-to-Select Answer Cards clickToSelectCards: boolean; // NEW: Unified Quiz Mode quizMode: 'traditional' | 'cards' | 'instant'; }; quiz: { showHints: boolean; confirmBeforeSubmit: boolean; highlightAnsweredQuestions: boolean; // MODERNIZED: Single timer configuration (removed confusing dual logic) useTimer: boolean; timerDuration: number; // seconds maxAttempts: number; // Educational Feedback Enhancement (ISSUE_009_FEEDBACK) showImmediateFeedback: boolean; showExplanationsAfterAnswer: boolean; enableCoachingMode: boolean; coachingIntensity: 'basic' | 'detailed' | 'comprehensive'; // Instant Click-to-Submit System instantSubmission: boolean; instantFeedbackDelay: number; allowAnswerChange: boolean; showInstantExplanation: boolean; // Enhanced UI Features (REQ-UI-011, REQ-UI-012) autoAdvanceEnabled: boolean; autoAdvanceDelay: number; smartSubmitLogic: boolean; partialSubmissionEnabled: boolean; // NEW: Unified Auto-advance Mode autoAdvanceMode: 'off' | 'basic' | 'enhanced' | 'custom'; }; } export class SettingsService { private static readonly STORAGE_KEY = 'quiz-app-settings'; private static instance: SettingsService; private settings: AppSettings; private listeners: Set<(settings: AppSettings) => void> = new Set(); constructor() { this.settings = this.loadSettings(); } /** * @description Gets singleton instance (for compatibility) * @returns {SettingsService} Singleton instance */ static getInstance(): SettingsService { if (!SettingsService.instance) { SettingsService.instance = new SettingsService(); } return SettingsService.instance; } private getDefaultSettings(): AppSettings { return { ui: { hideDisabledNavButtons: false, hideAllNavButtons: false, autoAdvanceQuestions: false, clickToAdvance: false, showContinueButton: true, announceSelections: true, selectionFeedbackDuration: 1000, showQuestionNumbers: true, enableAnimations: true, defaultViewMode: 'list', sidebarExpanded: true, theme: 'auto', accessibilityMode: false, // ISSUE_020: Click-to-Select Answer Cards clickToSelectCards: true, // Enable clickable answer cards in single mode // NEW: Unified Quiz Mode quizMode: 'cards', // Default to click-to-select cards }, quiz: { showHints: false, confirmBeforeSubmit: true, highlightAnsweredQuestions: true, // MODERNIZED: Single timer configuration with sensible defaults useTimer: false, timerDuration: 900, // 15 minutes default maxAttempts: 3, // Educational Feedback Enhancement (ISSUE_009_FEEDBACK) - ENABLED FOR ENHANCED LEARNING showImmediateFeedback: true, showExplanationsAfterAnswer: true, enableCoachingMode: true, coachingIntensity: 'basic', // Instant Click-to-Submit System instantSubmission: false, instantFeedbackDelay: 1500, // milliseconds before auto-advance allowAnswerChange: true, // allow clicking different answer before auto-advance showInstantExplanation: true, // Enhanced UI Features (REQ-UI-011, REQ-UI-012, REQ-UI-013) autoAdvanceEnabled: false, // User must explicitly enable autoAdvanceDelay: 3000, // 3 seconds default delay smartSubmitLogic: true, // Smart submit button logic enabled partialSubmissionEnabled: false, // Phase 2 feature, disabled for now // NEW: Unified Auto-advance Mode autoAdvanceMode: 'enhanced', // Default to enhanced with feedback }, }; } getSettings(): AppSettings { return { ...this.settings }; } updateSettings(updates: Partial<AppSettings>): void { this.settings = { ...this.settings, ...updates }; this.saveSettings(); this.notifyListeners(); } updateUISettings(uiSettings: Partial<AppSettings['ui']>): void { this.settings.ui = { ...this.settings.ui, ...uiSettings }; this.saveSettings(); this.notifyListeners(); } updateQuizSettings(quizSettings: Partial<AppSettings['quiz']>): void { this.settings.quiz = { ...this.settings.quiz, ...quizSettings }; this.saveSettings(); this.notifyListeners(); } resetToDefaults(): void { this.settings = this.getDefaultSettings(); this.saveSettings(); this.notifyListeners(); } subscribe(callback: (settings: AppSettings) => void): () => void { this.listeners.add(callback); return () => this.listeners.delete(callback); } private loadSettings(): AppSettings { try { const stored = localStorage.getItem(SettingsService.STORAGE_KEY); if (stored) { const parsed = JSON.parse(stored); return { ...this.getDefaultSettings(), ...parsed }; } } catch (error) { console.warn('Failed to load settings:', error); } return this.getDefaultSettings(); } private saveSettings(): void { try { localStorage.setItem(SettingsService.STORAGE_KEY, JSON.stringify(this.settings)); } catch (error) { console.error('Failed to save settings:', error); } } private notifyListeners(): void { this.listeners.forEach(callback => { try { callback(this.settings); } catch (error) { console.error('Error in settings listener:', error); } }); } } // Export singleton instance for compatibility const settingsService = new SettingsService(); export default settingsService;