updated changelog and refactored popover logic to eliminate unnecessary calculations during smooth pan/zoom animations

This commit is contained in:
Vidya Rupak 2025-12-21 21:40:35 -07:00
parent ad51532eba
commit 696f5bbffa
2 changed files with 65 additions and 46 deletions

View file

@ -2,6 +2,20 @@
## Visual & Layout Improvements (2025-12-21)
### Smart Positioning Node Popover
**Feature:** Clicking nodes now shows a floating popover with detailed information, positioned near the node with smart viewport edge detection.
**Implementation:**
- DOM-based popover
- Auto-positions to avoid viewport edges
- Click-outside to close
- Shows all node metadata: title, summary (2-line truncation), type, memory count, URL, creation date, and ID
- For memories: includes space, expiration date (if applicable), and forgotten status
**Files Changed:**
- `src/components/node-popover.tsx` - New popover component
- `src/components/memory-graph.tsx` - Smart positioning logic
### Document Type Icons on Cards
**Feature:** Document cards now display type-specific icons centered on the card for better visual identification.

View file

@ -405,6 +405,49 @@ export const MemoryGraph = ({
return nodes.find((n) => n.id === selectedNode) || null
}, [selectedNode, nodes])
// Calculate popover position (memoized for performance)
const popoverPosition = useMemo(() => {
if (!selectedNodeData) return null
// Calculate screen position of the node
const screenX = selectedNodeData.x * zoom + panX
const screenY = selectedNodeData.y * zoom + panY
// Popover dimensions (estimated)
const popoverWidth = 320
const popoverHeight = 400
const offset = 40
const padding = 16
// Smart positioning: flip to other side if would go off-screen
let popoverX = screenX + offset
let popoverY = screenY - 20
// Check right edge
if (popoverX + popoverWidth > containerSize.width - padding) {
// Flip to left side of node
popoverX = screenX - popoverWidth - offset
}
// Check left edge
if (popoverX < padding) {
popoverX = padding
}
// Check bottom edge
if (popoverY + popoverHeight > containerSize.height - padding) {
// Move up
popoverY = containerSize.height - popoverHeight - padding
}
// Check top edge
if (popoverY < padding) {
popoverY = padding
}
return { x: popoverX, y: popoverY }
}, [selectedNodeData, zoom, panX, containerSize.width, containerSize.height])
// Viewport-based loading: load more when most documents are visible (optional)
const checkAndLoadMore = useCallback(() => {
if (
@ -532,52 +575,14 @@ export const MemoryGraph = ({
/>
{/* Node popover - positioned near clicked node */}
{selectedNodeData && (() => {
// Calculate screen position of the node
const screenX = selectedNodeData.x * zoom + panX
const screenY = selectedNodeData.y * zoom + panY
// Popover dimensions (estimated)
const popoverWidth = 300
const popoverHeight = 200
const offset = 40
const padding = 16
// Smart positioning: flip to other side if would go off-screen
let popoverX = screenX + offset
let popoverY = screenY - 20
// Check right edge
if (popoverX + popoverWidth > containerSize.width - padding) {
// Flip to left side of node
popoverX = screenX - popoverWidth - offset
}
// Check left edge
if (popoverX < padding) {
popoverX = padding
}
// Check bottom edge
if (popoverY + popoverHeight > containerSize.height - padding) {
// Move up
popoverY = containerSize.height - popoverHeight - padding
}
// Check top edge
if (popoverY < padding) {
popoverY = padding
}
return (
<NodePopover
node={selectedNodeData}
x={popoverX}
y={popoverY}
onClose={() => setSelectedNode(null)}
/>
)
})()}
{selectedNodeData && popoverPosition && (
<NodePopover
node={selectedNodeData}
x={popoverPosition.x}
y={popoverPosition.y}
onClose={() => setSelectedNode(null)}
/>
)}
{/* Show welcome screen when no memories exist */}
{!isLoading &&