import React, { useState, useCallback, useEffect } from 'react'; import type { Settings } from '../types'; import { TerminalPreview } from './TerminalPreview'; import { useContextPreview } from '../hooks/useContextPreview'; interface ContextSettingsModalProps { isOpen: boolean; onClose: () => void; settings: Settings; onSave: (settings: Settings) => void; isSaving: boolean; saveStatus: string; } // Simple debounce helper function debounce any>(fn: T, ms: number): T { let timeoutId: NodeJS.Timeout; return ((...args: any[]) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn(...args), ms); }) as T; } // Collapsible section component function CollapsibleSection({ title, description, children, defaultOpen = true }: { title: string; description?: string; children: React.ReactNode; defaultOpen?: boolean; }) { const [isOpen, setIsOpen] = useState(defaultOpen); return (
{isOpen &&
{children}
}
); } // Chip group with select all/none function ChipGroup({ label, options, selectedValues, onToggle, onSelectAll, onSelectNone }: { label: string; options: string[]; selectedValues: string[]; onToggle: (value: string) => void; onSelectAll: () => void; onSelectNone: () => void; }) { const allSelected = options.every(opt => selectedValues.includes(opt)); const noneSelected = options.every(opt => !selectedValues.includes(opt)); return (
{label}
{options.map(option => ( ))}
); } // Form field with optional tooltip function FormField({ label, tooltip, children }: { label: string; tooltip?: string; children: React.ReactNode; }) { return (
{children}
); } // Toggle switch component function ToggleSwitch({ id, label, description, checked, onChange, disabled }: { id: string; label: string; description?: string; checked: boolean; onChange: (checked: boolean) => void; disabled?: boolean; }) { return (
{description && {description}}
); } export function ContextSettingsModal({ isOpen, onClose, settings, onSave, isSaving, saveStatus }: ContextSettingsModalProps) { const [formState, setFormState] = useState(settings); // Create debounced save function const debouncedSave = useCallback( debounce((newSettings: Settings) => { onSave(newSettings); }, 300), [onSave] ); // Update form state when settings prop changes useEffect(() => { setFormState(settings); }, [settings]); // Get context preview based on current form state const { preview, isLoading, error, projects, selectedProject, setSelectedProject } = useContextPreview(formState); const updateSetting = useCallback((key: keyof Settings, value: string) => { const newState = { ...formState, [key]: value }; setFormState(newState); debouncedSave(newState); }, [formState, debouncedSave]); const toggleBoolean = useCallback((key: keyof Settings) => { const currentValue = formState[key]; const newValue = currentValue === 'true' ? 'false' : 'true'; updateSetting(key, newValue); }, [formState, updateSetting]); const toggleArrayValue = useCallback((key: keyof Settings, value: string) => { const currentValue = formState[key] || ''; const currentArray = currentValue ? currentValue.split(',') : []; const newArray = currentArray.includes(value) ? currentArray.filter(v => v !== value) : [...currentArray, value]; updateSetting(key, newArray.join(',')); }, [formState, updateSetting]); const getArrayValues = useCallback((key: keyof Settings): string[] => { const currentValue = formState[key] || ''; return currentValue ? currentValue.split(',') : []; }, [formState]); const setAllArrayValues = useCallback((key: keyof Settings, values: string[]) => { updateSetting(key, values.join(',')); }, [updateSetting]); // Handle ESC key useEffect(() => { const handleEsc = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; if (isOpen) { window.addEventListener('keydown', handleEsc); return () => window.removeEventListener('keydown', handleEsc); } }, [isOpen, onClose]); if (!isOpen) return null; const observationTypes = ['bugfix', 'feature', 'refactor', 'discovery', 'decision', 'change']; const observationConcepts = ['how-it-works', 'why-it-exists', 'what-changed', 'problem-solution', 'gotcha', 'pattern', 'trade-off']; return (
e.stopPropagation()}> {/* Header */}

Settings

{/* Body - 2 columns */}
{/* Left column - Terminal Preview */}
{error ? (
Error loading preview: {error}
) : ( )}
{/* Right column - Settings Panel */}
{/* Section 1: Loading */} updateSetting('CLAUDE_MEM_CONTEXT_OBSERVATIONS', e.target.value)} /> updateSetting('CLAUDE_MEM_CONTEXT_SESSION_COUNT', e.target.value)} /> {/* Section 2: Filters */} toggleArrayValue('CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES', value)} onSelectAll={() => setAllArrayValues('CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES', observationTypes)} onSelectNone={() => setAllArrayValues('CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES', [])} /> toggleArrayValue('CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS', value)} onSelectAll={() => setAllArrayValues('CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS', observationConcepts)} onSelectNone={() => setAllArrayValues('CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS', [])} /> {/* Section 3: Display */}
Full Observations updateSetting('CLAUDE_MEM_CONTEXT_FULL_COUNT', e.target.value)} />
Token Economics
toggleBoolean('CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS')} /> toggleBoolean('CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS')} /> toggleBoolean('CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT')} />
{/* Section 4: Advanced */} updateSetting('CLAUDE_MEM_WORKER_PORT', e.target.value)} />
toggleBoolean('CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY')} /> toggleBoolean('CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE')} />
); }