Refactor observation handling: centralize constants and improve context settings
- Introduced `observation-metadata.ts` to define valid observation types and concepts, along with their corresponding emoji mappings. - Updated `context-hook.ts` to utilize new constants for observation types and concepts, enhancing maintainability. - Refactored `worker-service.ts` to validate observation types and concepts against the new centralized constants. - Consolidated settings management in `Sidebar.tsx` to streamline state handling for context settings. - Improved error handling and logging for context loading failures.
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Observation metadata constants
|
||||||
|
* Shared across hooks, worker service, and UI components
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid observation types
|
||||||
|
*/
|
||||||
|
export const OBSERVATION_TYPES = [
|
||||||
|
'bugfix',
|
||||||
|
'feature',
|
||||||
|
'refactor',
|
||||||
|
'discovery',
|
||||||
|
'decision',
|
||||||
|
'change'
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type ObservationType = typeof OBSERVATION_TYPES[number];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid observation concepts
|
||||||
|
*/
|
||||||
|
export const OBSERVATION_CONCEPTS = [
|
||||||
|
'how-it-works',
|
||||||
|
'why-it-exists',
|
||||||
|
'what-changed',
|
||||||
|
'problem-solution',
|
||||||
|
'gotcha',
|
||||||
|
'pattern',
|
||||||
|
'trade-off'
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type ObservationConcept = typeof OBSERVATION_CONCEPTS[number];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map observation types to emoji icons
|
||||||
|
*/
|
||||||
|
export const TYPE_ICON_MAP: Record<ObservationType | 'session-request', string> = {
|
||||||
|
'bugfix': '🔴',
|
||||||
|
'feature': '🟣',
|
||||||
|
'refactor': '🔄',
|
||||||
|
'change': '✅',
|
||||||
|
'discovery': '🔵',
|
||||||
|
'decision': '⚖️',
|
||||||
|
'session-request': '🎯'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map observation types to work emoji (for token display)
|
||||||
|
*/
|
||||||
|
export const TYPE_WORK_EMOJI_MAP: Record<ObservationType, string> = {
|
||||||
|
'discovery': '🔍', // research/exploration
|
||||||
|
'change': '🛠️', // building/modifying
|
||||||
|
'feature': '🛠️', // building/modifying
|
||||||
|
'bugfix': '🛠️', // building/modifying
|
||||||
|
'refactor': '🛠️', // building/modifying
|
||||||
|
'decision': '⚖️' // decision-making
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default observation types (comma-separated string for settings)
|
||||||
|
*/
|
||||||
|
export const DEFAULT_OBSERVATION_TYPES_STRING = OBSERVATION_TYPES.join(',');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default observation concepts (comma-separated string for settings)
|
||||||
|
*/
|
||||||
|
export const DEFAULT_OBSERVATION_CONCEPTS_STRING = OBSERVATION_CONCEPTS.join(',');
|
||||||
+35
-82
@@ -10,6 +10,15 @@ import { stdin } from 'process';
|
|||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
||||||
|
import {
|
||||||
|
OBSERVATION_TYPES,
|
||||||
|
OBSERVATION_CONCEPTS,
|
||||||
|
TYPE_ICON_MAP,
|
||||||
|
TYPE_WORK_EMOJI_MAP,
|
||||||
|
DEFAULT_OBSERVATION_TYPES_STRING,
|
||||||
|
DEFAULT_OBSERVATION_CONCEPTS_STRING
|
||||||
|
} from '../constants/observation-metadata.js';
|
||||||
|
import { logger } from '../utils/logger.js';
|
||||||
|
|
||||||
// Get __dirname equivalent in ESM
|
// Get __dirname equivalent in ESM
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -19,28 +28,6 @@ const __dirname = dirname(__filename);
|
|||||||
// From src/hooks/ we need to go up to plugin root: ../../
|
// From src/hooks/ we need to go up to plugin root: ../../
|
||||||
const VERSION_MARKER_PATH = path.join(__dirname, '../../.install-version');
|
const VERSION_MARKER_PATH = path.join(__dirname, '../../.install-version');
|
||||||
|
|
||||||
/**
|
|
||||||
* Get context depth from settings
|
|
||||||
* Priority: ~/.claude/settings.json > env var > default
|
|
||||||
*/
|
|
||||||
function getContextDepth(): number {
|
|
||||||
try {
|
|
||||||
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
|
||||||
if (existsSync(settingsPath)) {
|
|
||||||
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
||||||
if (settings.env?.CLAUDE_MEM_CONTEXT_OBSERVATIONS) {
|
|
||||||
const count = parseInt(settings.env.CLAUDE_MEM_CONTEXT_OBSERVATIONS, 10);
|
|
||||||
if (!isNaN(count) && count > 0) {
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Fall through to env var or default
|
|
||||||
}
|
|
||||||
return parseInt(process.env.CLAUDE_MEM_CONTEXT_OBSERVATIONS || '50', 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ContextConfig {
|
interface ContextConfig {
|
||||||
// Display counts
|
// Display counts
|
||||||
totalObservationCount: number;
|
totalObservationCount: number;
|
||||||
@@ -76,8 +63,8 @@ function loadContextConfig(): ContextConfig {
|
|||||||
showWorkTokens: true,
|
showWorkTokens: true,
|
||||||
showSavingsAmount: true,
|
showSavingsAmount: true,
|
||||||
showSavingsPercent: true,
|
showSavingsPercent: true,
|
||||||
observationTypes: new Set(['bugfix', 'feature', 'refactor', 'discovery', 'decision', 'change']),
|
observationTypes: new Set(OBSERVATION_TYPES),
|
||||||
observationConcepts: new Set(['how-it-works', 'why-it-exists', 'what-changed', 'problem-solution', 'gotcha', 'pattern', 'trade-off']),
|
observationConcepts: new Set(OBSERVATION_CONCEPTS),
|
||||||
fullObservationField: 'narrative' as const,
|
fullObservationField: 'narrative' as const,
|
||||||
showLastSummary: true,
|
showLastSummary: true,
|
||||||
showLastMessage: false,
|
showLastMessage: false,
|
||||||
@@ -99,25 +86,24 @@ function loadContextConfig(): ContextConfig {
|
|||||||
showSavingsAmount: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT !== 'false',
|
showSavingsAmount: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT !== 'false',
|
||||||
showSavingsPercent: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT !== 'false',
|
showSavingsPercent: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT !== 'false',
|
||||||
observationTypes: new Set(
|
observationTypes: new Set(
|
||||||
(env.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || 'bugfix,feature,refactor,discovery,decision,change')
|
(env.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_OBSERVATION_TYPES_STRING)
|
||||||
.split(',').map((t: string) => t.trim()).filter(Boolean)
|
.split(',').map((t: string) => t.trim()).filter(Boolean)
|
||||||
),
|
),
|
||||||
observationConcepts: new Set(
|
observationConcepts: new Set(
|
||||||
(env.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || 'how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off')
|
(env.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_OBSERVATION_CONCEPTS_STRING)
|
||||||
.split(',').map((c: string) => c.trim()).filter(Boolean)
|
.split(',').map((c: string) => c.trim()).filter(Boolean)
|
||||||
),
|
),
|
||||||
fullObservationField: (env.CLAUDE_MEM_CONTEXT_FULL_FIELD || 'narrative') as 'narrative' | 'facts',
|
fullObservationField: (env.CLAUDE_MEM_CONTEXT_FULL_FIELD || 'narrative') as 'narrative' | 'facts',
|
||||||
showLastSummary: env.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY !== 'false',
|
showLastSummary: env.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY !== 'false',
|
||||||
showLastMessage: env.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE === 'true',
|
showLastMessage: env.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE === 'true',
|
||||||
};
|
};
|
||||||
} catch {
|
} catch (error) {
|
||||||
|
logger.warn('CONTEXT', 'Failed to load context settings, using defaults', {}, error as Error);
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration: Read from settings.json or environment
|
// Configuration constants
|
||||||
const DISPLAY_OBSERVATION_COUNT = getContextDepth();
|
|
||||||
const DISPLAY_SESSION_COUNT = 10; // Recent sessions for timeline context
|
|
||||||
const CHARS_PER_TOKEN_ESTIMATE = 4; // Rough estimate for token counting
|
const CHARS_PER_TOKEN_ESTIMATE = 4; // Rough estimate for token counting
|
||||||
const SUMMARY_LOOKAHEAD = 1; // Fetch one extra summary for offset calculation
|
const SUMMARY_LOOKAHEAD = 1; // Fetch one extra summary for offset calculation
|
||||||
|
|
||||||
@@ -263,19 +249,32 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ALL recent observations for this project (not filtered by summaries)
|
// Build SQL WHERE clause for observation types
|
||||||
|
const typeArray = Array.from(config.observationTypes);
|
||||||
|
const typePlaceholders = typeArray.map(() => '?').join(',');
|
||||||
|
|
||||||
|
// Build SQL WHERE clause for concepts
|
||||||
|
const conceptArray = Array.from(config.observationConcepts);
|
||||||
|
const conceptPlaceholders = conceptArray.map(() => '?').join(',');
|
||||||
|
|
||||||
|
// Get recent observations filtered by type and concepts at SQL level
|
||||||
// This ensures we show observations even when summaries haven't been generated
|
// This ensures we show observations even when summaries haven't been generated
|
||||||
// Configurable via settings (default: 50)
|
// Configurable via settings (default: 50)
|
||||||
const allObservations = db.db.prepare(`
|
const observations = db.db.prepare(`
|
||||||
SELECT
|
SELECT
|
||||||
id, sdk_session_id, type, title, subtitle, narrative,
|
id, sdk_session_id, type, title, subtitle, narrative,
|
||||||
facts, concepts, files_read, files_modified, discovery_tokens,
|
facts, concepts, files_read, files_modified, discovery_tokens,
|
||||||
created_at, created_at_epoch
|
created_at, created_at_epoch
|
||||||
FROM observations
|
FROM observations
|
||||||
WHERE project = ?
|
WHERE project = ?
|
||||||
|
AND type IN (${typePlaceholders})
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM json_each(concepts)
|
||||||
|
WHERE value IN (${conceptPlaceholders})
|
||||||
|
)
|
||||||
ORDER BY created_at_epoch DESC
|
ORDER BY created_at_epoch DESC
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
`).all(project, config.totalObservationCount) as Observation[];
|
`).all(project, ...typeArray, ...conceptArray, config.totalObservationCount) as Observation[];
|
||||||
|
|
||||||
// Get recent summaries (optional - may not exist for recent sessions)
|
// Get recent summaries (optional - may not exist for recent sessions)
|
||||||
// Fetch one extra for offset calculation
|
// Fetch one extra for offset calculation
|
||||||
@@ -288,7 +287,7 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
|||||||
`).all(project, config.sessionCount + SUMMARY_LOOKAHEAD) as SessionSummary[];
|
`).all(project, config.sessionCount + SUMMARY_LOOKAHEAD) as SessionSummary[];
|
||||||
|
|
||||||
// If we have neither observations nor summaries, show empty state
|
// If we have neither observations nor summaries, show empty state
|
||||||
if (allObservations.length === 0 && recentSummaries.length === 0) {
|
if (observations.length === 0 && recentSummaries.length === 0) {
|
||||||
db.close();
|
db.close();
|
||||||
if (useColors) {
|
if (useColors) {
|
||||||
return `\n${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}\n${colors.gray}${'─'.repeat(60)}${colors.reset}\n\n${colors.dim}No previous sessions found for this project yet.${colors.reset}\n`;
|
return `\n${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}\n${colors.gray}${'─'.repeat(60)}${colors.reset}\n\n${colors.dim}No previous sessions found for this project yet.${colors.reset}\n`;
|
||||||
@@ -296,16 +295,6 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
|||||||
return `# [${project}] recent context\n\nNo previous sessions found for this project yet.`;
|
return `# [${project}] recent context\n\nNo previous sessions found for this project yet.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter observations by type
|
|
||||||
let observations = allObservations.filter(obs => config.observationTypes.has(obs.type));
|
|
||||||
|
|
||||||
// Filter by concepts (include if observation has at least one matching concept)
|
|
||||||
observations = observations.filter(obs => {
|
|
||||||
if (config.observationConcepts.size === 0) return true;
|
|
||||||
const obsConcepts = parseJsonArray(obs.concepts);
|
|
||||||
return obsConcepts.some(c => config.observationConcepts.has(c));
|
|
||||||
});
|
|
||||||
|
|
||||||
const displaySummaries = recentSummaries.slice(0, config.sessionCount);
|
const displaySummaries = recentSummaries.slice(0, config.sessionCount);
|
||||||
|
|
||||||
// All filtered observations are shown in timeline
|
// All filtered observations are shown in timeline
|
||||||
@@ -563,29 +552,7 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
|||||||
const title = obs.title || 'Untitled';
|
const title = obs.title || 'Untitled';
|
||||||
|
|
||||||
// Map observation type to emoji icon
|
// Map observation type to emoji icon
|
||||||
let icon = '•';
|
const icon = TYPE_ICON_MAP[obs.type as keyof typeof TYPE_ICON_MAP] || '•';
|
||||||
switch (obs.type) {
|
|
||||||
case 'bugfix':
|
|
||||||
icon = '🔴';
|
|
||||||
break;
|
|
||||||
case 'feature':
|
|
||||||
icon = '🟣';
|
|
||||||
break;
|
|
||||||
case 'refactor':
|
|
||||||
icon = '🔄';
|
|
||||||
break;
|
|
||||||
case 'change':
|
|
||||||
icon = '✅';
|
|
||||||
break;
|
|
||||||
case 'discovery':
|
|
||||||
icon = '🔵';
|
|
||||||
break;
|
|
||||||
case 'decision':
|
|
||||||
icon = '⚖️';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
icon = '•';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Section 2: Calculate read tokens (estimate from observation size)
|
// Section 2: Calculate read tokens (estimate from observation size)
|
||||||
const obsSize = (obs.title?.length || 0) +
|
const obsSize = (obs.title?.length || 0) +
|
||||||
@@ -598,21 +565,7 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
|||||||
const discoveryTokens = obs.discovery_tokens || 0;
|
const discoveryTokens = obs.discovery_tokens || 0;
|
||||||
|
|
||||||
// Map observation type to work emoji
|
// Map observation type to work emoji
|
||||||
let workEmoji = '🔍'; // default to research/discovery
|
const workEmoji = TYPE_WORK_EMOJI_MAP[obs.type as keyof typeof TYPE_WORK_EMOJI_MAP] || '🔍';
|
||||||
switch (obs.type) {
|
|
||||||
case 'discovery':
|
|
||||||
workEmoji = '🔍'; // research/exploration
|
|
||||||
break;
|
|
||||||
case 'change':
|
|
||||||
case 'feature':
|
|
||||||
case 'bugfix':
|
|
||||||
case 'refactor':
|
|
||||||
workEmoji = '🛠️'; // building/modifying
|
|
||||||
break;
|
|
||||||
case 'decision':
|
|
||||||
workEmoji = '⚖️'; // decision-making
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const discoveryDisplay = discoveryTokens > 0 ? `${workEmoji} ${discoveryTokens.toLocaleString()}` : '-';
|
const discoveryDisplay = discoveryTokens > 0 ? `${workEmoji} ${discoveryTokens.toLocaleString()}` : '-';
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ import { SDKAgent } from './worker/SDKAgent.js';
|
|||||||
import { PaginationHelper } from './worker/PaginationHelper.js';
|
import { PaginationHelper } from './worker/PaginationHelper.js';
|
||||||
import { SettingsManager } from './worker/SettingsManager.js';
|
import { SettingsManager } from './worker/SettingsManager.js';
|
||||||
import { getBranchInfo, switchBranch, pullUpdates, type BranchInfo, type SwitchResult } from './worker/BranchManager.js';
|
import { getBranchInfo, switchBranch, pullUpdates, type BranchInfo, type SwitchResult } from './worker/BranchManager.js';
|
||||||
|
import {
|
||||||
|
OBSERVATION_TYPES,
|
||||||
|
OBSERVATION_CONCEPTS,
|
||||||
|
DEFAULT_OBSERVATION_TYPES_STRING,
|
||||||
|
DEFAULT_OBSERVATION_CONCEPTS_STRING
|
||||||
|
} from '../constants/observation-metadata.js';
|
||||||
|
|
||||||
export class WorkerService {
|
export class WorkerService {
|
||||||
private app: express.Application;
|
private app: express.Application;
|
||||||
@@ -856,23 +862,21 @@ export class WorkerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate observation types
|
// Validate observation types
|
||||||
const validTypes = ['bugfix', 'feature', 'refactor', 'discovery', 'decision', 'change'];
|
|
||||||
if (settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES) {
|
if (settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES) {
|
||||||
const types = settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES.split(',').map((t: string) => t.trim());
|
const types = settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES.split(',').map((t: string) => t.trim());
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
if (type && !validTypes.includes(type)) {
|
if (type && !OBSERVATION_TYPES.includes(type as any)) {
|
||||||
return { valid: false, error: `Invalid observation type: ${type}` };
|
return { valid: false, error: `Invalid observation type: ${type}. Valid types: ${OBSERVATION_TYPES.join(', ')}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate observation concepts
|
// Validate observation concepts
|
||||||
const validConcepts = ['how-it-works', 'why-it-exists', 'what-changed', 'problem-solution', 'gotcha', 'pattern', 'trade-off'];
|
|
||||||
if (settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS) {
|
if (settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS) {
|
||||||
const concepts = settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS.split(',').map((c: string) => c.trim());
|
const concepts = settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS.split(',').map((c: string) => c.trim());
|
||||||
for (const concept of concepts) {
|
for (const concept of concepts) {
|
||||||
if (concept && !validConcepts.includes(concept)) {
|
if (concept && !OBSERVATION_CONCEPTS.includes(concept as any)) {
|
||||||
return { valid: false, error: `Invalid observation concept: ${concept}` };
|
return { valid: false, error: `Invalid observation concept: ${concept}. Valid concepts: ${OBSERVATION_CONCEPTS.join(', ')}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -899,8 +903,8 @@ export class WorkerService {
|
|||||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: 'true',
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: 'true',
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: 'true',
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: 'true',
|
||||||
// Observation Filtering
|
// Observation Filtering
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: 'bugfix,feature,refactor,discovery,decision,change',
|
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: DEFAULT_OBSERVATION_TYPES_STRING,
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: 'how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off',
|
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: DEFAULT_OBSERVATION_CONCEPTS_STRING,
|
||||||
// Display Configuration
|
// Display Configuration
|
||||||
CLAUDE_MEM_CONTEXT_FULL_COUNT: '5',
|
CLAUDE_MEM_CONTEXT_FULL_COUNT: '5',
|
||||||
CLAUDE_MEM_CONTEXT_FULL_FIELD: 'narrative',
|
CLAUDE_MEM_CONTEXT_FULL_FIELD: 'narrative',
|
||||||
@@ -926,8 +930,8 @@ export class WorkerService {
|
|||||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || 'true',
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || 'true',
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || 'true',
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || 'true',
|
||||||
// Observation Filtering
|
// Observation Filtering
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: env.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || 'bugfix,feature,refactor,discovery,decision,change',
|
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: env.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_OBSERVATION_TYPES_STRING,
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: env.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || 'how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off',
|
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: env.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_OBSERVATION_CONCEPTS_STRING,
|
||||||
// Display Configuration
|
// Display Configuration
|
||||||
CLAUDE_MEM_CONTEXT_FULL_COUNT: env.CLAUDE_MEM_CONTEXT_FULL_COUNT || '5',
|
CLAUDE_MEM_CONTEXT_FULL_COUNT: env.CLAUDE_MEM_CONTEXT_FULL_COUNT || '5',
|
||||||
CLAUDE_MEM_CONTEXT_FULL_FIELD: env.CLAUDE_MEM_CONTEXT_FULL_FIELD || 'narrative',
|
CLAUDE_MEM_CONTEXT_FULL_FIELD: env.CLAUDE_MEM_CONTEXT_FULL_FIELD || 'narrative',
|
||||||
|
|||||||
@@ -19,45 +19,52 @@ interface SidebarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConnected, projects, currentFilter, onFilterChange, onSave, onClose, onRefreshStats }: SidebarProps) {
|
export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConnected, projects, currentFilter, onFilterChange, onSave, onClose, onRefreshStats }: SidebarProps) {
|
||||||
// Settings form state
|
// Consolidated settings form state
|
||||||
const [model, setModel] = useState(settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL);
|
const [formState, setFormState] = useState<Settings>({
|
||||||
const [contextObs, setContextObs] = useState(settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS);
|
CLAUDE_MEM_MODEL: settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL,
|
||||||
const [workerPort, setWorkerPort] = useState(settings.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT);
|
CLAUDE_MEM_CONTEXT_OBSERVATIONS: settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS,
|
||||||
|
CLAUDE_MEM_WORKER_PORT: settings.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT,
|
||||||
// Context settings state
|
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS,
|
||||||
const [showReadTokens, setShowReadTokens] = useState(settings.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS);
|
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS,
|
||||||
const [showWorkTokens, setShowWorkTokens] = useState(settings.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS);
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT,
|
||||||
const [showSavingsAmount, setShowSavingsAmount] = useState(settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT);
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT,
|
||||||
const [showSavingsPercent, setShowSavingsPercent] = useState(settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT);
|
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES,
|
||||||
const [obsTypes, setObsTypes] = useState(settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES);
|
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS,
|
||||||
const [obsConcepts, setObsConcepts] = useState(settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS);
|
CLAUDE_MEM_CONTEXT_FULL_COUNT: settings.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT,
|
||||||
const [fullCount, setFullCount] = useState(settings.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT);
|
CLAUDE_MEM_CONTEXT_FULL_FIELD: settings.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD,
|
||||||
const [fullField, setFullField] = useState(settings.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD);
|
CLAUDE_MEM_CONTEXT_SESSION_COUNT: settings.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT,
|
||||||
const [sessionCount, setSessionCount] = useState(settings.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT);
|
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY,
|
||||||
const [showLastSummary, setShowLastSummary] = useState(settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY);
|
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE,
|
||||||
const [showLastMessage, setShowLastMessage] = useState(settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE);
|
});
|
||||||
|
|
||||||
// MCP toggle state (separate from settings)
|
// MCP toggle state (separate from settings)
|
||||||
const [mcpEnabled, setMcpEnabled] = useState(true);
|
const [mcpEnabled, setMcpEnabled] = useState(true);
|
||||||
const [mcpToggling, setMcpToggling] = useState(false);
|
const [mcpToggling, setMcpToggling] = useState(false);
|
||||||
const [mcpStatus, setMcpStatus] = useState('');
|
const [mcpStatus, setMcpStatus] = useState('');
|
||||||
|
|
||||||
// Update settings form state when settings change
|
// Helper to update form state
|
||||||
|
const updateFormState = (field: keyof Settings, value: string) => {
|
||||||
|
setFormState(prev => ({ ...prev, [field]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update settings form state when settings prop changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModel(settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL);
|
setFormState({
|
||||||
setContextObs(settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS);
|
CLAUDE_MEM_MODEL: settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL,
|
||||||
setWorkerPort(settings.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT);
|
CLAUDE_MEM_CONTEXT_OBSERVATIONS: settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS,
|
||||||
setShowReadTokens(settings.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS);
|
CLAUDE_MEM_WORKER_PORT: settings.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT,
|
||||||
setShowWorkTokens(settings.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS);
|
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS,
|
||||||
setShowSavingsAmount(settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT);
|
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS,
|
||||||
setShowSavingsPercent(settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT);
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT,
|
||||||
setObsTypes(settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES);
|
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT,
|
||||||
setObsConcepts(settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS);
|
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES,
|
||||||
setFullCount(settings.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT);
|
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS,
|
||||||
setFullField(settings.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD);
|
CLAUDE_MEM_CONTEXT_FULL_COUNT: settings.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT,
|
||||||
setSessionCount(settings.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT);
|
CLAUDE_MEM_CONTEXT_FULL_FIELD: settings.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD,
|
||||||
setShowLastSummary(settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY);
|
CLAUDE_MEM_CONTEXT_SESSION_COUNT: settings.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT,
|
||||||
setShowLastMessage(settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE);
|
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY,
|
||||||
|
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE,
|
||||||
|
});
|
||||||
}, [settings]);
|
}, [settings]);
|
||||||
|
|
||||||
// Fetch MCP status on mount
|
// Fetch MCP status on mount
|
||||||
@@ -76,22 +83,7 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
}, [isOpen, onRefreshStats]);
|
}, [isOpen, onRefreshStats]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
onSave({
|
onSave(formState);
|
||||||
CLAUDE_MEM_MODEL: model,
|
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATIONS: contextObs,
|
|
||||||
CLAUDE_MEM_WORKER_PORT: workerPort,
|
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: showReadTokens,
|
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: showWorkTokens,
|
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: showSavingsAmount,
|
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: showSavingsPercent,
|
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: obsTypes,
|
|
||||||
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: obsConcepts,
|
|
||||||
CLAUDE_MEM_CONTEXT_FULL_COUNT: fullCount,
|
|
||||||
CLAUDE_MEM_CONTEXT_FULL_FIELD: fullField,
|
|
||||||
CLAUDE_MEM_CONTEXT_SESSION_COUNT: sessionCount,
|
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: showLastSummary,
|
|
||||||
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: showLastMessage,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMcpToggle = async (enabled: boolean) => {
|
const handleMcpToggle = async (enabled: boolean) => {
|
||||||
@@ -228,8 +220,8 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
id="model"
|
id="model"
|
||||||
value={model}
|
value={formState.CLAUDE_MEM_MODEL}
|
||||||
onChange={e => setModel(e.target.value)}
|
onChange={e => updateFormState('CLAUDE_MEM_MODEL', e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="claude-haiku-4-5">claude-haiku-4-5</option>
|
<option value="claude-haiku-4-5">claude-haiku-4-5</option>
|
||||||
<option value="claude-sonnet-4-5">claude-sonnet-4-5</option>
|
<option value="claude-sonnet-4-5">claude-sonnet-4-5</option>
|
||||||
@@ -246,8 +238,8 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
id="contextObs"
|
id="contextObs"
|
||||||
min="1"
|
min="1"
|
||||||
max="200"
|
max="200"
|
||||||
value={contextObs}
|
value={formState.CLAUDE_MEM_CONTEXT_OBSERVATIONS}
|
||||||
onChange={e => setContextObs(e.target.value)}
|
onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_OBSERVATIONS', e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
@@ -260,8 +252,8 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
id="workerPort"
|
id="workerPort"
|
||||||
min="1024"
|
min="1024"
|
||||||
max="65535"
|
max="65535"
|
||||||
value={workerPort}
|
value={formState.CLAUDE_MEM_WORKER_PORT}
|
||||||
onChange={e => setWorkerPort(e.target.value)}
|
onChange={e => updateFormState('CLAUDE_MEM_WORKER_PORT', e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -273,19 +265,19 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
<input type="checkbox" checked={showReadTokens === 'true'} onChange={e => setShowReadTokens(e.target.checked ? 'true' : 'false')} />
|
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS', e.target.checked ? 'true' : 'false')} />
|
||||||
Show read tokens
|
Show read tokens
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
<input type="checkbox" checked={showWorkTokens === 'true'} onChange={e => setShowWorkTokens(e.target.checked ? 'true' : 'false')} />
|
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS', e.target.checked ? 'true' : 'false')} />
|
||||||
Show work tokens
|
Show work tokens
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
<input type="checkbox" checked={showSavingsAmount === 'true'} onChange={e => setShowSavingsAmount(e.target.checked ? 'true' : 'false')} />
|
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT', e.target.checked ? 'true' : 'false')} />
|
||||||
Show savings amount
|
Show savings amount
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
<input type="checkbox" checked={showSavingsPercent === 'true'} onChange={e => setShowSavingsPercent(e.target.checked ? 'true' : 'false')} />
|
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT', e.target.checked ? 'true' : 'false')} />
|
||||||
Show savings percentage
|
Show savings percentage
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -302,7 +294,7 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
<label htmlFor="fullCount" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
|
<label htmlFor="fullCount" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
|
||||||
Full observation count (0-20)
|
Full observation count (0-20)
|
||||||
</label>
|
</label>
|
||||||
<input type="number" id="fullCount" min="0" max="20" value={fullCount} onChange={e => setFullCount(e.target.value)} style={{ width: '100%' }} />
|
<input type="number" id="fullCount" min="0" max="20" value={formState.CLAUDE_MEM_CONTEXT_FULL_COUNT} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_FULL_COUNT', e.target.value)} style={{ width: '100%' }} />
|
||||||
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
|
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
|
||||||
Number of most recent observations to show with full details
|
Number of most recent observations to show with full details
|
||||||
</div>
|
</div>
|
||||||
@@ -311,7 +303,7 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
<label htmlFor="fullField" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
|
<label htmlFor="fullField" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
|
||||||
Full observation field
|
Full observation field
|
||||||
</label>
|
</label>
|
||||||
<select id="fullField" value={fullField} onChange={e => setFullField(e.target.value)} style={{ width: '100%' }}>
|
<select id="fullField" value={formState.CLAUDE_MEM_CONTEXT_FULL_FIELD} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_FULL_FIELD', e.target.value)} style={{ width: '100%' }}>
|
||||||
<option value="narrative">Narrative</option>
|
<option value="narrative">Narrative</option>
|
||||||
<option value="facts">Facts</option>
|
<option value="facts">Facts</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -320,7 +312,7 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
<label htmlFor="sessionCount" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
|
<label htmlFor="sessionCount" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
|
||||||
Session summary count (1-50)
|
Session summary count (1-50)
|
||||||
</label>
|
</label>
|
||||||
<input type="number" id="sessionCount" min="1" max="50" value={sessionCount} onChange={e => setSessionCount(e.target.value)} style={{ width: '100%' }} />
|
<input type="number" id="sessionCount" min="1" max="50" value={formState.CLAUDE_MEM_CONTEXT_SESSION_COUNT} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SESSION_COUNT', e.target.value)} style={{ width: '100%' }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -333,11 +325,11 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
<input type="checkbox" checked={showLastSummary === 'true'} onChange={e => setShowLastSummary(e.target.checked ? 'true' : 'false')} />
|
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY', e.target.checked ? 'true' : 'false')} />
|
||||||
Show last session summary
|
Show last session summary
|
||||||
</label>
|
</label>
|
||||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
<input type="checkbox" checked={showLastMessage === 'true'} onChange={e => setShowLastMessage(e.target.checked ? 'true' : 'false')} />
|
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE', e.target.checked ? 'true' : 'false')} />
|
||||||
Include last session message
|
Include last session message
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user