feat: add KnowledgeGraph component for visualizing and managing learning topics and relationships
This commit is contained in:
@@ -26,6 +26,7 @@ const KnowledgeGraph = () => {
|
||||
|
||||
const [topics, setTopics] = useState([]);
|
||||
const [relations, setRelations] = useState([]);
|
||||
const [showExcludeNodes, setShowExcludeNodes] = useState(false);
|
||||
|
||||
const reloadKb = useCallback(() => {
|
||||
Promise.all([db.getTopics(), db.getRelations()]).then(([allTopics, allRelations]) => {
|
||||
@@ -71,8 +72,15 @@ const KnowledgeGraph = () => {
|
||||
const { width, height } = dimensions;
|
||||
d3.select(svgRef.current).selectAll('*').remove();
|
||||
|
||||
const nodes = topics.map(d => ({ ...d }));
|
||||
const links = relations.map(d => ({ ...d }));
|
||||
const filteredTopics = showExcludeNodes ? topics : topics.filter(t => t.learning_relevance !== 'exclude');
|
||||
const filteredTopicIds = new Set(filteredTopics.map(t => t.id));
|
||||
const filteredRelations = relations.filter(r =>
|
||||
filteredTopicIds.has(r.source?.id || r.source) &&
|
||||
filteredTopicIds.has(r.target?.id || r.target)
|
||||
);
|
||||
|
||||
const nodes = filteredTopics.map(d => ({ ...d }));
|
||||
const links = filteredRelations.map(d => ({ ...d }));
|
||||
|
||||
const svg = d3.select(svgRef.current)
|
||||
.attr('viewBox', [0, 0, width, height])
|
||||
@@ -116,6 +124,12 @@ const KnowledgeGraph = () => {
|
||||
.attr('fill', d => color(d.type) || '#1F5560')
|
||||
.attr('stroke', '#fff')
|
||||
.attr('stroke-width', 2)
|
||||
.attr('stroke-dasharray', d => d.learning_relevance === 'exclude' ? '4,4' : 'none')
|
||||
.attr('opacity', d => {
|
||||
if (d.learning_relevance === 'exclude') return 0.3;
|
||||
if (d.learning_relevance === 'peripheral') return 0.5;
|
||||
return 1;
|
||||
})
|
||||
.on('click', (event, d) => {
|
||||
setSelectedNode(d);
|
||||
setIsEditing(false);
|
||||
@@ -392,6 +406,15 @@ Analyze this graph and return ONLY the optimized JSON object with this EXACT str
|
||||
|
||||
<div className="w-full md:w-80 bg-paper p-6 overflow-y-auto border-t md:border-t-0 border-bg-warm flex flex-col">
|
||||
<div className="mb-6 pb-4 border-b border-bg-warm space-y-3">
|
||||
<label className="flex items-center gap-2 text-sm text-fg-muted cursor-pointer mb-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showExcludeNodes}
|
||||
onChange={e => setShowExcludeNodes(e.target.checked)}
|
||||
className="rounded bg-bg-warm border-transparent focus:ring-0 text-teal"
|
||||
/>
|
||||
Show Excluded Nodes (Reference Material)
|
||||
</label>
|
||||
<div>
|
||||
<Button
|
||||
onClick={analyzeGraph}
|
||||
@@ -486,6 +509,19 @@ Analyze this graph and return ONLY the optimized JSON object with this EXACT str
|
||||
<option value="process">Process</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-fg-muted uppercase tracking-wider mb-1 block">Learning Relevance</label>
|
||||
<select
|
||||
className="w-full border border-bg-warm rounded-[var(--r-sm)] px-3 py-1.5 text-sm bg-bg"
|
||||
value={editData.learning_relevance || 'standard'}
|
||||
onChange={e => setEditData({...editData, learning_relevance: e.target.value})}
|
||||
>
|
||||
<option value="core">Core</option>
|
||||
<option value="standard">Standard</option>
|
||||
<option value="peripheral">Peripheral</option>
|
||||
<option value="exclude">Exclude</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-fg-muted uppercase tracking-wider mb-1 block">Description</label>
|
||||
<textarea
|
||||
@@ -506,8 +542,11 @@ Analyze this graph and return ONLY the optimized JSON object with this EXACT str
|
||||
<p className="font-medium">{selectedNode.label}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-fg-muted uppercase tracking-wider mb-1">Type</p>
|
||||
<span className="inline-block px-2 py-1 bg-bg-warm rounded-[var(--r-pill)] text-xs font-mono">{selectedNode.type}</span>
|
||||
<p className="text-xs text-fg-muted uppercase tracking-wider mb-1">Type & Relevance</p>
|
||||
<div className="flex gap-2">
|
||||
<span className="inline-block px-2 py-1 bg-bg-warm rounded-[var(--r-pill)] text-xs font-mono">{selectedNode.type}</span>
|
||||
<span className="inline-block px-2 py-1 bg-bg-warm rounded-[var(--r-pill)] text-xs font-mono opacity-80">{selectedNode.learning_relevance || 'standard'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-fg-muted uppercase tracking-wider mb-1">Description</p>
|
||||
|
||||
Reference in New Issue
Block a user