@andespindola/brainlink 0.1.0-beta.145 → 0.1.0-beta.147

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.
@@ -32,8 +32,11 @@ const state = {
32
32
  pointer: {
33
33
  down: false,
34
34
  moved: false,
35
+ dragging: false,
35
36
  x: 0,
36
37
  y: 0,
38
+ startX: 0,
39
+ startY: 0,
37
40
  worldAnchorX: 0,
38
41
  worldAnchorY: 0
39
42
  },
@@ -56,6 +59,8 @@ const state = {
56
59
  searchToken: 0,
57
60
  fetchToken: 0,
58
61
  fetchTimer: null,
62
+ cameraSyncScheduled: false,
63
+ lastDragFetchAt: 0,
59
64
  lastVisibleNodes: 0,
60
65
  lastVisibleEdges: 0,
61
66
  totals: {
@@ -242,9 +247,19 @@ const updateWorkerCamera = () => {
242
247
  if (!state.renderWorker || !state.workerReady) {
243
248
  return
244
249
  }
245
- state.renderWorker.postMessage({
246
- type: 'camera',
247
- camera: state.camera
250
+ if (state.cameraSyncScheduled) {
251
+ return
252
+ }
253
+ state.cameraSyncScheduled = true
254
+ requestAnimationFrame(() => {
255
+ state.cameraSyncScheduled = false
256
+ if (!state.renderWorker || !state.workerReady) {
257
+ return
258
+ }
259
+ state.renderWorker.postMessage({
260
+ type: 'camera',
261
+ camera: state.camera
262
+ })
248
263
  })
249
264
  }
250
265
 
@@ -542,7 +557,43 @@ const setViewportFromCanvas = () => {
542
557
  drawFallback()
543
558
  }
544
559
 
560
+ const pickFallbackNodeId = (screenX, screenY) => {
561
+ const nodes = normalizeList(state.chunk.nodes)
562
+ if (nodes.length === 0) {
563
+ return ''
564
+ }
565
+
566
+ let bestId = ''
567
+ let bestDistance = Infinity
568
+ for (let index = 0; index < nodes.length; index += 1) {
569
+ const node = nodes[index]
570
+ const id = typeof node[0] === 'string' ? node[0] : ''
571
+ if (!id) continue
572
+ const x = Number(node[2])
573
+ const y = Number(node[3])
574
+ const weight = Number(node[7])
575
+ if (!Number.isFinite(x) || !Number.isFinite(y)) continue
576
+ const point = worldToScreen(x, y)
577
+ const radius = Math.max(2.4, Math.min(14, 4 + (Number.isFinite(weight) ? weight : 0) * 0.55))
578
+ const distance = Math.hypot(screenX - point.x, screenY - point.y)
579
+ if (distance <= radius && distance < bestDistance) {
580
+ bestDistance = distance
581
+ bestId = id
582
+ }
583
+ }
584
+
585
+ return bestId
586
+ }
587
+
545
588
  const pickAt = (screenX, screenY) => {
589
+ if (state.rendererMode === 'fallback') {
590
+ const nodeId = pickFallbackNodeId(screenX, screenY)
591
+ if (nodeId) {
592
+ loadNodeDetails(nodeId).catch((error) => console.error(error))
593
+ }
594
+ return
595
+ }
596
+
546
597
  if (!state.renderWorker || !state.workerReady) {
547
598
  return
548
599
  }
@@ -575,6 +626,8 @@ const resolvePointer = (event) => {
575
626
  }
576
627
 
577
628
  const setupInput = () => {
629
+ const dragActivationDistance = 6
630
+
578
631
  canvas.addEventListener('wheel', (event) => {
579
632
  event.preventDefault()
580
633
  const pointer = resolvePointer(event)
@@ -586,8 +639,11 @@ const setupInput = () => {
586
639
  const pointer = resolvePointer(event)
587
640
  state.pointer.down = true
588
641
  state.pointer.moved = false
642
+ state.pointer.dragging = false
589
643
  state.pointer.x = pointer.x
590
644
  state.pointer.y = pointer.y
645
+ state.pointer.startX = pointer.x
646
+ state.pointer.startY = pointer.y
591
647
  const world = screenToWorld(pointer.x, pointer.y)
592
648
  state.pointer.worldAnchorX = world.x
593
649
  state.pointer.worldAnchorY = world.y
@@ -600,15 +656,26 @@ const setupInput = () => {
600
656
  if (state.pointer.down) {
601
657
  const dx = pointer.x - state.pointer.x
602
658
  const dy = pointer.y - state.pointer.y
603
- if (Math.abs(dx) + Math.abs(dy) > 2) {
659
+ const distanceFromStart = Math.hypot(pointer.x - state.pointer.startX, pointer.y - state.pointer.startY)
660
+ if (distanceFromStart >= dragActivationDistance) {
604
661
  state.pointer.moved = true
662
+ state.pointer.dragging = true
663
+ }
664
+ if (!state.pointer.dragging) {
665
+ state.pointer.x = pointer.x
666
+ state.pointer.y = pointer.y
667
+ return
605
668
  }
606
669
  state.camera.x += dx
607
670
  state.camera.y += dy
608
671
  state.pointer.x = pointer.x
609
672
  state.pointer.y = pointer.y
610
673
  updateWorkerCamera()
611
- scheduleChunkFetch()
674
+ const now = performance.now()
675
+ if (now - state.lastDragFetchAt > 180) {
676
+ state.lastDragFetchAt = now
677
+ scheduleChunkFetch()
678
+ }
612
679
  drawFallback()
613
680
  return
614
681
  }
@@ -624,12 +691,19 @@ const setupInput = () => {
624
691
 
625
692
  canvas.addEventListener('pointerup', (event) => {
626
693
  const pointer = resolvePointer(event)
627
- const shouldPick = !state.pointer.moved
694
+ const distanceFromStart = Math.hypot(pointer.x - state.pointer.startX, pointer.y - state.pointer.startY)
695
+ const shouldPick = !state.pointer.dragging && distanceFromStart < dragActivationDistance
696
+ const shouldRefreshAfterDrag = state.pointer.dragging
628
697
  state.pointer.down = false
698
+ state.pointer.dragging = false
629
699
  canvas.releasePointerCapture(event.pointerId)
630
700
 
631
701
  if (shouldPick) {
632
702
  pickAt(pointer.x, pointer.y)
703
+ return
704
+ }
705
+ if (shouldRefreshAfterDrag) {
706
+ scheduleChunkFetch()
633
707
  }
634
708
  })
635
709
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.145",
3
+ "version": "0.1.0-beta.147",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",