fix: make all three edge types visually distinct with unique colors

- Derives: soft blue/steel (#7094B8) -- cool desaturated blue for structural
  doc-to-memory links, visible but not attention-grabbing
- Updates: purple (#A78BFA) -- unchanged, glow + arrowheads for version chains
- Extends: sky blue (#38BDF8) -- bright blue with glow + dashed pattern for
  cross-references between memories

Previously derives and extends both used similar teal/cyan colors, making
extends edges invisible among the 220 derives edges. Now each type has a
completely different appearance (steel blue vs purple vs bright cyan dashed).

Also fixed extends edge zoom culling threshold from 0.3 to 0.08 -- extends
edges were being hidden at normal zoom levels (10-26%).

All edge types get glow for luminous aesthetic. Extends edges use dashed
lines matching the legend icon.

- All 107 tests pass, types clean
This commit is contained in:
Vorflux AI 2026-03-29 00:27:18 +00:00
parent 3dcabe41bb
commit 7e31f07924
5 changed files with 16 additions and 16 deletions

View file

@ -633,10 +633,10 @@ describe("getEdgeVisualProps: all MemoryRelation values return valid visual prop
})
}
it("extends edges have lower opacity than derives edges (barely visible)", () => {
it("extends edges have higher opacity than derives edges (rare but meaningful)", () => {
const ext = getEdgeVisualProps("extends")
const der = getEdgeVisualProps("derives")
expect(ext.opacity).toBeLessThan(der.opacity)
expect(ext.opacity).toBeGreaterThan(der.opacity)
})
it("updates edges have higher opacity than derives edges (version chains are prominent)", () => {
@ -647,7 +647,7 @@ describe("getEdgeVisualProps: all MemoryRelation values return valid visual prop
it("unknown edge type returns default props (opacity 0.4, thickness 1.2)", () => {
// The default case returns { opacity: 0.4, thickness: 1.2 }.
// A safe conservative fallback that doesn't match any specific known type.
// A safe conservative fallback matching derives (the most common edge type).
const unknown = getEdgeVisualProps("nonexistent")
expect(unknown.opacity).toBeCloseTo(0.4)
expect(unknown.thickness).toBeCloseTo(1.2)

View file

@ -61,7 +61,7 @@ describe("getMemoryBorderColor", () => {
describe("getEdgeVisualProps", () => {
it("returns correct props for derives edges", () => {
const props = getEdgeVisualProps("derives")
expect(props.opacity).toBeCloseTo(0.45)
expect(props.opacity).toBeCloseTo(0.4)
expect(props.thickness).toBeCloseTo(1.2)
})
@ -73,8 +73,8 @@ describe("getEdgeVisualProps", () => {
it("returns correct props for extends edges", () => {
const props = getEdgeVisualProps("extends")
expect(props.opacity).toBeCloseTo(0.4)
expect(props.thickness).toBeCloseTo(1.2)
expect(props.opacity).toBeCloseTo(0.55)
expect(props.thickness).toBeCloseTo(1.5)
})
it("returns default props for unknown edge types", () => {

View file

@ -59,11 +59,11 @@ function edgeStyle(
colors: GraphThemeColors,
): { color: string; width: number; opacity: number } {
if (edge.edgeType === "derives")
return { color: colors.edgeDerives, width: 1.2, opacity: 0.45 }
return { color: colors.edgeDerives, width: 1.2, opacity: 0.4 }
if (edge.edgeType === "updates")
return { color: colors.edgeUpdates, width: 2, opacity: 0.7 }
// "extends" and any unknown edge types
return { color: colors.edgeExtends, width: 1.2, opacity: 0.4 }
return { color: colors.edgeExtends, width: 1.5, opacity: 0.55 }
}
function batchKey(style: {
@ -101,9 +101,9 @@ function drawEdges(
const prepared: PreparedEdge[] = []
for (const edge of edges) {
// Zoom-based edge culling for extends edges (low-priority visual)
// Zoom-based edge culling for extends edges at very low zoom
if (edge.edgeType === "extends") {
if (viewport.zoom < 0.3) continue
if (viewport.zoom < 0.08) continue
}
const src =
@ -181,12 +181,12 @@ function drawEdges(
const isDimmed = key.endsWith("|d")
const batchEdgeType = first.edgeType
// Draw subtle glow pass behind all edge types (stronger for updates)
// Draw glow pass behind all edge types for luminous aesthetic
if (!isDimmed) {
const glowAlpha =
batchEdgeType === "updates"
? first.style.opacity * 0.4
: first.style.opacity * 0.25
: first.style.opacity * 0.3
const glowWidth =
batchEdgeType === "updates"
? first.style.width + 2

View file

@ -48,9 +48,9 @@ export const DEFAULT_COLORS: GraphThemeColors = {
textPrimary: "#ffffff",
textSecondary: "#e2e8f0",
textMuted: "#94a3b8",
edgeDerives: "#38BDF8",
edgeDerives: "#7094B8",
edgeUpdates: "#A78BFA",
edgeExtends: "#2DD4BF",
edgeExtends: "#38BDF8",
memBorderForgotten: "#EF4444",
memBorderExpiring: "#F59E0B",
memBorderRecent: "#10B981",

View file

@ -30,11 +30,11 @@ export function getMemoryBorderColor(
export function getEdgeVisualProps(edgeType: string) {
switch (edgeType) {
case "derives":
return { opacity: 0.45, thickness: 1.2 }
return { opacity: 0.4, thickness: 1.2 }
case "updates":
return { opacity: 0.7, thickness: 2 }
case "extends":
return { opacity: 0.4, thickness: 1.2 }
return { opacity: 0.55, thickness: 1.5 }
default:
return { opacity: 0.4, thickness: 1.2 }
}