diff options
Diffstat (limited to 'apps/web/components/graph/legend.tsx')
| -rw-r--r-- | apps/web/components/graph/legend.tsx | 302 |
1 files changed, 204 insertions, 98 deletions
diff --git a/apps/web/components/graph/legend.tsx b/apps/web/components/graph/legend.tsx index ca04f1e5..9410f33b 100644 --- a/apps/web/components/graph/legend.tsx +++ b/apps/web/components/graph/legend.tsx @@ -1,8 +1,7 @@ "use client" -import { Brain, ChevronDown, ChevronUp, FileText } from "lucide-react" +import { ChevronUp, ChevronDown, Settings } from "lucide-react" import { memo, useEffect, useState } from "react" -import { colors } from "./constants" import type { LegendProps } from "./types" const setCookie = (name: string, value: string, days = 365) => { @@ -25,22 +24,51 @@ const getCookie = (name: string): string | null => { return null } +// Toggle switch component +function Toggle({ enabled, onChange }: { enabled: boolean; onChange: (v: boolean) => void }) { + return ( + <button + type="button" + onClick={() => onChange(!enabled)} + className={`relative w-11 h-6 rounded-full transition-colors ${ + enabled ? "bg-blue-600" : "bg-[#1E3A5F]" + }`} + > + <span + className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-transform ${ + enabled ? "left-6" : "left-1" + }`} + /> + </button> + ) +} + export const Legend = memo(function Legend({ id, nodes = [], edges = [], isLoading = false, }: LegendProps) { - const [isExpanded, setIsExpanded] = useState(true) + const [isExpanded, setIsExpanded] = useState(false) const [isInitialized, setIsInitialized] = useState(false) + const [connectionsExpanded, setConnectionsExpanded] = useState(true) + + // Toggle states for filtering + const [showDocMemory, setShowDocMemory] = useState(true) + const [showDocSimilarity, setShowDocSimilarity] = useState(true) + const [showUpdates, setShowUpdates] = useState(false) + const [showExtends, setShowExtends] = useState(true) + const [showInferences, setShowInferences] = useState(false) + const [showStrong, setShowStrong] = useState(true) + const [showWeak, setShowWeak] = useState(true) useEffect(() => { if (!isInitialized) { const savedState = getCookie("legendCollapsed") - if (savedState === "true") { - setIsExpanded(false) - } else if (savedState === "false") { + if (savedState === "false") { setIsExpanded(true) + } else { + setIsExpanded(false) } setIsInitialized(true) } @@ -54,124 +82,202 @@ export const Legend = memo(function Legend({ const memoryCount = nodes.filter((n) => n.type === "memory").length const documentCount = nodes.filter((n) => n.type === "document").length + const connectionCount = edges.length return ( - <div - id={id} - className="absolute bottom-4 right-4 z-10 bg-white/5 backdrop-blur-xl border border-white/20 rounded-xl overflow-hidden" - > - <div className="p-3"> - {!isExpanded && ( - <button - type="button" - onClick={handleToggleExpanded} - className="flex items-center gap-2 text-white/70 hover:text-white transition-colors" - > - <span className="text-sm font-medium">?</span> - <ChevronUp className="w-4 h-4" /> - </button> - )} + <> + {/* Settings button - Bottom right */} + <button + type="button" + className="absolute bottom-4 right-4 z-10 flex items-center justify-center w-10 h-10 text-white/70 bg-[#0A1628]/80 backdrop-blur-xl border border-[#1E3A5F] rounded-lg hover:bg-[#0A1628] hover:text-white transition-colors" + title="Settings" + > + <Settings className="w-5 h-5" /> + </button> + {/* Legend - Positioned at bottom left */} + <div + id={id} + className="absolute bottom-4 left-4 z-10" + > + {/* Legend expanded content - appears above the button */} {isExpanded && ( - <> - <div className="flex items-center justify-between mb-3"> - <span className="text-sm font-medium text-white">Legend</span> - <button - type="button" - onClick={handleToggleExpanded} - className="text-white/60 hover:text-white transition-colors" - > - <ChevronDown className="w-4 h-4" /> - </button> - </div> + <div className="mb-2 bg-[#0A1628]/95 backdrop-blur-xl border border-[#1E3A5F] rounded-xl overflow-hidden min-w-[280px]"> + {/* Header */} + <button + type="button" + onClick={handleToggleExpanded} + className="w-full flex items-center gap-2 px-4 py-3 text-white font-medium hover:bg-white/5 transition-colors" + > + <ChevronUp className="w-4 h-4" /> + <span>Legend</span> + </button> - <div className="space-y-4"> - {!isLoading && ( - <div> - <div className="text-xs text-white/50 mb-2">Statistics</div> - <div className="space-y-1.5"> - <div className="flex items-center gap-2"> - <Brain className="w-3.5 h-3.5 text-blue-400" /> - <span className="text-xs text-white/70"> - {memoryCount} memories - </span> - </div> - <div className="flex items-center gap-2"> - <FileText className="w-3.5 h-3.5 text-slate-300" /> - <span className="text-xs text-white/70"> - {documentCount} documents - </span> - </div> - <div className="flex items-center gap-2"> - <div className="w-3.5 h-3.5 rounded-full bg-gradient-to-r from-blue-400 to-purple-400" /> - <span className="text-xs text-white/70"> - {edges.length} connections - </span> + <div className="px-4 pb-4"> + {/* STATISTICS */} + <div className="mb-4"> + <div className="text-xs text-[#525D6E] uppercase tracking-wider mb-3"> + Statistics + </div> + <div className="space-y-2"> + {/* Memories */} + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div + className="w-4 h-4" + style={{ + clipPath: + "polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)", + backgroundColor: "rgba(0, 180, 216, 0.4)", + border: "1px solid rgba(0, 180, 216, 0.6)", + }} + /> + <span className="text-white/90">Memories</span> + <ChevronDown className="w-3 h-3 text-white/50" /> </div> + <span className="text-white/60">{memoryCount}</span> </div> - </div> - )} - <div> - <div className="text-xs text-white/50 mb-2">Nodes</div> - <div className="space-y-1.5"> - <div className="flex items-center gap-2"> - <div className="w-4 h-3 rounded bg-white/20 border border-white/40" /> - <span className="text-xs text-white/70">Document</span> + {/* Documents */} + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-4 h-4 rounded-sm bg-[#1E3A5F] border border-[#2E4A6F]" /> + <span className="text-white/90">Documents</span> + <ChevronDown className="w-3 h-3 text-white/50" /> + </div> + <span className="text-white/60">{documentCount}</span> </div> - <div className="flex items-center gap-2"> - <div - className="w-3.5 h-3.5" - style={{ - clipPath: - "polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)", - backgroundColor: "rgba(147, 196, 253, 0.4)", - border: "1px solid rgba(147, 196, 253, 0.6)", - }} - /> - <span className="text-xs text-white/70">Memory</span> + + {/* Connections */} + <div> + <button + type="button" + onClick={() => setConnectionsExpanded(!connectionsExpanded)} + className="w-full flex items-center justify-between" + > + <div className="flex items-center gap-3"> + <svg + width="16" + height="16" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + className="text-white/70" + > + <circle cx="18" cy="5" r="3" /> + <circle cx="6" cy="12" r="3" /> + <circle cx="18" cy="19" r="3" /> + <line x1="8.59" y1="13.51" x2="15.42" y2="17.49" /> + <line x1="15.41" y1="6.51" x2="8.59" y2="10.49" /> + </svg> + <span className="text-white/90">Connections</span> + {connectionsExpanded ? ( + <ChevronUp className="w-3 h-3 text-white/50" /> + ) : ( + <ChevronDown className="w-3 h-3 text-white/50" /> + )} + </div> + <span className="text-white/60">{connectionCount}</span> + </button> + + {/* Expanded connections */} + {connectionsExpanded && ( + <div className="ml-7 mt-2 space-y-2"> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-4 h-0.5 bg-cyan-500/60" /> + <span className="text-white/70 text-sm">Doc > Memory</span> + </div> + </div> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div + className="w-4 h-0.5" + style={{ + background: + "repeating-linear-gradient(90deg, rgba(0, 180, 216, 0.6) 0px, rgba(0, 180, 216, 0.6) 2px, transparent 2px, transparent 4px)", + }} + /> + <span className="text-white/70 text-sm">Doc similarity</span> + </div> + <Toggle enabled={showDocSimilarity} onChange={setShowDocSimilarity} /> + </div> + </div> + )} </div> </div> </div> - <div> - <div className="text-xs text-white/50 mb-2">Connections</div> - <div className="space-y-1.5"> - <div className="flex items-center gap-2"> - <div className="w-4 h-0.5 bg-slate-400/40" /> - <span className="text-xs text-white/70">Doc → Memory</span> + {/* RELATIONS */} + <div className="mb-4"> + <div className="text-xs text-[#525D6E] uppercase tracking-wider mb-3"> + Relations + </div> + <div className="space-y-2"> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-4 h-0.5 bg-pink-500" /> + <span className="text-white/90">Updates</span> + </div> + <Toggle enabled={showUpdates} onChange={setShowUpdates} /> </div> - <div className="flex items-center gap-2"> - <div - className="w-4 h-0.5" - style={{ - background: - "repeating-linear-gradient(90deg, rgba(35, 189, 255, 0.6) 0px, rgba(35, 189, 255, 0.6) 3px, transparent 3px, transparent 6px)", - }} - /> - <span className="text-xs text-white/70">Doc similarity</span> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-4 h-0.5 bg-emerald-500" /> + <span className="text-white/90">Extends</span> + </div> + <Toggle enabled={showExtends} onChange={setShowExtends} /> + </div> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-4 h-0.5 bg-cyan-500" /> + <span className="text-white/90">Inferences</span> + </div> + <Toggle enabled={showInferences} onChange={setShowInferences} /> </div> </div> </div> + {/* SIMILARITY */} <div> - <div className="text-xs text-white/50 mb-2">Similarity</div> - <div className="space-y-1.5"> - <div className="flex items-center gap-2"> - <div className="w-4 h-0.5 bg-cyan-500/30" /> - <span className="text-xs text-white/70">Weak</span> + <div className="text-xs text-[#525D6E] uppercase tracking-wider mb-3"> + Similarity + </div> + <div className="space-y-2"> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-3 h-3 rounded-full bg-[#1E3A5F] border border-[#3E5A7F]" /> + <span className="text-white/90">Strong</span> + </div> + <Toggle enabled={showStrong} onChange={setShowStrong} /> </div> - <div className="flex items-center gap-2"> - <div className="w-4 h-1 bg-cyan-400/90" /> - <span className="text-xs text-white/70">Strong</span> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-3"> + <div className="w-3 h-3 rounded-full bg-[#1E3A5F] border border-[#3E5A7F]" /> + <span className="text-white/90">Weak</span> + </div> + <Toggle enabled={showWeak} onChange={setShowWeak} /> </div> </div> </div> </div> - </> + </div> + )} + + {/* Legend toggle button (collapsed state) */} + {!isExpanded && ( + <button + type="button" + onClick={handleToggleExpanded} + className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-white/70 bg-[#0A1628]/80 backdrop-blur-xl border border-[#1E3A5F] rounded-lg hover:bg-[#0A1628] hover:text-white transition-colors" + > + <ChevronDown className="w-4 h-4" /> + <span>Legend</span> + </button> )} </div> - </div> + </> ) }) |