Start fixing MDI

This commit is contained in:
2025-04-22 13:54:57 +02:00
parent 792272a478
commit f3b508c9f7
8 changed files with 461 additions and 228 deletions

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy, createEventDispatcher } from 'svelte'; import { onMount, onDestroy, createEventDispatcher } from 'svelte';
import { outputHistory, addToOutputHistory, addToInputHistory, navigateInputHistory, inputHistoryIndex, activeConnection, uiSettings, accessibilitySettings, inputHistory } from '$lib/stores/mudStore'; import { activeOutputHistory, addToOutputHistory, addToInputHistory, navigateInputHistory, activeInputHistoryIndex, activeConnection, uiSettings, accessibilitySettings, activeInputHistory } from '$lib/stores/mudStore';
import { tick } from 'svelte'; import { tick } from 'svelte';
import AnsiToHtml from 'ansi-to-html'; import AnsiToHtml from 'ansi-to-html';
import { AccessibilityManager } from '$lib/accessibility/AccessibilityManager'; import { AccessibilityManager } from '$lib/accessibility/AccessibilityManager';
@@ -106,7 +106,7 @@
addToInputHistory(currentInput); addToInputHistory(currentInput);
// Show the command in the output (only if not password - for privacy) // Show the command in the output (only if not password - for privacy)
const isPassword = currentInput.startsWith('password') || currentInput.toLowerCase() === $inputHistory[$inputHistory.length - 2]?.toLowerCase().replace('username', 'password'); const isPassword = currentInput.startsWith('password') || currentInput.toLowerCase() === $activeInputHistory[$activeInputHistory.length - 2]?.toLowerCase().replace('username', 'password');
if (!isPassword) { if (!isPassword) {
addToOutputHistory(`> ${currentInput}`, true); addToOutputHistory(`> ${currentInput}`, true);
} else { } else {
@@ -146,7 +146,8 @@
} else if (event.key === 'Escape') { } else if (event.key === 'Escape') {
event.preventDefault(); event.preventDefault();
currentInput = ''; currentInput = '';
inputHistoryIndex.set(-1); // No need to directly set inputHistoryIndex since we have per-profile indexes now
// This is handled in navigateInputHistory
} }
} }
@@ -267,7 +268,7 @@
// Watch output history changes to scroll to bottom // Watch output history changes to scroll to bottom
$: { $: {
if ($outputHistory) { if ($activeOutputHistory) {
scrollToBottom(); scrollToBottom();
// Update message elements when output history changes // Update message elements when output history changes
setTimeout(updateMessageElements, 0); setTimeout(updateMessageElements, 0);
@@ -317,7 +318,7 @@
tabindex="0" tabindex="0"
on:keydown={handleOutputKeyDown} on:keydown={handleOutputKeyDown}
style="font-family: {$uiSettings.font}; font-size: {$accessibilitySettings.fontSize}px; line-height: {$accessibilitySettings.lineSpacing};"> style="font-family: {$uiSettings.font}; font-size: {$accessibilitySettings.fontSize}px; line-height: {$accessibilitySettings.lineSpacing};">
{#each $outputHistory as item (item.id)} {#each $activeOutputHistory as item (item.id)}
<!-- For input lines, keep them as a single block --> <!-- For input lines, keep them as a single block -->
{#if item.isInput} {#if item.isInput}
<div class="mud-terminal-line mud-input-line" tabindex="-1"> <div class="mud-terminal-line mud-input-line" tabindex="-1">

View File

@@ -8,17 +8,48 @@
export let profile: MudProfile; export let profile: MudProfile;
export let isNewProfile = false; export let isNewProfile = false;
// Local state // Initialize with defaults first, then merge with provided profile
let localProfile = { ...profile }; let defaultProfile = {
let autoLogin = { ...localProfile.autoLogin || { enabled: false, username: '', password: '', commands: [] } }; id: isNewProfile ? `profile-${Date.now()}-${Math.random().toString(36).substring(2, 9)}` : (profile?.id || ''),
let newCommand = ''; name: 'New Profile',
let accessibilityOptions = { ...localProfile.accessibilityOptions || { host: 'mud.example.com',
port: 23,
useSSL: false,
ansiColor: true,
autoLogin: {
enabled: false,
username: '',
password: '',
commands: []
},
accessibilityOptions: {
textToSpeech: false, textToSpeech: false,
highContrast: false, highContrast: false,
speechRate: 1, speechRate: 1,
speechPitch: 1, speechPitch: 1,
speechVolume: 1 speechVolume: 1
}}; },
font: 'monospace',
fontSize: 14,
theme: 'dark'
};
// Local state - merge default with provided profile
let localProfile = { ...defaultProfile, ...profile };
console.log('Initialized profile editor with:', localProfile);
// Extract nested objects for easier binding
let autoLogin = {
...defaultProfile.autoLogin,
...(localProfile.autoLogin || {})
};
let newCommand = '';
let accessibilityOptions = {
...defaultProfile.accessibilityOptions,
...(localProfile.accessibilityOptions || {})
};
// Handle form submission // Handle form submission
function handleSubmit() { function handleSubmit() {

View File

@@ -60,11 +60,17 @@
input.click(); input.click();
} }
// Helper function to explicitly save settings
function saveSettings() {
console.log('Explicitly saving settings');
settingsManager.saveSettings();
}
onMount(() => { onMount(() => {
// Ensure settings are loaded from localStorage on component mount console.log('SettingsPanel mounted');
// This ensures the UI displays the values stored in localStorage // Log current settings for debugging
// No need to explicitly set the uiSettings and accessibilitySettings store values console.log('Current accessibility settings:', $accessibilitySettings);
// as settingsManager already does this during initialization console.log('Current UI settings:', $uiSettings);
}); });
</script> </script>
@@ -73,7 +79,12 @@
<div class="setting-item"> <div class="setting-item">
<span class="setting-name">Dark Mode</span> <span class="setting-name">Dark Mode</span>
<label class="switch"> <label class="switch">
<input type="checkbox" bind:checked={$uiSettings.isDarkMode}> <input type="checkbox"
bind:checked={$uiSettings.isDarkMode}
on:change={() => {
console.log('Dark mode changed to:', $uiSettings.isDarkMode);
settingsManager.saveSettings();
}}>
<span class="slider round"></span> <span class="slider round"></span>
</label> </label>
</div> </div>
@@ -153,7 +164,12 @@
<div class="setting-item"> <div class="setting-item">
<span class="setting-name">Text-to-Speech</span> <span class="setting-name">Text-to-Speech</span>
<label class="switch"> <label class="switch">
<input type="checkbox" bind:checked={$accessibilitySettings.textToSpeech}> <input type="checkbox"
bind:checked={$accessibilitySettings.textToSpeech}
on:change={() => {
console.log('Text-to-speech changed to:', $accessibilitySettings.textToSpeech);
settingsManager.saveSettings();
}}>
<span class="slider round"></span> <span class="slider round"></span>
</label> </label>
</div> </div>

View File

@@ -53,26 +53,52 @@ export class ProfileManager extends EventEmitter {
try { try {
const storedProfiles = localStorage.getItem(this.storageKey); const storedProfiles = localStorage.getItem(this.storageKey);
console.log('Retrieved from localStorage:', storedProfiles);
if (storedProfiles) { if (storedProfiles) {
this.profiles = JSON.parse(storedProfiles); 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); console.log('Loaded profiles from localStorage:', this.profiles);
if (this.profiles.length > 0) {
this.emit('profilesLoaded', this.profiles); 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');
}
} else { } else {
console.log('No profiles found in localStorage, creating default profile'); console.log('No profiles found in localStorage, creating default profile');
// Add a default profile if none exist }
// If we got here, we need to create a default profile
const defaultProfile = this.createDefaultProfile(); const defaultProfile = this.createDefaultProfile();
defaultProfile.name = 'Aardwolf MUD'; defaultProfile.name = 'Aardwolf MUD';
defaultProfile.host = 'aardmud.org'; defaultProfile.host = 'aardmud.org';
defaultProfile.port = 23; defaultProfile.port = 23;
// Clear profiles array and add the default
this.profiles = [];
this.addProfile(defaultProfile); this.addProfile(defaultProfile);
}
} catch (error) { } catch (error) {
console.error('Failed to load profiles from local storage:', error); console.error('Failed to load profiles from local storage:', error);
console.error('Error details:', error.message, error.stack);
// Add a default profile if there was an error // Add a default profile if there was an error
const defaultProfile = this.createDefaultProfile(); const defaultProfile = this.createDefaultProfile();
defaultProfile.name = 'Aardwolf MUD'; defaultProfile.name = 'Aardwolf MUD';
defaultProfile.host = 'aardmud.org'; defaultProfile.host = 'aardmud.org';
defaultProfile.port = 23; defaultProfile.port = 23;
// Clear profiles array and add the default
this.profiles = [];
this.addProfile(defaultProfile); this.addProfile(defaultProfile);
} }
} }
@@ -86,9 +112,21 @@ export class ProfileManager extends EventEmitter {
} }
try { try {
localStorage.setItem(this.storageKey, JSON.stringify(this.profiles)); // 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);
} catch (error) { } catch (error) {
console.error('Failed to save profiles to local storage:', error); console.error('Failed to save profiles to local storage:', error);
console.error('Error details:', error.message, error.stack);
} }
} }

View File

@@ -1,6 +1,5 @@
import { EventEmitter } from '$lib/utils/EventEmitter'; import { EventEmitter } from '$lib/utils/EventEmitter';
import { get } from 'svelte/store'; import { get, writable, type Writable } from 'svelte/store';
import { accessibilitySettings, uiSettings } from '$lib/stores/mudStore';
export interface Settings { export interface Settings {
accessibility: { accessibility: {
@@ -30,6 +29,11 @@ export interface Settings {
export class SettingsManager extends EventEmitter { export class SettingsManager extends EventEmitter {
private settings: Settings; private settings: Settings;
private readonly STORAGE_KEY = 'svelte-mud-settings'; private readonly STORAGE_KEY = 'svelte-mud-settings';
private initialized = false;
// Create our own stores rather than depending on the ones from mudStore
public accessibilitySettings: Writable<Settings['accessibility']>;
public uiSettings: Writable<Settings['ui']>;
constructor() { constructor() {
super(); super();
@@ -60,6 +64,31 @@ export class SettingsManager extends EventEmitter {
} }
}; };
// Create the stores with default values
this.accessibilitySettings = writable(this.settings.accessibility);
this.uiSettings = writable(this.settings.ui);
// Set up subscriptions to save settings when they change
this.accessibilitySettings.subscribe(value => {
// Skip during initialization
if (!this.initialized) return;
if (typeof window !== 'undefined') {
// Use a small timeout to batch multiple rapid changes
setTimeout(() => this.saveSettings(), 100);
}
});
this.uiSettings.subscribe(value => {
// Skip during initialization
if (!this.initialized) return;
if (typeof window !== 'undefined') {
// Use a small timeout to batch multiple rapid changes
setTimeout(() => this.saveSettings(), 100);
}
});
// Load settings from storage // Load settings from storage
this.loadSettings(); this.loadSettings();
} }
@@ -68,102 +97,87 @@ export class SettingsManager extends EventEmitter {
* Load settings from localStorage * Load settings from localStorage
*/ */
private loadSettings(): void { private loadSettings(): void {
if (typeof window === 'undefined') { // Skip during SSR
return; // Skip during SSR if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
console.log('SSR environment detected, skipping settings load');
return;
} }
try { try {
const storedSettings = localStorage.getItem(this.STORAGE_KEY); const storedSettings = localStorage.getItem(this.STORAGE_KEY);
if (storedSettings) { if (storedSettings) {
console.log('Retrieved settings from localStorage');
const parsedSettings = JSON.parse(storedSettings); const parsedSettings = JSON.parse(storedSettings);
// Merge with defaults to ensure all properties exist // Merge with defaults to ensure all properties exist
if (parsedSettings && typeof parsedSettings === 'object') {
// Update internal settings
this.settings = { this.settings = {
accessibility: { accessibility: {
...this.settings.accessibility, ...this.settings.accessibility,
...parsedSettings.accessibility ...(parsedSettings.accessibility || {})
}, },
ui: { ui: {
...this.settings.ui, ...this.settings.ui,
...parsedSettings.ui ...(parsedSettings.ui || {})
} }
}; };
console.log('Loaded settings from localStorage:', this.settings); console.log('Loaded settings from localStorage:', this.settings);
// Update Svelte stores with loaded settings // Set the stores without triggering the save callback
accessibilitySettings.set(this.settings.accessibility); this.accessibilitySettings.set(this.settings.accessibility);
uiSettings.set(this.settings.ui); this.uiSettings.set(this.settings.ui);
} else {
this.emit('settingsLoaded', this.settings); console.warn('Invalid settings format found in localStorage');
}
} else { } else {
console.log('No settings found in localStorage, using defaults'); 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);
} }
// Now that settings are loaded, mark as initialized
this.initialized = true;
// Notify listeners
this.emit('settingsLoaded', this.settings);
} catch (error) { } catch (error) {
console.error('Failed to load settings from localStorage:', error); console.error('Failed to load settings from localStorage:', error);
// Mark as initialized even on error
this.initialized = true;
} }
} }
/** // Save settings to localStorage
* Save settings to localStorage
*/
public saveSettings(): void { public saveSettings(): void {
if (typeof window === 'undefined') { if (typeof window === 'undefined' || !this.initialized) return;
return; // Skip during SSR
}
try { try {
// Get current values from stores // Get current values from stores
this.settings.accessibility = get(accessibilitySettings); this.settings.accessibility = get(this.accessibilitySettings);
this.settings.ui = get(uiSettings); this.settings.ui = get(this.uiSettings);
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.settings)); localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.settings));
console.log('Saved settings to localStorage:', this.settings); console.log('Saved settings to localStorage');
this.emit('settingsSaved', this.settings); this.emit('settingsSaved', this.settings);
} catch (error) { } catch (error) {
console.error('Failed to save settings to localStorage:', error); console.error('Failed to save settings:', error);
} }
} }
/** // Update methods
* Update accessibility settings
*/
public updateAccessibilitySettings(newSettings: Partial<Settings['accessibility']>): void { public updateAccessibilitySettings(newSettings: Partial<Settings['accessibility']>): void {
accessibilitySettings.update(current => { this.accessibilitySettings.update(current => ({...current, ...newSettings}));
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 { public updateUiSettings(newSettings: Partial<Settings['ui']>): void {
uiSettings.update(current => { this.uiSettings.update(current => ({...current, ...newSettings}));
const updated = { ...current, ...newSettings };
this.settings.ui = updated;
this.saveSettings();
return updated;
});
this.emit('uiSettingsUpdated', get(uiSettings));
} }
/** // Reset settings to defaults
* Reset settings to defaults
*/
public resetSettings(): void { public resetSettings(): void {
// Create default settings const defaults = {
const defaultSettings: Settings = {
accessibility: { accessibility: {
textToSpeech: false, textToSpeech: false,
highContrast: false, highContrast: false,
@@ -188,27 +202,19 @@ export class SettingsManager extends EventEmitter {
} }
}; };
// Update stores this.accessibilitySettings.set(defaults.accessibility);
accessibilitySettings.set(defaultSettings.accessibility); this.uiSettings.set(defaults.ui);
uiSettings.set(defaultSettings.ui); this.emit('settingsReset', defaults);
// Update internal settings and save
this.settings = defaultSettings;
this.saveSettings();
this.emit('settingsReset', defaultSettings);
} }
/** // Import/export
* Import settings from a JSON string
*/
public importSettings(json: string): void { public importSettings(json: string): void {
try { try {
const imported = JSON.parse(json); const imported = JSON.parse(json);
if (typeof imported === 'object' && imported !== null) { if (typeof imported === 'object' && imported !== null) {
// Create a valid settings object with defaults for missing properties // Create a valid settings object with defaults for missing properties
const newSettings: Settings = { const newSettings = {
accessibility: { accessibility: {
...this.settings.accessibility, ...this.settings.accessibility,
...(imported.accessibility || {}) ...(imported.accessibility || {})
@@ -220,12 +226,11 @@ export class SettingsManager extends EventEmitter {
}; };
// Update stores // Update stores
accessibilitySettings.set(newSettings.accessibility); this.accessibilitySettings.set(newSettings.accessibility);
uiSettings.set(newSettings.ui); this.uiSettings.set(newSettings.ui);
// Update internal settings and save // Update internal settings
this.settings = newSettings; this.settings = newSettings;
this.saveSettings();
this.emit('settingsImported', newSettings); this.emit('settingsImported', newSettings);
} }
@@ -235,13 +240,10 @@ export class SettingsManager extends EventEmitter {
} }
} }
/**
* Export settings to a JSON string
*/
public exportSettings(): string { public exportSettings(): string {
// Get current values from stores // Get current values from stores
this.settings.accessibility = get(accessibilitySettings); this.settings.accessibility = get(this.accessibilitySettings);
this.settings.ui = get(uiSettings); this.settings.ui = get(this.uiSettings);
return JSON.stringify(this.settings, null, 2); return JSON.stringify(this.settings, null, 2);
} }

View File

@@ -16,64 +16,27 @@ export const activeProfileId = writable<string | null>(null);
// Store for triggers // Store for triggers
export const triggers = writable<Trigger[]>([]); export const triggers = writable<Trigger[]>([]);
// Store for MUD output history // Store for MUD output history - keyed by profile ID
export const outputHistory = writable<{ export const outputHistory = writable<{
[profileId: string]: {
id: string; id: string;
text: string; text: string;
timestamp: number; timestamp: number;
isInput?: boolean; isInput?: boolean;
highlights?: { pattern: string; color: string; isRegex: boolean }[] highlights?: { pattern: string; color: string; isRegex: boolean }[]
}[]>([]); }[];
}>({});
// Store for connection status // Store for connection status
export const connectionStatus = writable<{ [key: string]: 'connected' | 'disconnected' | 'connecting' | 'error' }>({}); export const connectionStatus = writable<{ [key: string]: 'connected' | 'disconnected' | 'connecting' | 'error' }>({});
// Store for accessibility settings // Use the stores from SettingsManager
export const accessibilitySettings = writable({ export const accessibilitySettings = settingsManager.accessibilitySettings;
textToSpeech: false, export const uiSettings = settingsManager.uiSettings;
highContrast: false,
fontSize: 16,
lineSpacing: 1.2,
speechRate: 1,
speechPitch: 1,
speechVolume: 1,
interruptSpeechOnEnter: true // New setting for interrupting speech on Enter key
});
// Store for UI settings // Store for input history - keyed by profile ID
export const uiSettings = writable({ export const inputHistory = writable<{ [profileId: string]: string[] }>({});
isDarkMode: true, export const inputHistoryIndex = writable<{ [profileId: string]: number }>({});
showTimestamps: true,
showSidebar: true,
splitViewDirection: 'horizontal', // or 'vertical'
inputHistorySize: 100,
outputBufferSize: 1000,
ansiColor: true,
font: 'monospace',
debugGmcp: false, // Setting for GMCP debugging
globalVolume: 0.7 // Global volume control for sounds (0-1)
});
// Subscribe to settings changes to save to localStorage
accessibilitySettings.subscribe(value => {
// Skip during SSR
if (typeof window !== 'undefined') {
// Use a small timeout to batch multiple rapid changes
setTimeout(() => settingsManager.saveSettings(), 100);
}
});
uiSettings.subscribe(value => {
// Skip during SSR
if (typeof window !== 'undefined') {
// Use a small timeout to batch multiple rapid changes
setTimeout(() => settingsManager.saveSettings(), 100);
}
});
// Store for input history
export const inputHistory = writable<string[]>([]);
export const inputHistoryIndex = writable<number>(-1);
// Derived store for active profile // Derived store for active profile
export const activeProfile = derived( export const activeProfile = derived(
@@ -93,6 +56,44 @@ export const activeConnection = derived(
} }
); );
// Derived store for active output history
export const activeOutputHistory = derived(
[outputHistory, activeProfileId],
([$outputHistory, $activeProfileId]) => {
// If we have an active profile, use its history
if ($activeProfileId && $outputHistory[$activeProfileId]) {
return $outputHistory[$activeProfileId];
}
// Otherwise fall back to the default profile's history or an empty array
return $outputHistory['default'] || [];
}
);
// Derived store for active input history
export const activeInputHistory = derived(
[inputHistory, activeProfileId],
([$inputHistory, $activeProfileId]) => {
// If we have an active profile, use its history
if ($activeProfileId && $inputHistory[$activeProfileId]) {
return $inputHistory[$activeProfileId];
}
// Otherwise fall back to the default profile's history or an empty array
return $inputHistory['default'] || [];
}
);
// Derived store for active input history index
export const activeInputHistoryIndex = derived(
[inputHistoryIndex, activeProfileId],
([$inputHistoryIndex, $activeProfileId]) => {
const targetProfileId = $activeProfileId || 'default';
return $inputHistoryIndex[targetProfileId] !== undefined ?
$inputHistoryIndex[targetProfileId] : -1;
}
);
// Store for GMCP data // Store for GMCP data
export const gmcpData = writable<{ [module: string]: any }>({}); export const gmcpData = writable<{ [module: string]: any }>({});
@@ -101,7 +102,17 @@ export const gmcpDebugLog = writable<{ id: string; module: string; data: any; ti
// Helper functions // Helper functions
export function addToOutputHistory(text: string, isInput = false, highlights: { pattern: string; color: string; isRegex: boolean }[] = []) { export function addToOutputHistory(text: string, isInput = false, highlights: { pattern: string; color: string; isRegex: boolean }[] = []) {
outputHistory.update(history => { const profileId = get(activeProfileId);
outputHistory.update(allHistory => {
// Default profile ID for cases where there's no active profile
const targetProfileId = profileId || 'default';
// Initialize history for this profile if it doesn't exist
if (!allHistory[targetProfileId]) {
allHistory[targetProfileId] = [];
}
const maxSize = get(uiSettings).outputBufferSize; const maxSize = get(uiSettings).outputBufferSize;
const newItem = { const newItem = {
id: `output-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, id: `output-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
@@ -111,12 +122,15 @@ export function addToOutputHistory(text: string, isInput = false, highlights: {
highlights highlights
}; };
// Limit history size // Limit history size for this profile
const updatedHistory = [...history, newItem]; const updatedHistory = [...allHistory[targetProfileId], newItem];
if (updatedHistory.length > maxSize) { if (updatedHistory.length > maxSize) {
return updatedHistory.slice(updatedHistory.length - maxSize); allHistory[targetProfileId] = updatedHistory.slice(updatedHistory.length - maxSize);
} else {
allHistory[targetProfileId] = updatedHistory;
} }
return updatedHistory;
return allHistory;
}); });
} }
@@ -163,30 +177,56 @@ export function logGmcpMessage(module: string, data: any) {
} }
export function addToInputHistory(text: string) { export function addToInputHistory(text: string) {
inputHistory.update(history => { const profileId = get(activeProfileId);
inputHistory.update(allHistory => {
// Default profile ID for cases where there's no active profile
const targetProfileId = profileId || 'default';
// Initialize history for this profile if it doesn't exist
if (!allHistory[targetProfileId]) {
allHistory[targetProfileId] = [];
}
// Don't add empty strings or duplicates of the last command // Don't add empty strings or duplicates of the last command
if (!text || (history.length > 0 && history[history.length - 1] === text)) { if (!text || (allHistory[targetProfileId].length > 0 && allHistory[targetProfileId][allHistory[targetProfileId].length - 1] === text)) {
return history; return allHistory;
} }
const maxSize = get(uiSettings).inputHistorySize; const maxSize = get(uiSettings).inputHistorySize;
const updatedHistory = [...history, text]; const updatedHistory = [...allHistory[targetProfileId], text];
// Limit history size // Limit history size
if (updatedHistory.length > maxSize) { if (updatedHistory.length > maxSize) {
return updatedHistory.slice(updatedHistory.length - maxSize); allHistory[targetProfileId] = updatedHistory.slice(updatedHistory.length - maxSize);
} else {
allHistory[targetProfileId] = updatedHistory;
} }
return updatedHistory; return allHistory;
}); });
// Reset history index to point to the end // Reset history index to point to the end
inputHistoryIndex.set(-1); setInputHistoryIndex(profileId || 'default', -1);
}
// Helper to set input history index for a specific profile
function setInputHistoryIndex(profileId: string, index: number) {
inputHistoryIndex.update(indices => ({
...indices,
[profileId]: index
}));
} }
export function navigateInputHistory(direction: 'up' | 'down', currentInput: string) { export function navigateInputHistory(direction: 'up' | 'down', currentInput: string) {
const history = get(inputHistory); const profileId = get(activeProfileId);
let index = get(inputHistoryIndex); const targetProfileId = profileId || 'default';
const allHistory = get(inputHistory);
const history = allHistory[targetProfileId] || [];
const allIndices = get(inputHistoryIndex);
let index = allIndices[targetProfileId] !== undefined ? allIndices[targetProfileId] : -1;
if (history.length === 0) return currentInput; if (history.length === 0) return currentInput;
@@ -194,8 +234,13 @@ export function navigateInputHistory(direction: 'up' | 'down', currentInput: str
// If we're at the beginning of navigation, save the current input // If we're at the beginning of navigation, save the current input
if (index === -1) { if (index === -1) {
inputHistory.update(h => { inputHistory.update(h => {
if (currentInput && h[h.length - 1] !== currentInput) { // Initialize if needed
return [...h, currentInput]; if (!h[targetProfileId]) {
h[targetProfileId] = [];
}
if (currentInput && h[targetProfileId].length > 0 && h[targetProfileId][h[targetProfileId].length - 1] !== currentInput) {
h[targetProfileId] = [...h[targetProfileId], currentInput];
} }
return h; return h;
}); });
@@ -208,7 +253,8 @@ export function navigateInputHistory(direction: 'up' | 'down', currentInput: str
index = Math.max(-1, index - 1); index = Math.max(-1, index - 1);
} }
inputHistoryIndex.set(index); // Update the index for this profile
setInputHistoryIndex(targetProfileId, index);
// Return appropriate value from history or empty string // Return appropriate value from history or empty string
if (index === -1) { if (index === -1) {
@@ -219,7 +265,14 @@ export function navigateInputHistory(direction: 'up' | 'down', currentInput: str
} }
export function clearOutputHistory() { export function clearOutputHistory() {
outputHistory.set([]); const profileId = get(activeProfileId);
const targetProfileId = profileId || 'default';
outputHistory.update(allHistory => {
// Clear only the current profile's history
allHistory[targetProfileId] = [];
return allHistory;
});
} }
export function updateGmcpData(module: string, data: any) { export function updateGmcpData(module: string, data: any) {

View File

@@ -1,4 +1,4 @@
import { ProfileManager } from '$lib/profiles/ProfileManager'; // No need to import ProfileManager here
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { activeProfile } from '$lib/stores/mudStore'; import { activeProfile } from '$lib/stores/mudStore';
import type { MudProfile } from '$lib/profiles/ProfileManager'; import type { MudProfile } from '$lib/profiles/ProfileManager';
@@ -12,7 +12,6 @@ import TriggerEditor from '$lib/components/TriggerEditor.svelte';
*/ */
export class ModalHelper { export class ModalHelper {
private static modal: Modal | null = null; private static modal: Modal | null = null;
private static profileManager = new ProfileManager();
/** /**
* Show the full-featured profile editor modal * Show the full-featured profile editor modal
@@ -57,8 +56,31 @@ export class ModalHelper {
throw new Error('Failed to create modal instance'); throw new Error('Failed to create modal instance');
} }
// Get profile to edit or create new one // Get profile to edit or use the default empty profile
const profile = existingProfile || this.profileManager.createDefaultProfile(); const profile = existingProfile || {
id: `profile-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
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
}
};
const isNewProfile = !existingProfile; const isNewProfile = !existingProfile;
console.log('Setting up modal with profile:', profile); console.log('Setting up modal with profile:', profile);

View File

@@ -68,6 +68,8 @@
onMount(() => { onMount(() => {
console.log('Page mounted'); console.log('Page mounted');
try {
console.log('Starting initialization');
// Initialize profile manager // Initialize profile manager
profileManager = new ProfileManager(); profileManager = new ProfileManager();
console.log('Profile manager initialized'); console.log('Profile manager initialized');
@@ -76,10 +78,12 @@
triggerSystem = new TriggerSystem(); triggerSystem = new TriggerSystem();
console.log('Trigger system initialized'); console.log('Trigger system initialized');
// Load profiles // Load profiles first to ensure we have them
console.log('Loading profiles...');
loadProfiles(); loadProfiles();
console.log('Profiles loaded, count:', $profiles.length);
// Load triggers // Then load triggers
loadTriggers(); loadTriggers();
// Check URL parameters for debug mode // Check URL parameters for debug mode
@@ -100,6 +104,22 @@
console.log('Profile removed event detected'); console.log('Profile removed event detected');
loadProfiles(); loadProfiles();
}); });
// Ensure settings are properly loaded and initialized
console.log('Initial profiles state:', $profiles);
console.log('Active profile ID:', $activeProfileId);
// Make sure we have an active profile selected if any profiles exist
if ($profiles.length > 0 && !$activeProfileId) {
console.log('No active profile selected, setting first profile as active');
activeProfileId.set($profiles[0].id);
}
} catch (error) {
console.error('Error during page initialization:', error);
console.error('Error details:', error.message, error.stack);
// Provide user feedback about the error
addToOutputHistory(`Error initializing client: ${error.message}. Please reload the page.`);
}
}); });
// Check URL for debug parameters // Check URL for debug parameters
@@ -128,16 +148,66 @@
// Load profiles from manager // Load profiles from manager
function loadProfiles() { function loadProfiles() {
try {
if (!profileManager) {
console.error('Profile manager not initialized');
return;
}
// Get all profiles from the manager
const allProfiles = profileManager.getProfiles(); const allProfiles = profileManager.getProfiles();
console.log('Loaded profiles:', allProfiles); // Debug console.log('Loaded profiles from manager:', allProfiles);
// Create a default profile if none exist
if (allProfiles.length === 0) {
console.log('No profiles found, creating default profile');
const defaultProfile = profileManager.createDefaultProfile();
defaultProfile.name = 'Aardwolf MUD';
defaultProfile.host = 'aardmud.org';
defaultProfile.port = 23;
profileManager.addProfile(defaultProfile);
// Get profiles again after adding the default one
const updatedProfiles = profileManager.getProfiles();
console.log('Profiles after adding default:', updatedProfiles);
profiles.set(updatedProfiles);
// Set this as the active profile
if (updatedProfiles.length > 0) {
activeProfileId.set(updatedProfiles[0].id);
}
} else {
// Update the profiles store
profiles.set(allProfiles); profiles.set(allProfiles);
// Set active profile if available and not already set // Set active profile if available and not already set
if (!$activeProfileId && allProfiles.length > 0) { if (!$activeProfileId && allProfiles.length > 0) {
console.log('Setting active profile to:', allProfiles[0].id);
activeProfileId.set(allProfiles[0].id); activeProfileId.set(allProfiles[0].id);
} }
} }
// If active profile ID is set but no longer exists in profiles, reset it
if ($activeProfileId && !allProfiles.some(p => p.id === $activeProfileId)) {
console.log('Active profile no longer exists, resetting');
if (allProfiles.length > 0) {
activeProfileId.set(allProfiles[0].id);
} else {
activeProfileId.set(null);
}
}
// Add a default message to the output history so users see something
if (allProfiles.length > 0) {
addToOutputHistory('\nWelcome to SvelteMUD Client!\n\nSelect a profile from the Profiles tab and click Connect to begin.\n\n');
}
} catch (error) {
console.error('Error loading profiles:', error);
console.error('Error details:', error.message, error.stack);
addToOutputHistory(`Error loading profiles: ${error.message}`);
}
}
// Load triggers from system // Load triggers from system
function loadTriggers() { function loadTriggers() {
const allTriggers = triggerSystem.getTriggers(); const allTriggers = triggerSystem.getTriggers();