252 lines
6.7 KiB
TypeScript
252 lines
6.7 KiB
TypeScript
|
|
import { EventEmitter } from '$lib/utils/EventEmitter';
|
||
|
|
import { get } from 'svelte/store';
|
||
|
|
import { accessibilitySettings, uiSettings } from '$lib/stores/mudStore';
|
||
|
|
|
||
|
|
export interface Settings {
|
||
|
|
accessibility: {
|
||
|
|
textToSpeech: boolean;
|
||
|
|
highContrast: boolean;
|
||
|
|
fontSize: number;
|
||
|
|
lineSpacing: number;
|
||
|
|
speechRate: number;
|
||
|
|
speechPitch: number;
|
||
|
|
speechVolume: number;
|
||
|
|
interruptSpeechOnEnter: boolean;
|
||
|
|
};
|
||
|
|
ui: {
|
||
|
|
isDarkMode: boolean;
|
||
|
|
showTimestamps: boolean;
|
||
|
|
showSidebar: boolean;
|
||
|
|
splitViewDirection: 'horizontal' | 'vertical';
|
||
|
|
inputHistorySize: number;
|
||
|
|
outputBufferSize: number;
|
||
|
|
ansiColor: boolean;
|
||
|
|
font: string;
|
||
|
|
debugGmcp: boolean;
|
||
|
|
globalVolume: number;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export class SettingsManager extends EventEmitter {
|
||
|
|
private settings: Settings;
|
||
|
|
private readonly STORAGE_KEY = 'svelte-mud-settings';
|
||
|
|
|
||
|
|
constructor() {
|
||
|
|
super();
|
||
|
|
|
||
|
|
// Initialize with default settings
|
||
|
|
this.settings = {
|
||
|
|
accessibility: {
|
||
|
|
textToSpeech: false,
|
||
|
|
highContrast: false,
|
||
|
|
fontSize: 16,
|
||
|
|
lineSpacing: 1.2,
|
||
|
|
speechRate: 1,
|
||
|
|
speechPitch: 1,
|
||
|
|
speechVolume: 1,
|
||
|
|
interruptSpeechOnEnter: true
|
||
|
|
},
|
||
|
|
ui: {
|
||
|
|
isDarkMode: true,
|
||
|
|
showTimestamps: true,
|
||
|
|
showSidebar: true,
|
||
|
|
splitViewDirection: 'horizontal',
|
||
|
|
inputHistorySize: 100,
|
||
|
|
outputBufferSize: 1000,
|
||
|
|
ansiColor: true,
|
||
|
|
font: 'monospace',
|
||
|
|
debugGmcp: false,
|
||
|
|
globalVolume: 0.7
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Load settings from storage
|
||
|
|
this.loadSettings();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Load settings from localStorage
|
||
|
|
*/
|
||
|
|
private loadSettings(): void {
|
||
|
|
if (typeof window === 'undefined') {
|
||
|
|
return; // Skip during SSR
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const storedSettings = localStorage.getItem(this.STORAGE_KEY);
|
||
|
|
|
||
|
|
if (storedSettings) {
|
||
|
|
const parsedSettings = JSON.parse(storedSettings);
|
||
|
|
|
||
|
|
// Merge with defaults to ensure all properties exist
|
||
|
|
this.settings = {
|
||
|
|
accessibility: {
|
||
|
|
...this.settings.accessibility,
|
||
|
|
...parsedSettings.accessibility
|
||
|
|
},
|
||
|
|
ui: {
|
||
|
|
...this.settings.ui,
|
||
|
|
...parsedSettings.ui
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
console.log('Loaded settings from localStorage:', this.settings);
|
||
|
|
|
||
|
|
// Update Svelte stores with loaded settings
|
||
|
|
accessibilitySettings.set(this.settings.accessibility);
|
||
|
|
uiSettings.set(this.settings.ui);
|
||
|
|
|
||
|
|
this.emit('settingsLoaded', this.settings);
|
||
|
|
} else {
|
||
|
|
console.log('No settings found in localStorage, using defaults');
|
||
|
|
// Update Svelte stores with default settings
|
||
|
|
accessibilitySettings.set(this.settings.accessibility);
|
||
|
|
uiSettings.set(this.settings.ui);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to load settings from localStorage:', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Save settings to localStorage
|
||
|
|
*/
|
||
|
|
public saveSettings(): void {
|
||
|
|
if (typeof window === 'undefined') {
|
||
|
|
return; // Skip during SSR
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Get current values from stores
|
||
|
|
this.settings.accessibility = get(accessibilitySettings);
|
||
|
|
this.settings.ui = get(uiSettings);
|
||
|
|
|
||
|
|
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.settings));
|
||
|
|
console.log('Saved settings to localStorage:', this.settings);
|
||
|
|
|
||
|
|
this.emit('settingsSaved', this.settings);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to save settings to localStorage:', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update accessibility settings
|
||
|
|
*/
|
||
|
|
public updateAccessibilitySettings(newSettings: Partial<Settings['accessibility']>): void {
|
||
|
|
accessibilitySettings.update(current => {
|
||
|
|
const updated = { ...current, ...newSettings };
|
||
|
|
this.settings.accessibility = updated;
|
||
|
|
this.saveSettings();
|
||
|
|
return updated;
|
||
|
|
});
|
||
|
|
|
||
|
|
this.emit('accessibilitySettingsUpdated', get(accessibilitySettings));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update UI settings
|
||
|
|
*/
|
||
|
|
public updateUiSettings(newSettings: Partial<Settings['ui']>): void {
|
||
|
|
uiSettings.update(current => {
|
||
|
|
const updated = { ...current, ...newSettings };
|
||
|
|
this.settings.ui = updated;
|
||
|
|
this.saveSettings();
|
||
|
|
return updated;
|
||
|
|
});
|
||
|
|
|
||
|
|
this.emit('uiSettingsUpdated', get(uiSettings));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Reset settings to defaults
|
||
|
|
*/
|
||
|
|
public resetSettings(): void {
|
||
|
|
// Create default settings
|
||
|
|
const defaultSettings: Settings = {
|
||
|
|
accessibility: {
|
||
|
|
textToSpeech: false,
|
||
|
|
highContrast: false,
|
||
|
|
fontSize: 16,
|
||
|
|
lineSpacing: 1.2,
|
||
|
|
speechRate: 1,
|
||
|
|
speechPitch: 1,
|
||
|
|
speechVolume: 1,
|
||
|
|
interruptSpeechOnEnter: true
|
||
|
|
},
|
||
|
|
ui: {
|
||
|
|
isDarkMode: true,
|
||
|
|
showTimestamps: true,
|
||
|
|
showSidebar: true,
|
||
|
|
splitViewDirection: 'horizontal',
|
||
|
|
inputHistorySize: 100,
|
||
|
|
outputBufferSize: 1000,
|
||
|
|
ansiColor: true,
|
||
|
|
font: 'monospace',
|
||
|
|
debugGmcp: false,
|
||
|
|
globalVolume: 0.7
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Update stores
|
||
|
|
accessibilitySettings.set(defaultSettings.accessibility);
|
||
|
|
uiSettings.set(defaultSettings.ui);
|
||
|
|
|
||
|
|
// Update internal settings and save
|
||
|
|
this.settings = defaultSettings;
|
||
|
|
this.saveSettings();
|
||
|
|
|
||
|
|
this.emit('settingsReset', defaultSettings);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Import settings from a JSON string
|
||
|
|
*/
|
||
|
|
public importSettings(json: string): void {
|
||
|
|
try {
|
||
|
|
const imported = JSON.parse(json);
|
||
|
|
|
||
|
|
if (typeof imported === 'object' && imported !== null) {
|
||
|
|
// Create a valid settings object with defaults for missing properties
|
||
|
|
const newSettings: Settings = {
|
||
|
|
accessibility: {
|
||
|
|
...this.settings.accessibility,
|
||
|
|
...(imported.accessibility || {})
|
||
|
|
},
|
||
|
|
ui: {
|
||
|
|
...this.settings.ui,
|
||
|
|
...(imported.ui || {})
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Update stores
|
||
|
|
accessibilitySettings.set(newSettings.accessibility);
|
||
|
|
uiSettings.set(newSettings.ui);
|
||
|
|
|
||
|
|
// Update internal settings and save
|
||
|
|
this.settings = newSettings;
|
||
|
|
this.saveSettings();
|
||
|
|
|
||
|
|
this.emit('settingsImported', newSettings);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to import settings:', error);
|
||
|
|
throw new Error('Failed to import settings. Invalid format.');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Export settings to a JSON string
|
||
|
|
*/
|
||
|
|
public exportSettings(): string {
|
||
|
|
// Get current values from stores
|
||
|
|
this.settings.accessibility = get(accessibilitySettings);
|
||
|
|
this.settings.ui = get(uiSettings);
|
||
|
|
|
||
|
|
return JSON.stringify(this.settings, null, 2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create a singleton instance
|
||
|
|
export const settingsManager = new SettingsManager();
|