import React, { useState } from 'react'; import { Observation } from '../types'; import { formatDate } from '../utils/formatters'; interface ObservationCardProps { observation: Observation; } // Helper to strip project root from file paths function stripProjectRoot(filePath: string): string { // Try to extract relative path by finding common project markers const markers = ['/Scripts/', '/src/', '/plugin/', '/docs/']; for (const marker of markers) { const index = filePath.indexOf(marker); if (index !== -1) { // Keep the marker and everything after it return filePath.substring(index + 1); } } // Fallback: if path contains project name, strip everything before it const projectIndex = filePath.indexOf('claude-mem/'); if (projectIndex !== -1) { return filePath.substring(projectIndex + 'claude-mem/'.length); } // If no markers found, return basename or original path const parts = filePath.split('/'); return parts.length > 3 ? parts.slice(-3).join('/') : filePath; } export function ObservationCard({ observation }: ObservationCardProps) { const [showFacts, setShowFacts] = useState(false); const [showNarrative, setShowNarrative] = useState(false); const date = formatDate(observation.created_at_epoch); // Parse JSON fields const facts = observation.facts ? JSON.parse(observation.facts) : []; const concepts = observation.concepts ? JSON.parse(observation.concepts) : []; const filesRead = observation.files_read ? JSON.parse(observation.files_read).map(stripProjectRoot) : []; const filesModified = observation.files_modified ? JSON.parse(observation.files_modified).map(stripProjectRoot) : []; // Show facts toggle if there are facts, concepts, or files const hasFactsContent = facts.length > 0 || concepts.length > 0 || filesRead.length > 0 || filesModified.length > 0; return (
{/* Header with toggle buttons in top right */}
{observation.type} {observation.project}
{hasFactsContent && ( )} {observation.narrative && ( )}
{/* Title */}
{observation.title || 'Untitled'}
{/* Content based on toggle state */}
{!showFacts && !showNarrative && observation.subtitle && (
{observation.subtitle}
)} {showFacts && facts.length > 0 && ( )} {showNarrative && observation.narrative && (
{observation.narrative}
)}
{/* Metadata footer - id, date, and conditionally concepts/files when facts toggle is on */}
#{observation.id} • {date} {showFacts && (concepts.length > 0 || filesRead.length > 0 || filesModified.length > 0) && (
{concepts.map((concept: string, i: number) => ( {concept} ))} {filesRead.length > 0 && ( read: {filesRead.join(', ')} )} {filesModified.length > 0 && ( modified: {filesModified.join(', ')} )}
)}
); }