mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-19 16:13:19 +00:00
updated changelog and refactored popover logic to eliminate unnecessary calculations during smooth pan/zoom animations
This commit is contained in:
parent
ad51532eba
commit
696f5bbffa
2 changed files with 65 additions and 46 deletions
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue