# MUD Terminal Performance Optimization Summary ## Problem Identified The MUD terminal component had severe performance issues when the message history grew large due to expensive text processing operations being performed in the Svelte template on every render cycle. ### Key Issues: 1. **Expensive re-computation on every render**: `processAnsi()`, `applyHighlights()`, and `splitIntoLines()` were called for every message on every component re-render 2. **Complex text processing in template**: ANSI color processing, regex highlighting, and line splitting happened in the template using `{@const}` blocks 3. **Inefficient reactive statements**: Triggered unnecessary DOM work on every history change 4. **No caching**: The same text processing was repeated multiple times for the same content ## Optimizations Implemented ### 1. **Pre-processing in Store Layer** - Created `textProcessing.ts` utility with all text processing logic - Moved expensive operations to happen once when messages are added to store - Added `processedOutputHistory` store that contains pre-processed messages ### 2. **Intelligent Caching System** - Implemented `ProcessedMessage` interface with built-in cache - Cache keyed by UI settings that affect rendering (e.g., ANSI color enabled/disabled) - Messages are re-processed only when relevant settings change - Uses `Map` for efficient cache lookups ### 3. **Flattened Renderable Lines Store** - Created `activeRenderableLines` derived store that provides a flat array of all renderable lines - Eliminates complex nested logic in template - Each line has pre-computed properties (content, styling, metadata) - Single `{#each}` loop instead of nested processing ### 4. **Optimized Template Rendering** ```svelte {#each safeOutputHistory as item (item.id)} {#if item.isInput} {:else} {@const processedContent = applyHighlights(processAnsi(item.text), item.highlights || [])} {@const lines = splitIntoLines(processedContent)} {#if lines.length <= 1} {:else} {#each lines as line, lineIndex} {/each} {/if} {/if} {/each} {#each safeRenderableLines as line (line.id)}
{#if $uiSettings.showTimestamps && line.lineIndex === 0} [{formatTimestamp(line.timestamp)}] {/if}
{@html line.content}
{/each} ``` ### 5. **Optimized Reactive Statements** - Replaced `setTimeout()` with `Promise.resolve().then()` for better microtask scheduling - More targeted reactive updates that only trigger when necessary - Removed redundant reactive blocks ### 6. **Store Architecture Improvements** - Separated raw message storage from processed message storage - Made `addToOutputHistory()` handle both raw and processed storage - Ensured cache consistency when clearing history ## Performance Benefits ### Before Optimization: - **O(n)** text processing operations on every render for **n** messages - Multiple expensive regex operations per message per render - ANSI-to-HTML conversion happening repeatedly - Complex DOM operations during each reactive update ### After Optimization: - **O(1)** amortized cost per message (processing happens once) - **O(1)** cache lookups for repeated operations - Text processing only when messages are added or settings change - Simple, flat DOM structure with minimal reactive overhead ## Technical Implementation Details ### New Files Created: - `src/lib/utils/textProcessing.ts` - Centralized text processing utilities ### Modified Files: - `src/lib/stores/mudStore.ts` - Added processed stores and caching - `src/lib/components/MudTerminal.svelte` - Simplified template and removed processing functions ### Key Data Structures: ```typescript interface ProcessedMessage { id: string; originalText: string; timestamp: number; isInput: boolean; highlights: HighlightRule[]; processedContent: string; lines: ProcessedLine[]; processedCache: Map; // Cache by UI settings } interface ProcessedLine { id: string; content: string; // Pre-processed HTML content isSubline: boolean; // For indentation parentId: string; // Reference to parent message lineIndex: number; // Position in parent message } ``` ## Leveraging Svelte's Strengths The optimization takes full advantage of Svelte's reactive system: 1. **Derived Stores**: Used for computed values that automatically update when dependencies change 2. **Keyed Each Blocks**: Ensures efficient DOM updates with `{#each items as item (item.id)}` 3. **Conditional Classes**: Uses `class:name={condition}` for efficient class toggling 4. **Reactive Declarations**: Optimized `$:` statements that only run when necessary 5. **Store Composition**: Layered stores that build upon each other efficiently ## Expected Performance Gains For a terminal with 1000+ messages: - **Before**: ~1000 × (ANSI processing + regex highlighting + line splitting) per render - **After**: ~0 processing per render (cached results) - **Memory**: Slightly higher due to caching, but with configurable limits - **Responsiveness**: Should feel instant even with large message histories The optimization maintains all existing functionality while dramatically improving performance, especially as message history grows.