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
text/typescript
/**
* @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;