@andespindola/brainlink 0.1.0-beta.59 → 0.1.0-beta.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -543,11 +543,12 @@ const filterOverviewClustersByViewport = viewport =>
543
543
  const edgeBudgetForCurrentFrame = () => {
544
544
  const zoom = state.transform.scale
545
545
  if (zoom < 0.12) return 380
546
- if (zoom < 0.18) return 700
547
- if (zoom < 0.28) return 1100
548
- if (zoom < 0.45) return 1600
549
- if (zoom < 0.7) return 2100
550
- return renderEdgeBudget
546
+ if (zoom < 0.18) return 900
547
+ if (zoom < 0.28) return 1700
548
+ if (zoom < 0.45) return 2800
549
+ if (zoom < 0.7) return 4200
550
+ if (zoom < 1.05) return 5600
551
+ return 7600
551
552
  }
552
553
 
553
554
  const clusterBudgetForScale = (scale) => {
@@ -566,13 +567,45 @@ const nodeBudgetForScale = (scale) => {
566
567
  return renderNodeBudget
567
568
  }
568
569
 
570
+ const edgeIdentityKey = edge => {
571
+ if (!edge.target) return ''
572
+ const pair = edge.source < edge.target
573
+ ? edge.source + '|' + edge.target
574
+ : edge.target + '|' + edge.source
575
+ return pair + '|' + (edge.inferred ? 'mesh' : 'real')
576
+ }
577
+
578
+ const edgeRelevanceScore = edge => {
579
+ let score = edgeWeight(edge) * 10
580
+ if (!edge.inferred) {
581
+ score += 8
582
+ }
583
+
584
+ const selectedId = state.selected?.id
585
+ if (selectedId && (edge.source === selectedId || edge.target === selectedId)) {
586
+ score += 120
587
+ }
588
+
589
+ const hoveredId = state.hovered?.id
590
+ if (hoveredId && (edge.source === hoveredId || edge.target === hoveredId)) {
591
+ score += 70
592
+ }
593
+
594
+ const hubId = state.primaryHub?.id
595
+ if (hubId && (edge.source === hubId || edge.target === hubId)) {
596
+ score += 42
597
+ }
598
+
599
+ return score
600
+ }
601
+
569
602
  const collectVisibleEdgesForNodes = nodeIds => {
570
603
  if (nodeIds.size === 0) {
571
604
  return []
572
605
  }
573
606
 
574
607
  const seen = new Set()
575
- const collected = []
608
+ const candidates = []
576
609
  const limit = edgeBudgetForCurrentFrame()
577
610
 
578
611
  nodeIds.forEach(nodeId => {
@@ -582,20 +615,94 @@ const collectVisibleEdgesForNodes = nodeIds => {
582
615
  if (!edge.target || !nodeIds.has(edge.source) || !nodeIds.has(edge.target)) {
583
616
  continue
584
617
  }
585
- const key = edge.source < edge.target
586
- ? edge.source + '|' + edge.target + '|' + edge.targetTitle
587
- : edge.target + '|' + edge.source + '|' + edge.targetTitle
618
+ const key = edgeIdentityKey(edge)
588
619
  if (seen.has(key)) continue
589
620
 
590
621
  seen.add(key)
591
- collected.push(edge)
592
- if (collected.length >= limit) {
593
- return
594
- }
622
+ candidates.push(edge)
595
623
  }
596
624
  })
597
625
 
598
- return collected
626
+ if (candidates.length <= limit) {
627
+ return candidates
628
+ }
629
+
630
+ return candidates
631
+ .sort((left, right) => {
632
+ const scoreDelta = edgeRelevanceScore(right) - edgeRelevanceScore(left)
633
+ if (scoreDelta !== 0) {
634
+ return scoreDelta
635
+ }
636
+ const leftKey = edgeIdentityKey(left)
637
+ const rightKey = edgeIdentityKey(right)
638
+ return leftKey.localeCompare(rightKey)
639
+ })
640
+ .slice(0, limit)
641
+ }
642
+
643
+ const edgeOpacityForScale = (edge, scale) => {
644
+ if (edge.inferred) {
645
+ if (scale < 0.2) return 0.06
646
+ if (scale < 0.4) return 0.08
647
+ if (scale < 0.7) return 0.1
648
+ return 0.14
649
+ }
650
+
651
+ if (scale < 0.2) return 0.14
652
+ if (scale < 0.4) return 0.2
653
+ if (scale < 0.7) return 0.28
654
+ if (scale < 1.05) return 0.36
655
+ return 0.46
656
+ }
657
+
658
+ const edgeStrokeFor = (edge, selectedEdge) => {
659
+ if (selectedEdge) {
660
+ return graphTheme.edgeActive
661
+ }
662
+
663
+ const opacity = edgeOpacityForScale(edge, state.transform.scale)
664
+ return edge.inferred
665
+ ? 'rgba(203, 213, 225, ' + opacity + ')'
666
+ : 'rgba(153, 165, 181, ' + opacity + ')'
667
+ }
668
+
669
+ const edgeWidthFor = (edge, selectedEdge) => {
670
+ if (edge.inferred) {
671
+ return selectedEdge ? 1.22 : 0.84
672
+ }
673
+
674
+ return (selectedEdge ? 1.9 : 1.05) + Math.min(edgeWeight(edge) - 1, 8) * 0.24
675
+ }
676
+
677
+ const drawGraphEdge = (edge) => {
678
+ const selectedEdge = state.selected && (edge.source === state.selected.id || edge.target === state.selected.id)
679
+ ctx.beginPath()
680
+ ctx.moveTo(edge.sourceNode.x, edge.sourceNode.y)
681
+ ctx.lineTo(edge.targetNode.x, edge.targetNode.y)
682
+ ctx.strokeStyle = edgeStrokeFor(edge, selectedEdge)
683
+ ctx.lineWidth = edgeWidthFor(edge, selectedEdge)
684
+ ctx.stroke()
685
+ }
686
+
687
+ const drawGraphEdges = () => {
688
+ const selectedEdges = []
689
+ const regularEdges = []
690
+ for (let index = 0; index < state.renderEdges.length; index += 1) {
691
+ const edge = state.renderEdges[index]
692
+ const isSelected = state.selected && (edge.source === state.selected.id || edge.target === state.selected.id)
693
+ if (isSelected) {
694
+ selectedEdges.push(edge)
695
+ } else {
696
+ regularEdges.push(edge)
697
+ }
698
+ }
699
+
700
+ for (let index = 0; index < regularEdges.length; index += 1) {
701
+ drawGraphEdge(regularEdges[index])
702
+ }
703
+ for (let index = 0; index < selectedEdges.length; index += 1) {
704
+ drawGraphEdge(selectedEdges[index])
705
+ }
599
706
  }
600
707
 
601
708
  const edgePairKey = (source, target) =>
@@ -1544,22 +1651,7 @@ const render = now => {
1544
1651
  state.renderClusters.length === 0 &&
1545
1652
  state.transform.scale >= minimumEdgeScale
1546
1653
  if (drawEdges) {
1547
- state.renderEdges.forEach(edge => {
1548
- const selectedEdge = state.selected && (edge.source === state.selected.id || edge.target === state.selected.id)
1549
- const inferredEdge = Boolean(edge.inferred)
1550
- ctx.beginPath()
1551
- ctx.moveTo(edge.sourceNode.x, edge.sourceNode.y)
1552
- ctx.lineTo(edge.targetNode.x, edge.targetNode.y)
1553
- ctx.strokeStyle = selectedEdge
1554
- ? graphTheme.edgeActive
1555
- : inferredEdge
1556
- ? 'rgba(203, 213, 225, 0.1)'
1557
- : graphTheme.edge
1558
- ctx.lineWidth = inferredEdge
1559
- ? 0.82
1560
- : (selectedEdge ? 1.8 : 1) + Math.min(edgeWeight(edge) - 1, 8) * 0.22
1561
- ctx.stroke()
1562
- })
1654
+ drawGraphEdges()
1563
1655
  }
1564
1656
 
1565
1657
  if (state.renderClusters.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.59",
3
+ "version": "0.1.0-beta.60",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",