-
No memories found
+
No memories found
diff --git a/apps/web/components/graph/constants.ts b/apps/web/components/graph/constants.ts
index 15dbe9b8..8383b6ff 100644
--- a/apps/web/components/graph/constants.ts
+++ b/apps/web/components/graph/constants.ts
@@ -1,38 +1,50 @@
export const colors = {
background: {
- primary: "#0f1419",
- secondary: "#1a1f29",
- accent: "#252a35",
+ primary: "#000B1B",
+ secondary: "#01173C",
+ accent: "#0A2351",
},
+ // All nodes are now hexagons with cyan color
+ node: {
+ primary: "rgba(0, 180, 216, 0.25)",
+ secondary: "rgba(0, 180, 216, 0.35)",
+ accent: "rgba(0, 180, 216, 0.45)",
+ border: "rgba(0, 180, 216, 0.7)",
+ glow: "rgba(0, 180, 216, 0.5)",
+ },
+ // Legacy document colors (for compatibility)
document: {
- primary: "rgba(255, 255, 255, 0.21)",
- secondary: "rgba(255, 255, 255, 0.31)",
- accent: "rgba(255, 255, 255, 0.31)",
- border: "rgba(255, 255, 255, 0.6)",
- glow: "rgba(147, 197, 253, 0.4)",
+ primary: "rgba(0, 180, 216, 0.25)",
+ secondary: "rgba(0, 180, 216, 0.35)",
+ accent: "rgba(0, 180, 216, 0.45)",
+ border: "rgba(0, 180, 216, 0.7)",
+ glow: "rgba(0, 180, 216, 0.5)",
},
+ // Legacy memory colors (for compatibility)
memory: {
- primary: "rgba(147, 196, 253, 0.21)",
- secondary: "rgba(147, 196, 253, 0.31)",
- accent: "rgba(147, 197, 253, 0.31)",
- border: "rgba(147, 196, 253, 0.6)",
- glow: "rgba(147, 197, 253, 0.5)",
+ primary: "rgba(0, 180, 216, 0.25)",
+ secondary: "rgba(0, 180, 216, 0.35)",
+ accent: "rgba(0, 180, 216, 0.45)",
+ border: "rgba(0, 180, 216, 0.7)",
+ glow: "rgba(0, 180, 216, 0.5)",
},
connection: {
- weak: "rgba(35, 189, 255, 0.3)",
- memory: "rgba(148, 163, 184, 0.35)",
- medium: "rgba(35, 189, 255, 0.6)",
- strong: "rgba(35, 189, 255, 0.9)",
+ weak: "rgba(0, 180, 216, 0.3)",
+ memory: "rgba(0, 180, 216, 0.4)",
+ medium: "rgba(0, 180, 216, 0.6)",
+ strong: "rgba(0, 180, 216, 0.9)",
+ // Pink/magenta for special relationships
+ relation: "rgba(236, 72, 153, 0.6)",
},
text: {
primary: "#ffffff",
secondary: "#e2e8f0",
- muted: "#94a3b8",
+ muted: "#525D6E",
},
accent: {
- primary: "rgba(59, 130, 246, 0.7)",
- secondary: "rgba(99, 102, 241, 0.6)",
- glow: "rgba(147, 197, 253, 0.6)",
+ primary: "rgba(0, 180, 216, 0.7)",
+ secondary: "rgba(34, 97, 202, 0.6)",
+ glow: "rgba(0, 180, 216, 0.6)",
amber: "rgba(251, 165, 36, 0.8)",
emerald: "rgba(16, 185, 129, 0.4)",
},
@@ -42,9 +54,9 @@ export const colors = {
new: "rgba(16, 185, 129, 0.4)",
},
relations: {
- updates: "rgba(147, 77, 253, 0.5)",
+ updates: "rgba(236, 72, 153, 0.5)",
extends: "rgba(16, 185, 129, 0.5)",
- derives: "rgba(147, 197, 253, 0.5)",
+ derives: "rgba(0, 180, 216, 0.5)",
},
}
diff --git a/apps/web/components/graph/graph-canvas.tsx b/apps/web/components/graph/graph-canvas.tsx
index f78a9b25..634cdfad 100644
--- a/apps/web/components/graph/graph-canvas.tsx
+++ b/apps/web/components/graph/graph-canvas.tsx
@@ -138,28 +138,14 @@ export const GraphCanvas = memo
(
const screenY = node.y * zoom + panY
const nodeSize = node.size * zoom
- if (node.type === "document") {
- const docWidth = nodeSize * 1.4
- const docHeight = nodeSize * 0.9
- const halfW = docWidth / 2
- const halfH = docHeight / 2
+ // All nodes are hexagons now, use circular hit detection
+ const radius = node.type === "document" ? nodeSize * 0.6 : nodeSize / 2
+ const dx = x - screenX
+ const dy = y - screenY
+ const distance = Math.sqrt(dx * dx + dy * dy)
- if (
- x >= screenX - halfW &&
- x <= screenX + halfW &&
- y >= screenY - halfH &&
- y <= screenY + halfH
- ) {
- return node.id
- }
- } else {
- const dx = x - screenX
- const dy = y - screenY
- const distance = Math.sqrt(dx * dx + dy * dy)
-
- if (distance <= nodeSize / 2) {
- return node.id
- }
+ if (distance <= radius) {
+ return node.id
}
}
}
@@ -227,23 +213,20 @@ export const GraphCanvas = memo(
ctx.clearRect(0, 0, width, height)
- ctx.strokeStyle = "rgba(148, 163, 184, 0.03)"
- ctx.lineWidth = 1
- const gridSpacing = 100 * zoom
- const offsetX = panX % gridSpacing
- const offsetY = panY % gridSpacing
+ // Draw dotted background pattern
+ const dotSpacing = 30 * Math.max(zoom, 0.5) // Maintain reasonable spacing
+ const offsetX = panX % dotSpacing
+ const offsetY = panY % dotSpacing
+ const dotRadius = 1
- for (let x = offsetX; x < width; x += gridSpacing) {
- ctx.beginPath()
- ctx.moveTo(x, 0)
- ctx.lineTo(x, height)
- ctx.stroke()
- }
- for (let y = offsetY; y < height; y += gridSpacing) {
- ctx.beginPath()
- ctx.moveTo(0, y)
- ctx.lineTo(width, y)
- ctx.stroke()
+ ctx.fillStyle = "rgba(0, 180, 216, 0.15)"
+
+ for (let x = offsetX; x < width; x += dotSpacing) {
+ for (let y = offsetY; y < height; y += dotSpacing) {
+ ctx.beginPath()
+ ctx.arc(x, y, dotRadius, 0, 2 * Math.PI)
+ ctx.fill()
+ }
}
ctx.lineCap = "round"
@@ -420,115 +403,71 @@ export const GraphCanvas = memo(
return highlightSet.has(doc.id)
})()
- if (node.type === "document") {
- const docWidth = nodeSize * 1.4
- const docHeight = nodeSize * 0.9
+ // All nodes are now rendered as hexagons
+ const isDocument = node.type === "document"
+ const isMemory = node.type === "memory"
- ctx.fillStyle = isHovered
- ? colors.document.secondary
- : colors.document.primary
- ctx.globalAlpha = nodeOpacity
+ // Get node data for status checks
+ let isNew = false
+ if (isMemory) {
+ const mem = node.data as ViewportMemoryEntry
+ isNew = new Date(mem.createdAt).getTime() > Date.now() - 1000 * 60 * 60 * 24
+ }
- ctx.strokeStyle = isHovered
- ? colors.document.accent
- : colors.document.border
- ctx.lineWidth = isHovered ? 2 : 1
+ // Use consistent node colors for all nodes
+ let fillColor = colors.node.primary
+ let borderColor = colors.node.border
- const radius = useSimplifiedRendering ? 6 : 12
+ if (isHovered) {
+ fillColor = colors.node.secondary
+ }
+
+ if (isNew) {
+ borderColor = colors.status.new
+ }
+
+ // Document nodes are slightly larger
+ const radius = isDocument ? nodeSize * 0.6 : nodeSize / 2
+
+ ctx.fillStyle = fillColor
+ ctx.globalAlpha = shouldDim ? nodeOpacity : 1
+ ctx.strokeStyle = borderColor
+ ctx.lineWidth = isHovered ? 2 : 1.5
+
+ if (useSimplifiedRendering) {
+ // Simple circle for low zoom
ctx.beginPath()
- ctx.roundRect(
- screenX - docWidth / 2,
- screenY - docHeight / 2,
- docWidth,
- docHeight,
- radius,
- )
+ ctx.arc(screenX, screenY, radius, 0, 2 * Math.PI)
+ ctx.fill()
+ ctx.stroke()
+ } else {
+ // Hexagon for normal zoom
+ const sides = 6
+ ctx.beginPath()
+ for (let i = 0; i < sides; i++) {
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2
+ const x = screenX + radius * Math.cos(angle)
+ const y = screenY + radius * Math.sin(angle)
+ if (i === 0) {
+ ctx.moveTo(x, y)
+ } else {
+ ctx.lineTo(x, y)
+ }
+ }
+ ctx.closePath()
ctx.fill()
ctx.stroke()
- if (!useSimplifiedRendering && isHovered) {
- ctx.strokeStyle = "rgba(255, 255, 255, 0.1)"
- ctx.lineWidth = 1
- ctx.beginPath()
- ctx.roundRect(
- screenX - docWidth / 2 + 1,
- screenY - docHeight / 2 + 1,
- docWidth - 2,
- docHeight - 2,
- radius - 1,
- )
- ctx.stroke()
- }
-
- if (isHighlightedDocument) {
- ctx.save()
- ctx.globalAlpha = 0.9
- ctx.strokeStyle = colors.accent.primary
- ctx.lineWidth = 3
- ctx.setLineDash([6, 4])
- const avgDimension = (docWidth + docHeight) / 2
- const ringPadding = avgDimension * 0.1
- ctx.beginPath()
- ctx.roundRect(
- screenX - docWidth / 2 - ringPadding,
- screenY - docHeight / 2 - ringPadding,
- docWidth + ringPadding * 2,
- docHeight + ringPadding * 2,
- radius + 6,
- )
- ctx.stroke()
- ctx.setLineDash([])
- ctx.restore()
- }
-
- if (!useSimplifiedRendering) {
- const doc = node.data as ViewportDocument
- const iconSize = docHeight * 0.4
-
- drawDocumentIcon(
- ctx,
- screenX,
- screenY,
- iconSize,
- doc.type || "text",
- "rgba(255, 255, 255, 0.8)",
- )
- }
- } else {
- const mem = node.data as ViewportMemoryEntry
- const isNew =
- new Date(mem.createdAt).getTime() > Date.now() - 1000 * 60 * 60 * 24
-
- let fillColor = colors.memory.primary
- let borderColor = colors.memory.border
-
+ // Inner highlight on hover
if (isHovered) {
- fillColor = colors.memory.secondary
- }
-
- if (isNew) {
- borderColor = colors.status.new
- }
-
- const radius = nodeSize / 2
-
- ctx.fillStyle = fillColor
- ctx.globalAlpha = shouldDim ? nodeOpacity : 1
- ctx.strokeStyle = borderColor
- ctx.lineWidth = isHovered ? 2 : 1.5
-
- if (useSimplifiedRendering) {
- ctx.beginPath()
- ctx.arc(screenX, screenY, radius, 0, 2 * Math.PI)
- ctx.fill()
- ctx.stroke()
- } else {
- const sides = 6
+ ctx.strokeStyle = "rgba(0, 180, 216, 0.3)"
+ ctx.lineWidth = 1
+ const innerRadius = radius - 2
ctx.beginPath()
for (let i = 0; i < sides; i++) {
const angle = (i * 2 * Math.PI) / sides - Math.PI / 2
- const x = screenX + radius * Math.cos(angle)
- const y = screenY + radius * Math.sin(angle)
+ const x = screenX + innerRadius * Math.cos(angle)
+ const y = screenY + innerRadius * Math.sin(angle)
if (i === 0) {
ctx.moveTo(x, y)
} else {
@@ -536,80 +475,73 @@ export const GraphCanvas = memo(
}
}
ctx.closePath()
- ctx.fill()
ctx.stroke()
-
- if (isHovered) {
- ctx.strokeStyle = "rgba(147, 197, 253, 0.3)"
- ctx.lineWidth = 1
- const innerRadius = radius - 2
- ctx.beginPath()
- for (let i = 0; i < sides; i++) {
- const angle = (i * 2 * Math.PI) / sides - Math.PI / 2
- const x = screenX + innerRadius * Math.cos(angle)
- const y = screenY + innerRadius * Math.sin(angle)
- if (i === 0) {
- ctx.moveTo(x, y)
- } else {
- ctx.lineTo(x, y)
- }
- }
- ctx.closePath()
- ctx.stroke()
- }
- }
-
- if (isNew) {
- ctx.fillStyle = colors.status.new
- ctx.beginPath()
- ctx.arc(
- screenX + nodeSize * 0.25,
- screenY - nodeSize * 0.25,
- Math.max(2, nodeSize * 0.15),
- 0,
- 2 * Math.PI,
- )
- ctx.fill()
}
}
+ // Highlighted document indicator
+ if (isHighlightedDocument) {
+ ctx.save()
+ ctx.globalAlpha = 0.9
+ ctx.strokeStyle = colors.accent.primary
+ ctx.lineWidth = 3
+ ctx.setLineDash([6, 4])
+ const highlightRadius = radius * 1.2
+ const sides = 6
+ ctx.beginPath()
+ for (let i = 0; i < sides; i++) {
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2
+ const x = screenX + highlightRadius * Math.cos(angle)
+ const y = screenY + highlightRadius * Math.sin(angle)
+ if (i === 0) {
+ ctx.moveTo(x, y)
+ } else {
+ ctx.lineTo(x, y)
+ }
+ }
+ ctx.closePath()
+ ctx.stroke()
+ ctx.setLineDash([])
+ ctx.restore()
+ }
+
+ // New item indicator dot
+ if (isNew) {
+ ctx.fillStyle = colors.status.new
+ ctx.beginPath()
+ ctx.arc(
+ screenX + nodeSize * 0.25,
+ screenY - nodeSize * 0.25,
+ Math.max(2, nodeSize * 0.15),
+ 0,
+ 2 * Math.PI,
+ )
+ ctx.fill()
+ }
+
+ // Unified glow effect for all nodes (hexagon)
if (!useSimplifiedRendering && isHovered) {
- const glowColor =
- node.type === "document" ? colors.document.glow : colors.memory.glow
+ const glowColor = colors.node.glow
ctx.strokeStyle = glowColor
ctx.lineWidth = 1
ctx.setLineDash([3, 3])
ctx.globalAlpha = 0.6
+ const glowRadius = (node.type === "document" ? nodeSize * 0.6 : nodeSize / 2) * 1.3
+ const sides = 6
ctx.beginPath()
- if (node.type === "document") {
- const docWidth = nodeSize * 1.4
- const docHeight = nodeSize * 0.9
- const avgDimension = (docWidth + docHeight) / 2
- const glowPadding = avgDimension * 0.1
- ctx.roundRect(
- screenX - docWidth / 2 - glowPadding,
- screenY - docHeight / 2 - glowPadding,
- docWidth + glowPadding * 2,
- docHeight + glowPadding * 2,
- 15,
- )
- } else {
- const glowRadius = nodeSize * 0.7
- const sides = 6
- for (let i = 0; i < sides; i++) {
- const angle = (i * 2 * Math.PI) / sides - Math.PI / 2
- const x = screenX + glowRadius * Math.cos(angle)
- const y = screenY + glowRadius * Math.sin(angle)
- if (i === 0) {
- ctx.moveTo(x, y)
- } else {
- ctx.lineTo(x, y)
- }
+ for (let i = 0; i < sides; i++) {
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2
+ const x = screenX + glowRadius * Math.cos(angle)
+ const y = screenY + glowRadius * Math.sin(angle)
+ if (i === 0) {
+ ctx.moveTo(x, y)
+ } else {
+ ctx.lineTo(x, y)
}
- ctx.closePath()
}
+ ctx.closePath()
ctx.stroke()
ctx.setLineDash([])
}
diff --git a/apps/web/components/graph/graph.tsx b/apps/web/components/graph/graph.tsx
index ed5542e2..a9e7e73e 100644
--- a/apps/web/components/graph/graph.tsx
+++ b/apps/web/components/graph/graph.tsx
@@ -436,6 +436,7 @@ export function Graph({ containerTags, children }: GraphProps) {
isTimelineActive={isTimelineStreaming}
timelineProgress={timelineProgress}
nodes={nodes}
+ zoom={zoom}
/>
)}
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 (
+
+ )
+}
+
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 (
-