Skip to content
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@elizaos/plugin-knowledge",
"description": "Plugin for Knowledge",
"version": "1.5.10",
"version": "1.5.11",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down
8 changes: 8 additions & 0 deletions src/frontend/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Export all UI components
export { MemoryGraph } from './memory-graph';
export { MemoryGraphOptimized } from './memory-graph-optimized';
export { KnowledgeTab } from './knowledge-tab';
export { Badge } from './badge';
export { Button } from './button';
export { Card } from './card';
export { Input } from './input';
114 changes: 83 additions & 31 deletions src/frontend/ui/knowledge-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Button } from './button';
import { Card } from './card';
import { Input } from './input';
import { MemoryGraph } from './memory-graph';
import { MemoryGraphOptimized } from './memory-graph-optimized';

// Declare global window extension for TypeScript
declare global {
Expand Down Expand Up @@ -566,6 +567,25 @@ export function KnowledgeTab({ agentId }: { agentId: UUID }) {
}
}, [handleScroll]);

// Track changes to selectedMemory and scroll to it
const detailsPanelRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (selectedMemory && detailsPanelRef.current) {
// Scroll the details panel into view smoothly
detailsPanelRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}
}, [selectedMemory]);

// Memoized callback for graph node clicks to prevent unnecessary re-renders
// MUST be defined here (before early returns) to follow Rules of Hooks
const handleGraphNodeClick = useCallback((memory: Memory) => {
setSelectedMemory(memory);
}, []);
Comment thread
cursor[bot] marked this conversation as resolved.

if (isLoading && (!memories || memories.length === 0) && !showSearch) {
return (
<div className="flex items-center justify-center h-40">Loading knowledge documents...</div>
Expand Down Expand Up @@ -1070,11 +1090,12 @@ export function KnowledgeTab({ agentId }: { agentId: UUID }) {
const MemoryDetails = ({ memory }: { memory: Memory }) => {
const metadata = memory.metadata as MemoryMetadata;
const isFragment = metadata?.type === 'fragment';
const isDocument = metadata?.type === 'document';

return (
<div className="border-t border-border bg-card text-card-foreground h-full flex flex-col">
<div className="p-4 flex justify-between items-start flex-shrink-0">
<div className="space-y-1">
<div className="h-full flex flex-col border-t border-border bg-card text-card-foreground">
<div className="flex-shrink-0 p-4 flex justify-between items-start">
<div className="space-y-1 flex-1">
<h3 className="text-sm font-medium flex items-center gap-2">
{isFragment ? (
<span className="flex items-center">
Expand Down Expand Up @@ -1114,22 +1135,58 @@ export function KnowledgeTab({ agentId }: { agentId: UUID }) {
</div>
</div>

<Button
variant="ghost"
size="sm"
onClick={() => setSelectedMemory(null)}
className="text-xs h-7 px-2"
>
Close
</Button>
<div className="flex items-center gap-2">
{isDocument && (
<Button
variant="default"
size="sm"
onClick={() => setViewingContent(memory)}
className="text-xs h-7 px-3"
>
<FileText className="h-3 w-3 mr-1" />
Open Document
</Button>
)}
<Button
variant="ghost"
size="sm"
onClick={() => setSelectedMemory(null)}
className="text-xs h-7 px-2"
>
Close
</Button>
</div>
</div>

<div className="px-4 pb-4 flex-1 flex flex-col">
<div className="bg-background rounded border border-border p-3 text-sm overflow-auto flex-1">
<pre className="whitespace-pre-wrap font-mono text-xs h-full">
{memory.content?.text || 'No content available'}
</pre>
</div>
<div className="flex-1 overflow-hidden px-4 pb-4 flex flex-col">
{isDocument ? (
<div className="flex-1 min-h-0 bg-background rounded border border-border p-6 flex flex-col items-center justify-center text-center">
<FileText className="h-12 w-12 text-muted-foreground mb-4" />
<h4 className="text-sm font-medium mb-2">
{metadata?.title || metadata?.filename || 'Document'}
</h4>
<p className="text-xs text-muted-foreground mb-4">
Click "Open Document" to view the full content
</p>
{metadata?.fileExt && (
<Badge variant="outline" className="text-xs">
{metadata.fileExt.toUpperCase()}
</Badge>
)}
</div>
) : (
<div
className="flex-1 min-h-0 bg-background rounded border border-border p-3 overflow-y-auto"
style={{
scrollbarWidth: 'thin',
scrollbarColor: 'rgba(155, 155, 155, 0.5) transparent'
}}
>
<pre className="whitespace-pre-wrap font-mono text-xs break-words max-w-full">
{memory.content?.text || 'No content available'}
</pre>
</div>
)}

{memory.embedding && (
<div className="mt-2 flex items-center text-xs text-muted-foreground flex-shrink-0">
Expand Down Expand Up @@ -1435,18 +1492,10 @@ export function KnowledgeTab({ agentId }: { agentId: UUID }) {
<div
className={`p-4 overflow-hidden ${selectedMemory ? 'h-1/3' : 'flex-1'} transition-all duration-300`}
>
<MemoryGraph
memories={graphMemories}
onNodeClick={(memory) => {
setSelectedMemory(memory);

// If clicking on a document, load its fragments
const metadata = memory.metadata as any;
if (metadata?.type === 'document') {
setSelectedDocumentForGraph(memory.id as UUID);
}
}}
<MemoryGraphOptimized
onNodeClick={handleGraphNodeClick}
selectedMemoryId={selectedMemory?.id}
agentId={agentId}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Graph Component Update Causes Data Inconsistency

The replacement of MemoryGraph with MemoryGraphOptimized removed the memories prop, leading to two main issues:

  1. The graph no longer respects the selectedDocumentForGraph state, breaking the ability to focus on a specific document's fragments and instead always displaying a paginated view.
  2. The new component fetches data independently, causing stale data to be displayed. Changes like document uploads, deletions, or modifications are not automatically reflected, requiring a manual refresh.
Locations (1)

Fix in CursorFix in Web

/>
{viewMode === 'graph' && graphLoading && selectedDocumentForGraph && (
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card/90 backdrop-blur-sm p-4 rounded-lg shadow-lg border border-border">
Expand All @@ -1460,7 +1509,10 @@ export function KnowledgeTab({ agentId }: { agentId: UUID }) {

{/* Display details of selected node */}
{selectedMemory && (
<div className="h-2/3 overflow-hidden flex-1 transition-all duration-300">
<div
ref={detailsPanelRef}
className="h-2/3 overflow-hidden transition-all duration-300"
>
<MemoryDetails memory={selectedMemory} />
</div>
)}
Expand Down Expand Up @@ -1635,8 +1687,8 @@ export function KnowledgeTab({ agentId }: { agentId: UUID }) {
} else {
// For all other documents, display as plain text
return (
<div className="h-full w-full bg-background rounded-lg border border-border p-6">
<pre className="whitespace-pre-wrap text-sm font-mono leading-relaxed text-foreground">
<div className="h-full w-full bg-background rounded-lg border border-border p-6 overflow-y-auto">
<pre className="whitespace-pre-wrap text-sm font-mono leading-relaxed text-foreground break-words">
{viewingContent.content?.text || 'No content available'}
</pre>
</div>
Expand Down
Loading