mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-15 23:31:05 +00:00
feat: rename edge types to derives/updates/extends with distinct colors
- Rename edge types: doc-memory -> derives, version -> updates, same-space -> extends - Assign distinct colors: derives (#3B82F6 blue), updates (#8B5CF6 purple), extends (#14B8A6 teal) - Update legend labels to show Derives, Updates, Extends - Update theme color properties: edgeDerives, edgeUpdates, edgeExtends - Update MCP app edge types and colors to match - Remove redundant drawDocDocLines proximity rendering (extends edges now serve this purpose) - Update all tests and comments for new edge type names
This commit is contained in:
parent
06e51f2126
commit
b43c311092
10 changed files with 82 additions and 178 deletions
|
|
@ -84,7 +84,7 @@ type GraphNode = MemoryNode | DocumentNode
|
|||
interface GraphLink extends LinkObject {
|
||||
source: string | GraphNode
|
||||
target: string | GraphNode
|
||||
edgeType: "doc-memory" | "version" | "same-space"
|
||||
edgeType: "derives" | "updates" | "extends"
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -99,14 +99,14 @@ const MEMORY_BORDER = {
|
|||
|
||||
const EDGE_COLORS = {
|
||||
dark: {
|
||||
"doc-memory": "#4A5568",
|
||||
version: "#8B5CF6",
|
||||
"same-space": "#00D4B8",
|
||||
derives: "#3B82F6",
|
||||
updates: "#8B5CF6",
|
||||
extends: "#14B8A6",
|
||||
},
|
||||
light: {
|
||||
"doc-memory": "#A0AEC0",
|
||||
version: "#8B5CF6",
|
||||
"same-space": "#0D9488",
|
||||
derives: "#60A5FA",
|
||||
updates: "#8B5CF6",
|
||||
extends: "#2DD4BF",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ function transformData(data: ToolResultData): {
|
|||
const nodeIds = new Set<string>()
|
||||
const SPREAD = 50
|
||||
|
||||
// Group documents by spaceId for same-space edges
|
||||
// Group documents by spaceId for extends edges
|
||||
const spaceGroups = new Map<string, string[]>()
|
||||
|
||||
for (const doc of data.documents) {
|
||||
|
|
@ -214,19 +214,19 @@ function transformData(data: ToolResultData): {
|
|||
} as MemoryNode)
|
||||
nodeIds.add(mem.id)
|
||||
|
||||
// Doc-memory link
|
||||
links.push({ source: doc.id, target: mem.id, edgeType: "doc-memory" })
|
||||
// Derives link
|
||||
links.push({ source: doc.id, target: mem.id, edgeType: "derives" })
|
||||
|
||||
// Version chain link
|
||||
// Updates link
|
||||
if (mem.parentMemoryId && nodeIds.has(mem.parentMemoryId)) {
|
||||
links.push({
|
||||
source: mem.parentMemoryId,
|
||||
target: mem.id,
|
||||
edgeType: "version",
|
||||
edgeType: "updates",
|
||||
})
|
||||
}
|
||||
|
||||
// Track space groups for same-space edges
|
||||
// Track space groups for extends edges
|
||||
if (mem.spaceId) {
|
||||
const group = spaceGroups.get(mem.spaceId)
|
||||
if (group) group.push(doc.id)
|
||||
|
|
@ -235,7 +235,7 @@ function transformData(data: ToolResultData): {
|
|||
}
|
||||
}
|
||||
|
||||
// Same-space edges between documents sharing a space
|
||||
// Extends edges between documents sharing a space
|
||||
const addedEdges = new Set<string>()
|
||||
for (const docIds of spaceGroups.values()) {
|
||||
const unique = [...new Set(docIds)]
|
||||
|
|
@ -247,7 +247,7 @@ function transformData(data: ToolResultData): {
|
|||
links.push({
|
||||
source: unique[i]!,
|
||||
target: unique[j]!,
|
||||
edgeType: "same-space",
|
||||
edgeType: "extends",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -313,7 +313,7 @@ function drawDocumentNode(
|
|||
// =============================================================================
|
||||
function getLinkColor(link: GraphLink): string {
|
||||
const palette = isDark ? EDGE_COLORS.dark : EDGE_COLORS.light
|
||||
return palette[link.edgeType] || palette["doc-memory"]
|
||||
return palette[link.edgeType] || palette["derives"]
|
||||
}
|
||||
|
||||
const graph = new ForceGraph<GraphNode, GraphLink>(container)
|
||||
|
|
@ -366,17 +366,17 @@ const graph = new ForceGraph<GraphNode, GraphLink>(container)
|
|||
},
|
||||
)
|
||||
.linkWidth((link: GraphLink) => {
|
||||
if (link.edgeType === "version") return 2
|
||||
if (link.edgeType === "same-space") return 0.5
|
||||
if (link.edgeType === "updates") return 2
|
||||
if (link.edgeType === "extends") return 0.5
|
||||
return 1
|
||||
})
|
||||
.linkColor(getLinkColor)
|
||||
.linkLineDash((link: GraphLink) => {
|
||||
if (link.edgeType === "same-space") return [4, 2]
|
||||
if (link.edgeType === "extends") return [4, 2]
|
||||
return null as unknown as number[]
|
||||
})
|
||||
.linkDirectionalArrowLength((link: GraphLink) =>
|
||||
link.edgeType === "version" ? 4 : 0,
|
||||
link.edgeType === "updates" ? 4 : 0,
|
||||
)
|
||||
.linkDirectionalArrowRelPos(1)
|
||||
.onNodeClick(handleNodeClick)
|
||||
|
|
@ -390,11 +390,11 @@ const graph = new ForceGraph<GraphNode, GraphLink>(container)
|
|||
.d3Force(
|
||||
"link",
|
||||
forceLink()
|
||||
.distance((l: GraphLink) => (l.edgeType === "doc-memory" ? 40 : 80))
|
||||
.distance((l: GraphLink) => (l.edgeType === "derives" ? 40 : 80))
|
||||
.strength((l: GraphLink) => {
|
||||
if (l.edgeType === "doc-memory") return 0.8
|
||||
if (l.edgeType === "version") return 1.0
|
||||
return 0.15 // same-space
|
||||
if (l.edgeType === "derives") return 0.8
|
||||
if (l.edgeType === "updates") return 1.0
|
||||
return 0.15 // extends
|
||||
}),
|
||||
)
|
||||
.d3Force("collide", forceCollide(18))
|
||||
|
|
|
|||
|
|
@ -59,20 +59,20 @@ describe("getMemoryBorderColor", () => {
|
|||
})
|
||||
|
||||
describe("getEdgeVisualProps", () => {
|
||||
it("returns correct props for doc-memory edges", () => {
|
||||
const props = getEdgeVisualProps("doc-memory")
|
||||
it("returns correct props for derives edges", () => {
|
||||
const props = getEdgeVisualProps("derives")
|
||||
expect(props.opacity).toBeCloseTo(0.3)
|
||||
expect(props.thickness).toBeCloseTo(1.5)
|
||||
})
|
||||
|
||||
it("returns correct props for version edges", () => {
|
||||
const props = getEdgeVisualProps("version")
|
||||
it("returns correct props for updates edges", () => {
|
||||
const props = getEdgeVisualProps("updates")
|
||||
expect(props.opacity).toBeCloseTo(0.6)
|
||||
expect(props.thickness).toBeCloseTo(2)
|
||||
})
|
||||
|
||||
it("returns correct props for same-space edges", () => {
|
||||
const props = getEdgeVisualProps("same-space")
|
||||
it("returns correct props for extends edges", () => {
|
||||
const props = getEdgeVisualProps("extends")
|
||||
expect(props.opacity).toBeCloseTo(0.15)
|
||||
expect(props.thickness).toBeCloseTo(1)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ function makeEdge(source: string, target: string): GraphEdge {
|
|||
source,
|
||||
target,
|
||||
visualProps: { opacity: 0.5, thickness: 1.5 },
|
||||
edgeType: "doc-memory",
|
||||
edgeType: "derives",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,111 +34,21 @@ export function renderFrame(
|
|||
colors: GraphThemeColors,
|
||||
): void {
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
drawDocDocLines(ctx, nodes, viewport, width, height, colors)
|
||||
drawEdges(ctx, edges, viewport, width, height, state, nodeMap, colors)
|
||||
drawNodes(ctx, nodes, viewport, width, height, state, colors)
|
||||
}
|
||||
|
||||
function drawDocDocLines(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
nodes: GraphNode[],
|
||||
viewport: ViewportState,
|
||||
width: number,
|
||||
height: number,
|
||||
colors: GraphThemeColors,
|
||||
): void {
|
||||
// Collect visible docs with screen positions
|
||||
const docs: { x: number; y: number }[] = []
|
||||
for (const n of nodes) {
|
||||
if (n.type !== "document") continue
|
||||
const s = viewport.worldToScreen(n.x, n.y)
|
||||
if (s.x > -100 && s.x < width + 100 && s.y > -100 && s.y < height + 100) {
|
||||
docs.push(s)
|
||||
}
|
||||
}
|
||||
if (docs.length < 2) return
|
||||
|
||||
// Build spatial grid for O(n*k) nearest-neighbor instead of O(n^2)
|
||||
const CELL = 200
|
||||
const grid = new Map<string, number[]>()
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
const d = docs[i]!
|
||||
const key = `${Math.floor(d.x / CELL)},${Math.floor(d.y / CELL)}`
|
||||
let cell = grid.get(key)
|
||||
if (!cell) {
|
||||
cell = []
|
||||
grid.set(key, cell)
|
||||
}
|
||||
cell.push(i)
|
||||
}
|
||||
|
||||
// For each doc, find 2 nearest from neighboring cells only
|
||||
ctx.strokeStyle = colors.edgeSameSpace
|
||||
ctx.lineWidth = 1
|
||||
ctx.globalAlpha = 0.3
|
||||
ctx.setLineDash([4, 6])
|
||||
ctx.beginPath()
|
||||
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
const d = docs[i]!
|
||||
const cx = Math.floor(d.x / CELL)
|
||||
const cy = Math.floor(d.y / CELL)
|
||||
let best1 = -1
|
||||
let best2 = -1
|
||||
let dist1 = Number.POSITIVE_INFINITY
|
||||
let dist2 = Number.POSITIVE_INFINITY
|
||||
|
||||
for (let dx = -1; dx <= 1; dx++) {
|
||||
for (let dy = -1; dy <= 1; dy++) {
|
||||
const cell = grid.get(`${cx + dx},${cy + dy}`)
|
||||
if (!cell) continue
|
||||
for (const j of cell) {
|
||||
if (j === i) continue
|
||||
const other = docs[j]!
|
||||
const ox = other.x - d.x
|
||||
const oy = other.y - d.y
|
||||
const dist = ox * ox + oy * oy
|
||||
if (dist < dist1) {
|
||||
best2 = best1
|
||||
dist2 = dist1
|
||||
best1 = j
|
||||
dist1 = dist
|
||||
} else if (dist < dist2) {
|
||||
best2 = j
|
||||
dist2 = dist
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best1 >= 0 && i < best1) {
|
||||
const b1 = docs[best1]!
|
||||
ctx.moveTo(d.x, d.y)
|
||||
ctx.lineTo(b1.x, b1.y)
|
||||
}
|
||||
if (best2 >= 0 && i < best2) {
|
||||
const b2 = docs[best2]!
|
||||
ctx.moveTo(d.x, d.y)
|
||||
ctx.lineTo(b2.x, b2.y)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stroke()
|
||||
ctx.setLineDash([])
|
||||
ctx.globalAlpha = 1
|
||||
}
|
||||
|
||||
function edgeStyle(
|
||||
edge: GraphEdge,
|
||||
colors: GraphThemeColors,
|
||||
): { color: string; width: number } {
|
||||
if (edge.edgeType === "doc-memory")
|
||||
return { color: colors.edgeDocMemory, width: 1.5 }
|
||||
if (edge.edgeType === "version")
|
||||
return { color: colors.edgeVersion, width: 2 }
|
||||
if (edge.edgeType === "same-space")
|
||||
return { color: colors.edgeSameSpace, width: 1 }
|
||||
return { color: colors.edgeSameSpace, width: 1 }
|
||||
if (edge.edgeType === "derives")
|
||||
return { color: colors.edgeDerives, width: 1.5 }
|
||||
if (edge.edgeType === "updates")
|
||||
return { color: colors.edgeUpdates, width: 2 }
|
||||
if (edge.edgeType === "extends")
|
||||
return { color: colors.edgeExtends, width: 1 }
|
||||
return { color: colors.edgeExtends, width: 1 }
|
||||
}
|
||||
|
||||
function batchKey(style: { color: string; width: number }): string {
|
||||
|
|
@ -152,7 +62,7 @@ interface PreparedEdge {
|
|||
endY: number
|
||||
connected: boolean
|
||||
style: { color: string; width: number }
|
||||
isVersion: boolean
|
||||
isUpdates: boolean
|
||||
arrowSize: number
|
||||
}
|
||||
|
||||
|
|
@ -172,8 +82,8 @@ function drawEdges(
|
|||
const prepared: PreparedEdge[] = []
|
||||
|
||||
for (const edge of edges) {
|
||||
// Zoom-based edge culling for same-space edges (low-priority visual)
|
||||
if (edge.edgeType === "same-space") {
|
||||
// Zoom-based edge culling for extends edges (low-priority visual)
|
||||
if (edge.edgeType === "extends") {
|
||||
if (viewport.zoom < 0.3) continue
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +93,7 @@ function drawEdges(
|
|||
typeof edge.target === "string" ? nodeMap.get(edge.target) : edge.target
|
||||
if (!src || !tgt) continue
|
||||
|
||||
if (edge.edgeType === "doc-memory") {
|
||||
if (edge.edgeType === "derives") {
|
||||
const mem = src.type === "memory" ? src : tgt
|
||||
if (mem.size * viewport.zoom < 3) continue
|
||||
}
|
||||
|
|
@ -226,9 +136,9 @@ function drawEdges(
|
|||
endY: t.y - uy * tr,
|
||||
connected,
|
||||
style: edgeStyle(edge, colors),
|
||||
isVersion: edge.edgeType === "version",
|
||||
isUpdates: edge.edgeType === "updates",
|
||||
arrowSize:
|
||||
edge.edgeType === "version" ? Math.max(6, 8 * viewport.zoom) : 0,
|
||||
edge.edgeType === "updates" ? Math.max(6, 8 * viewport.zoom) : 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -251,10 +161,10 @@ function drawEdges(
|
|||
if (!first) continue
|
||||
const isDimmed = key.endsWith("|d")
|
||||
|
||||
// Draw glow pass behind version edges for visual emphasis
|
||||
// Version edges share a distinct style key, so a batch is all-version or none
|
||||
const isVersionBatch = first.isVersion
|
||||
if (isVersionBatch && !isDimmed) {
|
||||
// Draw glow pass behind updates edges for visual emphasis
|
||||
// Updates edges share a distinct style key, so a batch is all-updates or none
|
||||
const isUpdatesBatch = first.isUpdates
|
||||
if (isUpdatesBatch && !isDimmed) {
|
||||
ctx.save()
|
||||
ctx.globalAlpha = 0.3
|
||||
ctx.strokeStyle = first.style.color
|
||||
|
|
@ -279,7 +189,7 @@ function drawEdges(
|
|||
}
|
||||
ctx.stroke()
|
||||
|
||||
if (isVersionBatch) {
|
||||
if (isUpdatesBatch) {
|
||||
ctx.fillStyle = first.style.color
|
||||
for (const e of batch) {
|
||||
drawArrowHead(ctx, e.startX, e.startY, e.endX, e.endY, e.arrowSize)
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ export class ForceSimulation {
|
|||
d3
|
||||
.forceLink<GraphNode, GraphEdge>(edges)
|
||||
.id((d) => d.id)
|
||||
.distance((link) => (link.edgeType === "doc-memory" ? 150 : 300))
|
||||
.distance((link) => (link.edgeType === "derives" ? 150 : 300))
|
||||
.strength((link) => {
|
||||
if (link.edgeType === "doc-memory") return 0.8
|
||||
if (link.edgeType === "version") return 1.0
|
||||
if (link.edgeType === "same-space") return 0.1
|
||||
if (link.edgeType === "derives") return 0.8
|
||||
if (link.edgeType === "updates") return 1.0
|
||||
if (link.edgeType === "extends") return 0.1
|
||||
return 0.3
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -383,20 +383,20 @@ export const Legend = memo(function Legend({
|
|||
>
|
||||
<div style={rowStyle}>
|
||||
<div style={rowLeftStyle}>
|
||||
<LineIcon color={colors.edgeDocMemory} />
|
||||
<span style={edgeLabelStyle}>Doc > Memory</span>
|
||||
<LineIcon color={colors.edgeDerives} />
|
||||
<span style={edgeLabelStyle}>Derives</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={rowStyle}>
|
||||
<div style={rowLeftStyle}>
|
||||
<LineIcon color={colors.edgeVersion} />
|
||||
<span style={edgeLabelStyle}>Version chain</span>
|
||||
<LineIcon color={colors.edgeUpdates} />
|
||||
<span style={edgeLabelStyle}>Updates</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={rowStyle}>
|
||||
<div style={rowLeftStyle}>
|
||||
<LineIcon color={colors.edgeSameSpace} dashed />
|
||||
<span style={edgeLabelStyle}>Same space</span>
|
||||
<LineIcon color={colors.edgeExtends} dashed />
|
||||
<span style={edgeLabelStyle}>Extends</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ export const DEFAULT_COLORS: GraphThemeColors = {
|
|||
textPrimary: "#ffffff",
|
||||
textSecondary: "#e2e8f0",
|
||||
textMuted: "#94a3b8",
|
||||
edgeDocMemory: "#4A5568",
|
||||
edgeVersion: "#8B5CF6",
|
||||
edgeSameSpace: "#4A6A8A",
|
||||
edgeDerives: "#3B82F6",
|
||||
edgeUpdates: "#8B5CF6",
|
||||
edgeExtends: "#14B8A6",
|
||||
memBorderForgotten: "#EF4444",
|
||||
memBorderExpiring: "#F59E0B",
|
||||
memBorderRecent: "#10B981",
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ export function getMemoryBorderColor(
|
|||
|
||||
export function getEdgeVisualProps(edgeType: string) {
|
||||
switch (edgeType) {
|
||||
case "doc-memory":
|
||||
case "derives":
|
||||
return { opacity: 0.3, thickness: 1.5 }
|
||||
case "version":
|
||||
case "updates":
|
||||
return { opacity: 0.6, thickness: 2 }
|
||||
case "same-space":
|
||||
case "extends":
|
||||
return { opacity: 0.15, thickness: 1 }
|
||||
default:
|
||||
return { opacity: 0.3, thickness: 1 }
|
||||
|
|
@ -168,20 +168,20 @@ export function useGraphData(
|
|||
for (const mem of doc.memories) allNodeIds.add(mem.id)
|
||||
}
|
||||
|
||||
// Doc-memory edges
|
||||
// Derives edges (doc -> memory)
|
||||
for (const doc of documents) {
|
||||
for (const mem of doc.memories) {
|
||||
result.push({
|
||||
id: `dm-${doc.id}-${mem.id}`,
|
||||
source: doc.id,
|
||||
target: mem.id,
|
||||
visualProps: getEdgeVisualProps("doc-memory"),
|
||||
edgeType: "doc-memory",
|
||||
visualProps: getEdgeVisualProps("derives"),
|
||||
edgeType: "derives",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Version chain edges
|
||||
// Updates edges (version chain)
|
||||
for (const doc of documents) {
|
||||
for (const mem of doc.memories) {
|
||||
if (mem.parentMemoryId && allNodeIds.has(mem.parentMemoryId)) {
|
||||
|
|
@ -189,14 +189,14 @@ export function useGraphData(
|
|||
id: `ver-${mem.parentMemoryId}-${mem.id}`,
|
||||
source: mem.parentMemoryId,
|
||||
target: mem.id,
|
||||
visualProps: getEdgeVisualProps("version"),
|
||||
edgeType: "version",
|
||||
visualProps: getEdgeVisualProps("updates"),
|
||||
edgeType: "updates",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same-space edges: connect documents that share a spaceId
|
||||
// Extends edges: connect documents that share a spaceId
|
||||
const spaceGroups = new Map<string, string[]>()
|
||||
for (const doc of documents) {
|
||||
for (const mem of doc.memories) {
|
||||
|
|
@ -221,8 +221,8 @@ export function useGraphData(
|
|||
id: `ss-${key}`,
|
||||
source: a,
|
||||
target: b,
|
||||
visualProps: getEdgeVisualProps("same-space"),
|
||||
edgeType: "same-space",
|
||||
visualProps: getEdgeVisualProps("extends"),
|
||||
edgeType: "extends",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,15 +32,9 @@ function resolveColors(): GraphThemeColors {
|
|||
DEFAULT_COLORS.textSecondary,
|
||||
),
|
||||
textMuted: readCssVar("--graph-text-muted", DEFAULT_COLORS.textMuted),
|
||||
edgeDocMemory: readCssVar(
|
||||
"--graph-edge-doc-mem",
|
||||
DEFAULT_COLORS.edgeDocMemory,
|
||||
),
|
||||
edgeVersion: readCssVar("--graph-edge-version", DEFAULT_COLORS.edgeVersion),
|
||||
edgeSameSpace: readCssVar(
|
||||
"--graph-edge-same-space",
|
||||
DEFAULT_COLORS.edgeSameSpace,
|
||||
),
|
||||
edgeDerives: readCssVar("--graph-edge-derives", DEFAULT_COLORS.edgeDerives),
|
||||
edgeUpdates: readCssVar("--graph-edge-updates", DEFAULT_COLORS.edgeUpdates),
|
||||
edgeExtends: readCssVar("--graph-edge-extends", DEFAULT_COLORS.edgeExtends),
|
||||
memBorderForgotten: readCssVar(
|
||||
"--graph-mem-border-forgotten",
|
||||
DEFAULT_COLORS.memBorderForgotten,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export interface GraphApiDocument {
|
|||
export interface GraphApiEdge {
|
||||
source: string
|
||||
target: string
|
||||
edgeType: "doc-memory" | "version" | "same-space"
|
||||
edgeType: "derives" | "updates" | "extends"
|
||||
}
|
||||
|
||||
// Typed node data
|
||||
|
|
@ -86,7 +86,7 @@ export interface GraphEdge {
|
|||
opacity: number
|
||||
thickness: number
|
||||
}
|
||||
edgeType: "doc-memory" | "version" | "same-space"
|
||||
edgeType: "derives" | "updates" | "extends"
|
||||
}
|
||||
|
||||
export interface GraphThemeColors {
|
||||
|
|
@ -101,9 +101,9 @@ export interface GraphThemeColors {
|
|||
textPrimary: string
|
||||
textSecondary: string
|
||||
textMuted: string
|
||||
edgeDocMemory: string
|
||||
edgeVersion: string
|
||||
edgeSameSpace: string
|
||||
edgeDerives: string
|
||||
edgeUpdates: string
|
||||
edgeExtends: string
|
||||
memBorderForgotten: string
|
||||
memBorderExpiring: string
|
||||
memBorderRecent: string
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue