Files
svelte-mud/src/lib/stores/mudStore.ts

215 lines
6.3 KiB
TypeScript
Raw Normal View History

2025-04-21 14:12:36 +02:00
import { writable, derived, get } from 'svelte/store';
import type { MudProfile } from '$lib/profiles/ProfileManager';
import type { MudConnection } from '$lib/connection/MudConnection';
import type { Trigger } from '$lib/triggers/TriggerSystem';
// Store for active connections
export const connections = writable<{ [key: string]: MudConnection }>({});
// Store for profiles
export const profiles = writable<MudProfile[]>([]);
// Store for active profile ID
export const activeProfileId = writable<string | null>(null);
// Store for triggers
export const triggers = writable<Trigger[]>([]);
// Store for MUD output history
export const outputHistory = writable<{
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
});
// 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)
});
// Store for input history
export const inputHistory = writable<string[]>([]);
export const inputHistoryIndex = writable<number>(-1);
// Derived store for active profile
export const activeProfile = derived(
[profiles, activeProfileId],
([$profiles, $activeProfileId]) => {
if (!$activeProfileId) return null;
return $profiles.find(profile => profile.id === $activeProfileId) || null;
}
);
// Derived store for active connection
export const activeConnection = derived(
[connections, activeProfileId],
([$connections, $activeProfileId]) => {
if (!$activeProfileId) return null;
return $connections[$activeProfileId] || null;
}
);
// Store for GMCP data
export const gmcpData = writable<{ [module: string]: any }>({});
// Store for GMCP debug messages
export const gmcpDebugLog = writable<{ id: string; module: string; data: any; timestamp: number }[]>([]);
// Helper functions
export function addToOutputHistory(text: string, isInput = false, highlights: { pattern: string; color: string; isRegex: boolean }[] = []) {
outputHistory.update(history => {
const maxSize = get(uiSettings).outputBufferSize;
const newItem = {
id: `output-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
text,
timestamp: Date.now(),
isInput,
highlights
};
// Limit history size
const updatedHistory = [...history, newItem];
if (updatedHistory.length > maxSize) {
return updatedHistory.slice(updatedHistory.length - maxSize);
}
return updatedHistory;
});
}
/**
* Add GMCP message to debug log and possibly to output history if enabled
*/
export function logGmcpMessage(module: string, data: any) {
console.log('logGmcpMessage called for module:', module);
// Always add to debug log
gmcpDebugLog.update(log => {
const maxSize = 100; // Keep last 100 GMCP messages
const newItem = {
id: `gmcp-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
module,
data,
timestamp: Date.now()
};
const updatedLog = [...log, newItem];
if (updatedLog.length > maxSize) {
return updatedLog.slice(updatedLog.length - maxSize);
}
return updatedLog;
});
// Get the CURRENT state of the debugGmcp setting, not a snapshot
let currentSettings = get(uiSettings);
console.log('Current debugGmcp setting:', currentSettings.debugGmcp);
// If debug mode is enabled, also add to normal output
if (currentSettings.debugGmcp) {
console.log('GMCP debug is enabled, adding to output history');
const dataString = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);
const gmcpText = `[GMCP] ${module}: ${dataString}`;
addToOutputHistory(gmcpText, false, [
{ pattern: '\\[GMCP\\]', color: '#8be9fd', isRegex: true },
{ pattern: module, color: '#ff79c6', isRegex: false }
]);
} else {
console.log('GMCP debug is disabled, not adding to output history');
}
}
export function addToInputHistory(text: string) {
inputHistory.update(history => {
// Don't add empty strings or duplicates of the last command
if (!text || (history.length > 0 && history[history.length - 1] === text)) {
return history;
}
const maxSize = get(uiSettings).inputHistorySize;
const updatedHistory = [...history, text];
// Limit history size
if (updatedHistory.length > maxSize) {
return updatedHistory.slice(updatedHistory.length - maxSize);
}
return updatedHistory;
});
// Reset history index to point to the end
inputHistoryIndex.set(-1);
}
export function navigateInputHistory(direction: 'up' | 'down', currentInput: string) {
const history = get(inputHistory);
let index = get(inputHistoryIndex);
if (history.length === 0) return currentInput;
if (direction === 'up') {
// 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];
}
return h;
});
}
// Move up in history
index = Math.min(history.length - 1, index + 1);
} else if (direction === 'down') {
// Move down in history
index = Math.max(-1, index - 1);
}
inputHistoryIndex.set(index);
// Return appropriate value from history or empty string
if (index === -1) {
return '';
} else {
return history[history.length - 1 - index];
}
}
export function clearOutputHistory() {
outputHistory.set([]);
}
export function updateGmcpData(module: string, data: any) {
gmcpData.update(currentData => {
return {
...currentData,
[module]: data
};
});
}