2025-04-21 14:12:36 +02:00
|
|
|
import { EventEmitter } from '$lib/utils/EventEmitter';
|
|
|
|
|
|
|
|
|
|
export interface MudProfile {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
host: string;
|
|
|
|
|
port: number;
|
|
|
|
|
useSSL: boolean;
|
|
|
|
|
autoLogin?: {
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
username: string;
|
|
|
|
|
password: string;
|
|
|
|
|
commands: string[];
|
|
|
|
|
};
|
|
|
|
|
triggers?: string; // JSON string of triggers
|
|
|
|
|
aliases?: {
|
|
|
|
|
[key: string]: string;
|
|
|
|
|
};
|
|
|
|
|
macros?: {
|
|
|
|
|
[key: string]: string;
|
|
|
|
|
};
|
|
|
|
|
gmcpPackages?: string[]; // Additional GMCP packages to support
|
|
|
|
|
font?: string;
|
|
|
|
|
fontSize?: number;
|
|
|
|
|
theme?: string;
|
|
|
|
|
ansiColor: boolean;
|
|
|
|
|
accessibilityOptions?: {
|
|
|
|
|
textToSpeech: boolean;
|
|
|
|
|
highContrast: boolean;
|
|
|
|
|
speechRate: number;
|
|
|
|
|
speechPitch: number;
|
|
|
|
|
speechVolume: number;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ProfileManager extends EventEmitter {
|
|
|
|
|
private profiles: MudProfile[] = [];
|
|
|
|
|
private activeProfileId: string | null = null;
|
|
|
|
|
private storageKey = 'svelte-mud-profiles';
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
this.loadProfiles();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load profiles from local storage
|
|
|
|
|
*/
|
|
|
|
|
private loadProfiles(): void {
|
|
|
|
|
if (typeof window === 'undefined') {
|
|
|
|
|
return; // Skip during SSR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const storedProfiles = localStorage.getItem(this.storageKey);
|
2025-04-22 13:54:57 +02:00
|
|
|
console.log('Retrieved from localStorage:', storedProfiles);
|
|
|
|
|
|
2025-04-21 14:12:36 +02:00
|
|
|
if (storedProfiles) {
|
2025-04-22 13:54:57 +02:00
|
|
|
const parsed = JSON.parse(storedProfiles);
|
|
|
|
|
console.log('Parsed profiles:', parsed);
|
|
|
|
|
|
|
|
|
|
// Validate profiles before assigning
|
|
|
|
|
if (Array.isArray(parsed)) {
|
|
|
|
|
// Filter out invalid profiles
|
|
|
|
|
this.profiles = parsed.filter(profile => this.isValidProfile(profile));
|
|
|
|
|
console.log('Loaded profiles from localStorage:', this.profiles);
|
|
|
|
|
|
|
|
|
|
if (this.profiles.length > 0) {
|
|
|
|
|
this.emit('profilesLoaded', this.profiles);
|
|
|
|
|
return; // Success, exit early
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('No valid profiles found in stored data, creating default');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('Stored profiles data is not an array, creating default');
|
|
|
|
|
}
|
2025-04-21 14:12:36 +02:00
|
|
|
} else {
|
|
|
|
|
console.log('No profiles found in localStorage, creating default profile');
|
|
|
|
|
}
|
2025-04-22 13:54:57 +02:00
|
|
|
|
|
|
|
|
// If we got here, we need to create a default profile
|
|
|
|
|
const defaultProfile = this.createDefaultProfile();
|
|
|
|
|
defaultProfile.name = 'Aardwolf MUD';
|
|
|
|
|
defaultProfile.host = 'aardmud.org';
|
|
|
|
|
defaultProfile.port = 23;
|
|
|
|
|
|
|
|
|
|
// Clear profiles array and add the default
|
|
|
|
|
this.profiles = [];
|
|
|
|
|
this.addProfile(defaultProfile);
|
2025-04-21 14:12:36 +02:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to load profiles from local storage:', error);
|
2025-04-22 13:54:57 +02:00
|
|
|
console.error('Error details:', error.message, error.stack);
|
|
|
|
|
|
2025-04-21 14:12:36 +02:00
|
|
|
// Add a default profile if there was an error
|
|
|
|
|
const defaultProfile = this.createDefaultProfile();
|
|
|
|
|
defaultProfile.name = 'Aardwolf MUD';
|
|
|
|
|
defaultProfile.host = 'aardmud.org';
|
|
|
|
|
defaultProfile.port = 23;
|
2025-04-22 13:54:57 +02:00
|
|
|
|
|
|
|
|
// Clear profiles array and add the default
|
|
|
|
|
this.profiles = [];
|
2025-04-21 14:12:36 +02:00
|
|
|
this.addProfile(defaultProfile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Save profiles to local storage
|
|
|
|
|
*/
|
|
|
|
|
private saveProfiles(): void {
|
|
|
|
|
if (typeof window === 'undefined') {
|
|
|
|
|
return; // Skip during SSR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2025-04-22 13:54:57 +02:00
|
|
|
// Add logging to help debug
|
|
|
|
|
console.log('Saving profiles to localStorage:', this.profiles);
|
|
|
|
|
|
|
|
|
|
const profilesJson = JSON.stringify(this.profiles);
|
|
|
|
|
console.log('Profiles JSON:', profilesJson);
|
|
|
|
|
|
|
|
|
|
localStorage.setItem(this.storageKey, profilesJson);
|
|
|
|
|
console.log('Profiles saved successfully');
|
|
|
|
|
|
|
|
|
|
// Validate by reading back
|
|
|
|
|
const readBack = localStorage.getItem(this.storageKey);
|
|
|
|
|
console.log('Read back from localStorage:', readBack);
|
2025-04-21 14:12:36 +02:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to save profiles to local storage:', error);
|
2025-04-22 13:54:57 +02:00
|
|
|
console.error('Error details:', error.message, error.stack);
|
2025-04-21 14:12:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a new profile
|
|
|
|
|
*/
|
|
|
|
|
public addProfile(profile: MudProfile): void {
|
|
|
|
|
// Ensure all required fields are present
|
|
|
|
|
if (!profile.id) {
|
|
|
|
|
profile.id = `profile-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (profile.useSSL === undefined) {
|
|
|
|
|
profile.useSSL = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (profile.ansiColor === undefined) {
|
|
|
|
|
profile.ansiColor = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if a profile with the same ID already exists
|
|
|
|
|
const existingIndex = this.profiles.findIndex(p => p.id === profile.id);
|
|
|
|
|
|
|
|
|
|
if (existingIndex !== -1) {
|
|
|
|
|
// Update existing profile
|
|
|
|
|
this.profiles[existingIndex] = profile;
|
|
|
|
|
console.log(`Updated profile ${profile.id} (${profile.name})`, profile);
|
|
|
|
|
this.emit('profileUpdated', profile);
|
|
|
|
|
} else {
|
|
|
|
|
// Add new profile
|
|
|
|
|
this.profiles.push(profile);
|
|
|
|
|
console.log(`Added new profile ${profile.id} (${profile.name})`, profile);
|
|
|
|
|
this.emit('profileAdded', profile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save to storage
|
|
|
|
|
this.saveProfiles();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove a profile by ID
|
|
|
|
|
*/
|
|
|
|
|
public removeProfile(profileId: string): void {
|
|
|
|
|
const index = this.profiles.findIndex(p => p.id === profileId);
|
|
|
|
|
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
const removedProfile = this.profiles[index];
|
|
|
|
|
this.profiles.splice(index, 1);
|
|
|
|
|
|
|
|
|
|
// If the active profile was removed, clear the active profile
|
|
|
|
|
if (this.activeProfileId === profileId) {
|
|
|
|
|
this.activeProfileId = null;
|
|
|
|
|
this.emit('activeProfileChanged', null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.emit('profileRemoved', removedProfile);
|
|
|
|
|
this.saveProfiles();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all profiles
|
|
|
|
|
*/
|
|
|
|
|
public getProfiles(): MudProfile[] {
|
|
|
|
|
return [...this.profiles];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a profile by ID
|
|
|
|
|
*/
|
|
|
|
|
public getProfile(profileId: string): MudProfile | undefined {
|
|
|
|
|
return this.profiles.find(p => p.id === profileId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the active profile
|
|
|
|
|
*/
|
|
|
|
|
public setActiveProfile(profileId: string): void {
|
|
|
|
|
const profile = this.getProfile(profileId);
|
|
|
|
|
|
|
|
|
|
if (profile) {
|
|
|
|
|
this.activeProfileId = profileId;
|
|
|
|
|
this.emit('activeProfileChanged', profile);
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`Profile with ID ${profileId} not found`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the active profile
|
|
|
|
|
*/
|
|
|
|
|
public getActiveProfile(): MudProfile | null {
|
|
|
|
|
if (!this.activeProfileId) return null;
|
|
|
|
|
|
|
|
|
|
const activeProfile = this.getProfile(this.activeProfileId);
|
|
|
|
|
return activeProfile || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Import profiles from a JSON string
|
|
|
|
|
*/
|
|
|
|
|
public importProfiles(json: string): void {
|
|
|
|
|
try {
|
|
|
|
|
const imported = JSON.parse(json);
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(imported)) {
|
|
|
|
|
imported.forEach(profile => {
|
|
|
|
|
if (this.isValidProfile(profile)) {
|
|
|
|
|
this.addProfile(profile);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.emit('profilesImported', imported);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to import profiles:', error);
|
|
|
|
|
throw new Error('Failed to import profiles. Invalid format.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Export profiles to a JSON string
|
|
|
|
|
*/
|
|
|
|
|
public exportProfiles(): string {
|
|
|
|
|
return JSON.stringify(this.profiles, null, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Validate a profile object
|
|
|
|
|
*/
|
|
|
|
|
private isValidProfile(obj: any): boolean {
|
|
|
|
|
return (
|
|
|
|
|
typeof obj === 'object' &&
|
|
|
|
|
typeof obj.id === 'string' &&
|
|
|
|
|
typeof obj.name === 'string' &&
|
|
|
|
|
typeof obj.host === 'string' &&
|
|
|
|
|
typeof obj.port === 'number' &&
|
|
|
|
|
typeof obj.useSSL === 'boolean' &&
|
|
|
|
|
typeof obj.ansiColor === 'boolean'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a default profile
|
|
|
|
|
*/
|
|
|
|
|
public createDefaultProfile(): MudProfile {
|
|
|
|
|
const id = `profile-${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
id,
|
|
|
|
|
name: 'New Profile',
|
|
|
|
|
host: 'mud.example.com',
|
|
|
|
|
port: 23,
|
|
|
|
|
useSSL: false,
|
|
|
|
|
ansiColor: true,
|
|
|
|
|
autoLogin: {
|
|
|
|
|
enabled: false,
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
commands: []
|
|
|
|
|
},
|
|
|
|
|
aliases: {},
|
|
|
|
|
macros: {},
|
|
|
|
|
gmcpPackages: [],
|
|
|
|
|
accessibilityOptions: {
|
|
|
|
|
textToSpeech: false,
|
|
|
|
|
highContrast: false,
|
|
|
|
|
speechRate: 1,
|
|
|
|
|
speechPitch: 1,
|
|
|
|
|
speechVolume: 1
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|