UNPKG

lorehub

Version:

Capture and surface the collective wisdom of your codebase

214 lines 10.8 kB
import React, { useState, useEffect } from 'react'; import { Box, Text, useInput } from 'ink'; import { Table } from './Table.js'; import { TruncatedText } from './TruncatedText.js'; import { useTerminalDimensions } from '../hooks/useTerminalDimensions.js'; export function SimilarLoresView({ db, lore: lore, onBack }) { const { columns, rows } = useTerminalDimensions(); const [currentLore, setCurrentLore] = useState(lore); const [similarLores, setSimilarLores] = useState([]); const [loading, setLoading] = useState(true); const [selectedIndex, setSelectedIndex] = useState(0); const [navigationHistory, setNavigationHistory] = useState([lore]); const [similarLoresCounts, setSimilarLoresCounts] = useState(new Map()); useInput((input, key) => { if (input === 'q' || key.escape) { onBack(); } else if (key.upArrow || input === 'k') { if (selectedIndex > 0) { setSelectedIndex(selectedIndex - 1); } } else if (key.downArrow || input === 'j') { if (selectedIndex < similarLores.length - 1) { setSelectedIndex(selectedIndex + 1); } } else if (input === 's' && similarLores.length > 0 && !loading) { // Navigate to the selected similar lore const selectedSimilarLore = similarLores[selectedIndex]; if (selectedSimilarLore) { setNavigationHistory([...navigationHistory, currentLore]); setCurrentLore(selectedSimilarLore); setSelectedIndex(0); setLoading(true); } } else if (input === 'b' && navigationHistory.length > 1) { // Go back in navigation history const newHistory = [...navigationHistory]; newHistory.pop(); // Remove current const previousLore = newHistory[newHistory.length - 1]; if (previousLore) { setNavigationHistory(newHistory); setCurrentLore(previousLore); setSelectedIndex(0); setLoading(true); } } }); useEffect(() => { const loadSimilarLores = async () => { try { const similar = await db.findSimilarLores(currentLore.id, { limit: 20, threshold: 0.3 }); // Load realm info for each similar lore const loresWithRealms = similar.map(f => { const realm = db.findRealm(f.realmId || f.realmId); const currentRealm = db.findRealmByPath(process.cwd()); return { ...f, realmName: realm?.name || 'Unknown', realmPath: realm?.path || '', isCurrentRealm: currentRealm?.id === (f.realmId || f.realmId) }; }); setSimilarLores(loresWithRealms); // Load similar counts for each lore const counts = new Map(); await Promise.all(loresWithRealms.map(async (f) => { try { const similar = await db.findSimilarLores(f.id, { limit: 10, threshold: 0.5 }); counts.set(f.id, similar.length); } catch (error) { counts.set(f.id, 0); } })); setSimilarLoresCounts(counts); } catch (error) { console.error('Failed to load similar lores:', error); setSimilarLores([]); } finally { setLoading(false); } }; setLoading(true); loadSimilarLores(); }, [db, currentLore]); if (loading) { return React.createElement(Text, null, "Loading similar lores..."); } if (similarLores.length === 0) { return (React.createElement(Box, { flexDirection: "column" }, React.createElement(Text, { color: "yellow" }, "No similar lores 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 loresWidth = columns > 120 ? Math.floor(columns * 0.6) : Math.floor(columns * 0.5); const detailsWidth = columns - loresWidth - 3; // Helper function to get status letter const getStatusLetter = (status) => { switch (status) { case 'living': return 'L'; case 'archived': return 'A'; case 'whispered': return 'W'; case 'proclaimed': return 'P'; default: return '?'; } }; // Prepare table data const tableData = similarLores.map((lore, index) => { const similarCount = similarLoresCounts.get(lore.id) || 0; const isSelected = index === selectedIndex; const similarityPercent = Math.round(lore.similarity * 100); // Calculate max content length const contentMaxLength = Math.max(30, loresWidth - 40); const content = lore.content.length > contentMaxLength ? lore.content.substring(0, contentMaxLength - 3) + '...' : lore.content; return { '': isSelected ? '→' : ' ', 'Match': `${similarityPercent}%`, 'Sim': similarCount > 0 ? `${similarCount}≈` : '-', 'Type': lore.type.substring(0, 3).toUpperCase(), 'S': getStatusLetter(lore.status || 'living'), 'Content': content, }; }); const selectedLore = similarLores[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 lores to"), navigationHistory.length > 1 && (React.createElement(Text, { dimColor: true }, " (depth: ", navigationHistory.length - 1, ")")), React.createElement(Text, { bold: true }, ":")), React.createElement(Text, { color: "cyan" }, "[", currentLore.type, "] ", currentLore.content.substring(0, 60), currentLore.content.length > 60 ? '...' : ''), React.createElement(Box, { marginTop: 1 }, React.createElement(Text, { dimColor: true }, "Found ", similarLores.length, " similar lore", similarLores.length !== 1 ? 's' : ''))), React.createElement(Box, { flexDirection: "row", height: contentHeight, overflow: "hidden" }, React.createElement(Box, { flexDirection: "column", width: loresWidth, marginRight: 2 }, React.createElement(Box, { marginBottom: 1 }, React.createElement(Text, { bold: true, dimColor: true }, "Similar Lores")), React.createElement(Box, { flexDirection: "column" }, similarLores.length > 0 ? (React.createElement(Table, { data: tableData })) : (React.createElement(Text, { dimColor: true }, "No similar lores found")))), React.createElement(Box, { flexDirection: "column", width: detailsWidth, overflow: "hidden" }, React.createElement(Text, { bold: true, dimColor: true }, "Details"), selectedLore && (React.createElement(Box, { flexDirection: "column", marginTop: 1 }, React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, React.createElement(Text, { dimColor: true }, "Similarity: ", (selectedLore.similarity * 100).toFixed(1), "%"), React.createElement(Text, { dimColor: true }, "Realm: ", selectedLore.realmName, selectedLore.isCurrentRealm ? ' •' : ''), React.createElement(Text, { dimColor: true }, "Type: ", selectedLore.type, " | Status: ", selectedLore.status || 'living', " | Confidence: ", selectedLore.confidence, "%"), React.createElement(Text, { dimColor: true }, "Created: ", selectedLore.createdAt.toLocaleDateString(), " ", selectedLore.createdAt.toLocaleTimeString()), React.createElement(Text, { dimColor: true }, "Sigils: ", selectedLore.sigils.length > 0 ? selectedLore.sigils.join(', ') : 'none'), React.createElement(Text, { dimColor: true }, "Provinces: ", selectedLore.provinces.length > 0 ? selectedLore.provinces.join(', ') : 'none')), React.createElement(Text, { dimColor: true }, '─'.repeat(Math.min(detailsWidth - 2, 50))), React.createElement(Box, { flexDirection: "column", marginTop: 1 }, React.createElement(Box, { marginBottom: 1 }, React.createElement(Text, { wrap: "wrap" }, selectedLore.content)), selectedLore.why && (React.createElement(Box, { marginTop: 1, flexDirection: "column" }, React.createElement(Box, { marginBottom: 0 }, React.createElement(Text, { bold: true, dimColor: true }, "Why:")), React.createElement(Box, null, React.createElement(Text, { wrap: "wrap", dimColor: true }, selectedLore.why))))))))), 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=SimilarLoresView.js.map