UNPKG

lorehub

Version:

Capture and surface the collective wisdom of your codebase

170 lines 9.64 kB
import React, { useState, useEffect } from 'react'; import { Box, Text, useInput } from 'ink'; import SelectInput from 'ink-select-input'; import { TruncatedText } from './TruncatedText.js'; import { useTerminalDimensions } from '../hooks/useTerminalDimensions.js'; export function SimilarFactsView({ db, fact, onBack }) { const { columns, rows } = useTerminalDimensions(); const [currentFact, setCurrentFact] = useState(fact); const [similarFacts, setSimilarFacts] = useState([]); const [loading, setLoading] = useState(true); const [selectedIndex, setSelectedIndex] = useState(0); const [navigationHistory, setNavigationHistory] = useState([fact]); const [similarFactsCounts, setSimilarFactsCounts] = useState(new Map()); useInput((input, key) => { if (input === 'q' || key.escape) { onBack(); } else if (input === 's' && similarFacts.length > 0 && !loading) { // Navigate to the selected similar fact const selectedSimilarFact = similarFacts[selectedIndex]; if (selectedSimilarFact) { setNavigationHistory([...navigationHistory, currentFact]); setCurrentFact(selectedSimilarFact); setSelectedIndex(0); setLoading(true); } } else if (input === 'b' && navigationHistory.length > 1) { // Go back in navigation history const newHistory = [...navigationHistory]; newHistory.pop(); // Remove current const previousFact = newHistory[newHistory.length - 1]; if (previousFact) { setNavigationHistory(newHistory); setCurrentFact(previousFact); setSelectedIndex(0); setLoading(true); } } }); useEffect(() => { const loadSimilarFacts = async () => { try { const similar = await db.findSimilarFacts(currentFact.id, { limit: 20, threshold: 0.3 }); // Load project info for each similar fact const factsWithProjects = similar.map(f => { const project = db.findProject(f.projectId || f.realmId); const currentProject = db.findProjectByPath(process.cwd()); return { ...f, projectName: project?.name || 'Unknown', projectPath: project?.path || '', isCurrentProject: currentProject?.id === (f.projectId || f.realmId) }; }); setSimilarFacts(factsWithProjects); // Load similar counts for each fact const counts = new Map(); await Promise.all(factsWithProjects.map(async (f) => { try { const similar = await db.findSimilarFacts(f.id, { limit: 10, threshold: 0.5 }); counts.set(f.id, similar.length); } catch (error) { counts.set(f.id, 0); } })); setSimilarFactsCounts(counts); } catch (error) { console.error('Failed to load similar facts:', error); setSimilarFacts([]); } finally { setLoading(false); } }; setLoading(true); loadSimilarFacts(); }, [db, currentFact]); if (loading) { return React.createElement(Text, null, "Loading similar facts..."); } if (similarFacts.length === 0) { return (React.createElement(Box, { flexDirection: "column" }, React.createElement(Text, { color: "yellow" }, "No similar facts found"), React.createElement(Box, { marginTop: 1 }, React.createElement(Text, { dimColor: true }, "Press q or ESC to go back")))); } // Calculate dimensions early before using them const headerHeight = 4; const footerHeight = 2; const contentHeight = rows - headerHeight - footerHeight - 1; // Use same split as main view const factsWidth = columns > 120 ? Math.floor(columns * 0.6) : Math.floor(columns * 0.5); const detailsWidth = columns - factsWidth - 3; const items = similarFacts.map((f, index) => { const typeStr = `[${f.type.substring(0, 3).toUpperCase()}]`; const similarityStr = `${(f.similarity * 100).toFixed(0)}%`; const similarCount = similarFactsCounts.get(f.id) || 0; // Use fixed-width formatting similar to main view // Fixed width similarity percentage (4 chars) // Fixed width similar count (3 chars + ≈) const similarityFixed = similarityStr.padStart(4); const similarCountStr = similarCount > 0 ? `${similarCount.toString().padStart(3)}≈` : ' '; // Calculate available width // Account for: similarity% (4), space (1), similar count (4), space (1), type (5), space (1) const prefixLength = 4 + 1 + 4 + 1 + 5 + 1; const availableWidth = factsWidth - prefixLength - 4; const contentMaxLength = Math.max(20, availableWidth); const content = f.content.length > contentMaxLength ? f.content.substring(0, contentMaxLength - 3) + '...' : f.content; return { label: `${similarityFixed} ${similarCountStr} ${typeStr} ${content}`, value: index, }; }); const selectedFact = similarFacts[selectedIndex]; return (React.createElement(Box, { flexDirection: "column", height: rows - 1 }, React.createElement(Box, { height: 4, flexDirection: "column" }, React.createElement(Box, { flexDirection: "row" }, React.createElement(Text, { bold: true }, "Similar facts to"), navigationHistory.length > 1 && (React.createElement(Text, { dimColor: true }, " (depth: ", navigationHistory.length - 1, ")")), React.createElement(Text, { bold: true }, ":")), React.createElement(Text, { color: "cyan" }, "[", currentFact.type, "] ", currentFact.content.substring(0, 60), currentFact.content.length > 60 ? '...' : ''), React.createElement(Box, { marginTop: 1 }, React.createElement(Text, { dimColor: true }, "Found ", similarFacts.length, " similar fact", similarFacts.length !== 1 ? 's' : ''))), React.createElement(Box, { flexDirection: "row", height: contentHeight, overflow: "hidden" }, React.createElement(Box, { flexDirection: "column", width: factsWidth, marginRight: 2 }, React.createElement(Text, { bold: true, dimColor: true }, "Similar Facts"), React.createElement(Box, { marginTop: 1 }, React.createElement(SelectInput, { items: items, onHighlight: (item) => setSelectedIndex(item.value), initialIndex: 0, limit: contentHeight - 2 }))), React.createElement(Box, { flexDirection: "column", width: detailsWidth, overflow: "hidden" }, React.createElement(Text, { bold: true, dimColor: true }, "Details"), selectedFact && (React.createElement(Box, { flexDirection: "column", marginTop: 1, height: contentHeight - 2, overflow: "hidden" }, React.createElement(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden" }, React.createElement(TruncatedText, { text: selectedFact.content, maxLines: selectedFact.why ? Math.floor((contentHeight - 10) * 0.6) : contentHeight - 10, width: detailsWidth }), selectedFact.why && (React.createElement(Box, { marginTop: 1, flexDirection: "column" }, React.createElement(TruncatedText, { text: `Why: ${selectedFact.why}`, maxLines: Math.floor((contentHeight - 10) * 0.4), width: detailsWidth, dimColor: true })))), React.createElement(Box, { flexDirection: "column", flexShrink: 0, marginTop: 1 }, React.createElement(TruncatedText, { text: `Similarity: ${(selectedFact.similarity * 100).toFixed(1)}%`, maxLines: 1, width: detailsWidth, dimColor: true }), React.createElement(TruncatedText, { text: `Project: ${selectedFact.projectName}`, maxLines: 1, width: detailsWidth, dimColor: true }), React.createElement(TruncatedText, { text: `Type: ${selectedFact.type} | Confidence: ${selectedFact.confidence}%`, maxLines: 1, width: detailsWidth, dimColor: true }), React.createElement(TruncatedText, { text: `Created: ${selectedFact.createdAt.toLocaleDateString()}`, maxLines: 1, width: detailsWidth, dimColor: true }), selectedFact.sigils.length > 0 && (React.createElement(TruncatedText, { text: `Sigils: ${selectedFact.sigils.join(', ')}`, maxLines: 1, width: detailsWidth, dimColor: true }))))))), React.createElement(Box, { height: 2, marginTop: 1 }, React.createElement(Text, { dimColor: true }, "q/ESC: exit | \u2191\u2193: navigate | s: dive deeper", navigationHistory.length > 1 && ' | b: back')))); } //# sourceMappingURL=SimilarFactsView.js.map