Start fixing MDI
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
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 AnsiToHtml from 'ansi-to-html';
|
||||
import { AccessibilityManager } from '$lib/accessibility/AccessibilityManager';
|
||||
@@ -106,7 +106,7 @@
|
||||
addToInputHistory(currentInput);
|
||||
|
||||
// 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) {
|
||||
addToOutputHistory(`> ${currentInput}`, true);
|
||||
} else {
|
||||
@@ -146,7 +146,8 @@
|
||||
} else if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
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
|
||||
$: {
|
||||
if ($outputHistory) {
|
||||
if ($activeOutputHistory) {
|
||||
scrollToBottom();
|
||||
// Update message elements when output history changes
|
||||
setTimeout(updateMessageElements, 0);
|
||||
@@ -317,7 +318,7 @@
|
||||
tabindex="0"
|
||||
on:keydown={handleOutputKeyDown}
|
||||
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 -->
|
||||
{#if item.isInput}
|
||||
<div class="mud-terminal-line mud-input-line" tabindex="-1">
|
||||
|
||||
@@ -8,17 +8,48 @@
|
||||
export let profile: MudProfile;
|
||||
export let isNewProfile = false;
|
||||
|
||||
// Local state
|
||||
let localProfile = { ...profile };
|
||||
let autoLogin = { ...localProfile.autoLogin || { enabled: false, username: '', password: '', commands: [] } };
|
||||
// Initialize with defaults first, then merge with provided profile
|
||||
let defaultProfile = {
|
||||
id: isNewProfile ? `profile-${Date.now()}-${Math.random().toString(36).substring(2, 9)}` : (profile?.id || ''),
|
||||
name: 'New Profile',
|
||||
host: 'mud.example.com',
|
||||
port: 23,
|
||||
useSSL: false,
|
||||
ansiColor: true,
|
||||
autoLogin: {
|
||||
enabled: false,
|
||||
username: '',
|
||||
password: '',
|
||||
commands: []
|
||||
},
|
||||
accessibilityOptions: {
|
||||
textToSpeech: false,
|
||||
highContrast: false,
|
||||
speechRate: 1,
|
||||
speechPitch: 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 = { ...localProfile.accessibilityOptions || {
|
||||
textToSpeech: false,
|
||||
highContrast: false,
|
||||
speechRate: 1,
|
||||
speechPitch: 1,
|
||||
speechVolume: 1
|
||||
}};
|
||||
|
||||
let accessibilityOptions = {
|
||||
...defaultProfile.accessibilityOptions,
|
||||
...(localProfile.accessibilityOptions || {})
|
||||
};
|
||||
|
||||
// Handle form submission
|
||||
function handleSubmit() {
|
||||
|
||||
@@ -60,11 +60,17 @@
|
||||
input.click();
|
||||
}
|
||||
|
||||
// Helper function to explicitly save settings
|
||||
function saveSettings() {
|
||||
console.log('Explicitly saving settings');
|
||||
settingsManager.saveSettings();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// Ensure settings are loaded from localStorage on component mount
|
||||
// This ensures the UI displays the values stored in localStorage
|
||||
// No need to explicitly set the uiSettings and accessibilitySettings store values
|
||||
// as settingsManager already does this during initialization
|
||||
console.log('SettingsPanel mounted');
|
||||
// Log current settings for debugging
|
||||
console.log('Current accessibility settings:', $accessibilitySettings);
|
||||
console.log('Current UI settings:', $uiSettings);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -73,7 +79,12 @@
|
||||
<div class="setting-item">
|
||||
<span class="setting-name">Dark Mode</span>
|
||||
<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>
|
||||
</label>
|
||||
</div>
|
||||
@@ -153,7 +164,12 @@
|
||||
<div class="setting-item">
|
||||
<span class="setting-name">Text-to-Speech</span>
|
||||
<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>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -53,26 +53,52 @@ export class ProfileManager extends EventEmitter {
|
||||
|
||||
try {
|
||||
const storedProfiles = localStorage.getItem(this.storageKey);
|
||||
console.log('Retrieved from localStorage:', storedProfiles);
|
||||
|
||||
if (storedProfiles) {
|
||||
this.profiles = JSON.parse(storedProfiles);
|
||||
console.log('Loaded profiles from localStorage:', this.profiles);
|
||||
this.emit('profilesLoaded', this.profiles);
|
||||
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');
|
||||
}
|
||||
} else {
|
||||
console.log('No profiles found in localStorage, creating default profile');
|
||||
// Add a default profile if none exist
|
||||
const defaultProfile = this.createDefaultProfile();
|
||||
defaultProfile.name = 'Aardwolf MUD';
|
||||
defaultProfile.host = 'aardmud.org';
|
||||
defaultProfile.port = 23;
|
||||
this.addProfile(defaultProfile);
|
||||
}
|
||||
|
||||
// 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);
|
||||
} catch (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
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -86,9 +112,21 @@ export class ProfileManager extends EventEmitter {
|
||||
}
|
||||
|
||||
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) {
|
||||
console.error('Failed to save profiles to local storage:', error);
|
||||
console.error('Error details:', error.message, error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { EventEmitter } from '$lib/utils/EventEmitter';
|
||||
import { get } from 'svelte/store';
|
||||
import { accessibilitySettings, uiSettings } from '$lib/stores/mudStore';
|
||||
import { get, writable, type Writable } from 'svelte/store';
|
||||
|
||||
export interface Settings {
|
||||
accessibility: {
|
||||
@@ -30,6 +29,11 @@ export interface Settings {
|
||||
export class SettingsManager extends EventEmitter {
|
||||
private settings: 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() {
|
||||
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
|
||||
this.loadSettings();
|
||||
}
|
||||
@@ -68,102 +97,87 @@ export class SettingsManager extends EventEmitter {
|
||||
* Load settings from localStorage
|
||||
*/
|
||||
private loadSettings(): void {
|
||||
if (typeof window === 'undefined') {
|
||||
return; // Skip during SSR
|
||||
// Skip during SSR
|
||||
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
||||
console.log('SSR environment detected, skipping settings load');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const storedSettings = localStorage.getItem(this.STORAGE_KEY);
|
||||
|
||||
if (storedSettings) {
|
||||
console.log('Retrieved settings from localStorage');
|
||||
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);
|
||||
if (parsedSettings && typeof parsedSettings === 'object') {
|
||||
// Update internal settings
|
||||
this.settings = {
|
||||
accessibility: {
|
||||
...this.settings.accessibility,
|
||||
...(parsedSettings.accessibility || {})
|
||||
},
|
||||
ui: {
|
||||
...this.settings.ui,
|
||||
...(parsedSettings.ui || {})
|
||||
}
|
||||
};
|
||||
|
||||
console.log('Loaded settings from localStorage:', this.settings);
|
||||
|
||||
// Set the stores without triggering the save callback
|
||||
this.accessibilitySettings.set(this.settings.accessibility);
|
||||
this.uiSettings.set(this.settings.ui);
|
||||
} else {
|
||||
console.warn('Invalid settings format found in localStorage');
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
|
||||
// Now that settings are loaded, mark as initialized
|
||||
this.initialized = true;
|
||||
|
||||
// Notify listeners
|
||||
this.emit('settingsLoaded', this.settings);
|
||||
} catch (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 {
|
||||
if (typeof window === 'undefined') {
|
||||
return; // Skip during SSR
|
||||
}
|
||||
if (typeof window === 'undefined' || !this.initialized) return;
|
||||
|
||||
try {
|
||||
// Get current values from stores
|
||||
this.settings.accessibility = get(accessibilitySettings);
|
||||
this.settings.ui = get(uiSettings);
|
||||
this.settings.accessibility = get(this.accessibilitySettings);
|
||||
this.settings.ui = get(this.uiSettings);
|
||||
|
||||
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);
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings to localStorage:', error);
|
||||
console.error('Failed to save settings:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update accessibility settings
|
||||
*/
|
||||
// Update methods
|
||||
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));
|
||||
this.accessibilitySettings.update(current => ({...current, ...newSettings}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
this.uiSettings.update(current => ({...current, ...newSettings}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset settings to defaults
|
||||
*/
|
||||
// Reset settings to defaults
|
||||
public resetSettings(): void {
|
||||
// Create default settings
|
||||
const defaultSettings: Settings = {
|
||||
const defaults = {
|
||||
accessibility: {
|
||||
textToSpeech: false,
|
||||
highContrast: false,
|
||||
@@ -188,27 +202,19 @@ export class SettingsManager extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
// Update stores
|
||||
accessibilitySettings.set(defaultSettings.accessibility);
|
||||
uiSettings.set(defaultSettings.ui);
|
||||
|
||||
// Update internal settings and save
|
||||
this.settings = defaultSettings;
|
||||
this.saveSettings();
|
||||
|
||||
this.emit('settingsReset', defaultSettings);
|
||||
this.accessibilitySettings.set(defaults.accessibility);
|
||||
this.uiSettings.set(defaults.ui);
|
||||
this.emit('settingsReset', defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings from a JSON string
|
||||
*/
|
||||
// Import/export
|
||||
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 = {
|
||||
const newSettings = {
|
||||
accessibility: {
|
||||
...this.settings.accessibility,
|
||||
...(imported.accessibility || {})
|
||||
@@ -220,12 +226,11 @@ export class SettingsManager extends EventEmitter {
|
||||
};
|
||||
|
||||
// Update stores
|
||||
accessibilitySettings.set(newSettings.accessibility);
|
||||
uiSettings.set(newSettings.ui);
|
||||
this.accessibilitySettings.set(newSettings.accessibility);
|
||||
this.uiSettings.set(newSettings.ui);
|
||||
|
||||
// Update internal settings and save
|
||||
// Update internal settings
|
||||
this.settings = newSettings;
|
||||
this.saveSettings();
|
||||
|
||||
this.emit('settingsImported', newSettings);
|
||||
}
|
||||
@@ -235,13 +240,10 @@ export class SettingsManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export settings to a JSON string
|
||||
*/
|
||||
public exportSettings(): string {
|
||||
// Get current values from stores
|
||||
this.settings.accessibility = get(accessibilitySettings);
|
||||
this.settings.ui = get(uiSettings);
|
||||
this.settings.accessibility = get(this.accessibilitySettings);
|
||||
this.settings.ui = get(this.uiSettings);
|
||||
|
||||
return JSON.stringify(this.settings, null, 2);
|
||||
}
|
||||
|
||||
@@ -16,64 +16,27 @@ export const activeProfileId = writable<string | null>(null);
|
||||
// Store for triggers
|
||||
export const triggers = writable<Trigger[]>([]);
|
||||
|
||||
// Store for MUD output history
|
||||
// Store for MUD output history - keyed by profile ID
|
||||
export const outputHistory = writable<{
|
||||
id: string;
|
||||
text: string;
|
||||
timestamp: number;
|
||||
isInput?: boolean;
|
||||
highlights?: { pattern: string; color: string; isRegex: boolean }[]
|
||||
}[]>([]);
|
||||
[profileId: string]: {
|
||||
id: string;
|
||||
text: string;
|
||||
timestamp: number;
|
||||
isInput?: boolean;
|
||||
highlights?: { pattern: string; color: string; isRegex: boolean }[]
|
||||
}[];
|
||||
}>({});
|
||||
|
||||
// Store for connection status
|
||||
export const connectionStatus = writable<{ [key: string]: 'connected' | 'disconnected' | 'connecting' | 'error' }>({});
|
||||
|
||||
// Store for accessibility settings
|
||||
export const accessibilitySettings = writable({
|
||||
textToSpeech: false,
|
||||
highContrast: false,
|
||||
fontSize: 16,
|
||||
lineSpacing: 1.2,
|
||||
speechRate: 1,
|
||||
speechPitch: 1,
|
||||
speechVolume: 1,
|
||||
interruptSpeechOnEnter: true // New setting for interrupting speech on Enter key
|
||||
});
|
||||
// Use the stores from SettingsManager
|
||||
export const accessibilitySettings = settingsManager.accessibilitySettings;
|
||||
export const uiSettings = settingsManager.uiSettings;
|
||||
|
||||
// Store for UI settings
|
||||
export const uiSettings = writable({
|
||||
isDarkMode: true,
|
||||
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);
|
||||
// Store for input history - keyed by profile ID
|
||||
export const inputHistory = writable<{ [profileId: string]: string[] }>({});
|
||||
export const inputHistoryIndex = writable<{ [profileId: string]: number }>({});
|
||||
|
||||
// Derived store for active profile
|
||||
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
|
||||
export const gmcpData = writable<{ [module: string]: any }>({});
|
||||
|
||||
@@ -101,7 +102,17 @@ export const gmcpDebugLog = writable<{ id: string; module: string; data: any; ti
|
||||
|
||||
// Helper functions
|
||||
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 newItem = {
|
||||
id: `output-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
|
||||
@@ -111,12 +122,15 @@ export function addToOutputHistory(text: string, isInput = false, highlights: {
|
||||
highlights
|
||||
};
|
||||
|
||||
// Limit history size
|
||||
const updatedHistory = [...history, newItem];
|
||||
// Limit history size for this profile
|
||||
const updatedHistory = [...allHistory[targetProfileId], newItem];
|
||||
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) {
|
||||
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
|
||||
if (!text || (history.length > 0 && history[history.length - 1] === text)) {
|
||||
return history;
|
||||
if (!text || (allHistory[targetProfileId].length > 0 && allHistory[targetProfileId][allHistory[targetProfileId].length - 1] === text)) {
|
||||
return allHistory;
|
||||
}
|
||||
|
||||
const maxSize = get(uiSettings).inputHistorySize;
|
||||
const updatedHistory = [...history, text];
|
||||
const updatedHistory = [...allHistory[targetProfileId], text];
|
||||
|
||||
// Limit history size
|
||||
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
|
||||
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) {
|
||||
const history = get(inputHistory);
|
||||
let index = get(inputHistoryIndex);
|
||||
const profileId = get(activeProfileId);
|
||||
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;
|
||||
|
||||
@@ -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 (index === -1) {
|
||||
inputHistory.update(h => {
|
||||
if (currentInput && h[h.length - 1] !== currentInput) {
|
||||
return [...h, currentInput];
|
||||
// Initialize if needed
|
||||
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;
|
||||
});
|
||||
@@ -208,7 +253,8 @@ export function navigateInputHistory(direction: 'up' | 'down', currentInput: str
|
||||
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
|
||||
if (index === -1) {
|
||||
@@ -219,7 +265,14 @@ export function navigateInputHistory(direction: 'up' | 'down', currentInput: str
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProfileManager } from '$lib/profiles/ProfileManager';
|
||||
// No need to import ProfileManager here
|
||||
import { get } from 'svelte/store';
|
||||
import { activeProfile } from '$lib/stores/mudStore';
|
||||
import type { MudProfile } from '$lib/profiles/ProfileManager';
|
||||
@@ -12,7 +12,6 @@ import TriggerEditor from '$lib/components/TriggerEditor.svelte';
|
||||
*/
|
||||
export class ModalHelper {
|
||||
private static modal: Modal | null = null;
|
||||
private static profileManager = new ProfileManager();
|
||||
|
||||
/**
|
||||
* Show the full-featured profile editor modal
|
||||
@@ -57,8 +56,31 @@ export class ModalHelper {
|
||||
throw new Error('Failed to create modal instance');
|
||||
}
|
||||
|
||||
// Get profile to edit or create new one
|
||||
const profile = existingProfile || this.profileManager.createDefaultProfile();
|
||||
// Get profile to edit or use the default empty profile
|
||||
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;
|
||||
|
||||
console.log('Setting up modal with profile:', profile);
|
||||
|
||||
@@ -68,38 +68,58 @@
|
||||
|
||||
onMount(() => {
|
||||
console.log('Page mounted');
|
||||
// Initialize profile manager
|
||||
profileManager = new ProfileManager();
|
||||
console.log('Profile manager initialized');
|
||||
|
||||
// Initialize trigger system
|
||||
triggerSystem = new TriggerSystem();
|
||||
console.log('Trigger system initialized');
|
||||
|
||||
// Load profiles
|
||||
loadProfiles();
|
||||
|
||||
// Load triggers
|
||||
loadTriggers();
|
||||
|
||||
// Check URL parameters for debug mode
|
||||
checkUrlParams();
|
||||
|
||||
// Subscribe to profile manager events for real-time updates
|
||||
profileManager.on('profileAdded', () => {
|
||||
console.log('Profile added event detected');
|
||||
try {
|
||||
console.log('Starting initialization');
|
||||
// Initialize profile manager
|
||||
profileManager = new ProfileManager();
|
||||
console.log('Profile manager initialized');
|
||||
|
||||
// Initialize trigger system
|
||||
triggerSystem = new TriggerSystem();
|
||||
console.log('Trigger system initialized');
|
||||
|
||||
// Load profiles first to ensure we have them
|
||||
console.log('Loading profiles...');
|
||||
loadProfiles();
|
||||
});
|
||||
|
||||
profileManager.on('profileUpdated', () => {
|
||||
console.log('Profile updated event detected');
|
||||
loadProfiles();
|
||||
});
|
||||
|
||||
profileManager.on('profileRemoved', () => {
|
||||
console.log('Profile removed event detected');
|
||||
loadProfiles();
|
||||
});
|
||||
console.log('Profiles loaded, count:', $profiles.length);
|
||||
|
||||
// Then load triggers
|
||||
loadTriggers();
|
||||
|
||||
// Check URL parameters for debug mode
|
||||
checkUrlParams();
|
||||
|
||||
// Subscribe to profile manager events for real-time updates
|
||||
profileManager.on('profileAdded', () => {
|
||||
console.log('Profile added event detected');
|
||||
loadProfiles();
|
||||
});
|
||||
|
||||
profileManager.on('profileUpdated', () => {
|
||||
console.log('Profile updated event detected');
|
||||
loadProfiles();
|
||||
});
|
||||
|
||||
profileManager.on('profileRemoved', () => {
|
||||
console.log('Profile removed event detected');
|
||||
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
|
||||
@@ -128,13 +148,63 @@
|
||||
|
||||
// Load profiles from manager
|
||||
function loadProfiles() {
|
||||
const allProfiles = profileManager.getProfiles();
|
||||
console.log('Loaded profiles:', allProfiles); // Debug
|
||||
profiles.set(allProfiles);
|
||||
|
||||
// Set active profile if available and not already set
|
||||
if (!$activeProfileId && allProfiles.length > 0) {
|
||||
activeProfileId.set(allProfiles[0].id);
|
||||
try {
|
||||
if (!profileManager) {
|
||||
console.error('Profile manager not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all profiles from the manager
|
||||
const allProfiles = profileManager.getProfiles();
|
||||
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);
|
||||
|
||||
// Set active profile if available and not already set
|
||||
if (!$activeProfileId && allProfiles.length > 0) {
|
||||
console.log('Setting active profile to:', 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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user