@andespindola/brainlink 0.1.0-beta.158 → 0.1.0-beta.159

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.
@@ -73,6 +73,7 @@ const state = {
73
73
  cells: new Map()
74
74
  },
75
75
  miniMapView: null,
76
+ miniMapDirty: true,
76
77
  overlayScheduled: false,
77
78
  chunk: {
78
79
  nodes: [],
@@ -80,6 +81,8 @@ const state = {
80
81
  },
81
82
  selectedNodeId: null,
82
83
  searchToken: 0,
84
+ searchTimer: null,
85
+ searchResultIds: new Set(),
83
86
  fetchToken: 0,
84
87
  fetchTimer: null,
85
88
  fetchAbortController: null,
@@ -732,7 +735,10 @@ const updateGraphOverlays = () => {
732
735
  requestAnimationFrame(() => {
733
736
  state.overlayScheduled = false
734
737
  drawLabels()
735
- drawMiniMap()
738
+ if (state.miniMapDirty) {
739
+ drawMiniMap()
740
+ state.miniMapDirty = false
741
+ }
736
742
  })
737
743
  }
738
744
 
@@ -963,6 +969,7 @@ const fetchChunk = async ({ fit } = { fit: false }) => {
963
969
  nodes: chunkNodes,
964
970
  edges: normalizeList(chunk.edges)
965
971
  }
972
+ state.miniMapDirty = true
966
973
  state.spatialIndex.key = ''
967
974
  const renderChunk = { ...chunk, nodes: chunkNodes }
968
975
  state.totals = {
@@ -980,6 +987,7 @@ const fetchChunk = async ({ fit } = { fit: false }) => {
980
987
  if (state.renderWorker && state.workerReady) {
981
988
  state.renderWorker.postMessage({ type: 'chunk', chunk: renderChunk })
982
989
  state.renderWorker.postMessage({ type: 'select', id: state.selectedNodeId })
990
+ state.renderWorker.postMessage({ type: 'highlight', ids: Array.from(state.searchResultIds) })
983
991
  }
984
992
 
985
993
  updateGraphOverlays()
@@ -1010,6 +1018,7 @@ const setViewportFromCanvas = () => {
1010
1018
  state.viewport.width = Math.max(320, rect.width)
1011
1019
  state.viewport.height = Math.max(320, rect.height)
1012
1020
  state.viewport.ratio = window.devicePixelRatio || 1
1021
+ state.miniMapDirty = true
1013
1022
  updateWorkerSize()
1014
1023
  drawFallback()
1015
1024
  }
@@ -1278,32 +1287,50 @@ const setupControls = () => {
1278
1287
  })
1279
1288
 
1280
1289
  elements.search.addEventListener('input', () => {
1281
- const token = ++state.searchToken
1282
- const query = (elements.search.value || '').trim()
1283
- if (!query) {
1284
- if (state.renderWorker && state.workerReady) {
1285
- state.renderWorker.postMessage({ type: 'highlight', ids: [] })
1286
- }
1287
- return
1290
+ if (state.searchTimer) {
1291
+ clearTimeout(state.searchTimer)
1288
1292
  }
1289
-
1290
- fetch('/api/graph-filter?q=' + encodeURIComponent(query) + '&limit=1800' + scopeQuery('&'))
1291
- .then((response) => response.json())
1292
- .then((payload) => {
1293
- if (token !== state.searchToken) {
1294
- return
1295
- }
1296
- const ids = Array.isArray(payload?.nodeIds) ? payload.nodeIds : []
1297
- if (state.renderWorker && state.workerReady) {
1298
- state.renderWorker.postMessage({ type: 'highlight', ids })
1299
- }
1300
- })
1301
- .catch((error) => {
1302
- console.error(error)
1303
- })
1293
+ state.searchTimer = setTimeout(() => {
1294
+ state.searchTimer = null
1295
+ runGraphSearch().catch((error) => console.error(error))
1296
+ }, 160)
1304
1297
  })
1305
1298
  }
1306
1299
 
1300
+ const runGraphSearch = async () => {
1301
+ const token = ++state.searchToken
1302
+ const query = (elements.search.value || '').trim()
1303
+ if (!query) {
1304
+ state.searchResultIds = new Set()
1305
+ setFocusedNodeIds(new Set())
1306
+ if (state.renderWorker && state.workerReady) {
1307
+ state.renderWorker.postMessage({ type: 'highlight', ids: [] })
1308
+ }
1309
+ return
1310
+ }
1311
+
1312
+ const response = await fetch('/api/graph-filter?q=' + encodeURIComponent(query) + '&limit=1800' + scopeQuery('&'))
1313
+ if (!response.ok) {
1314
+ throw new Error('Failed to search graph')
1315
+ }
1316
+ const payload = await response.json()
1317
+ if (token !== state.searchToken) {
1318
+ return
1319
+ }
1320
+
1321
+ const ids = Array.isArray(payload?.nodeIds) ? payload.nodeIds.filter((id) => typeof id === 'string' && id.length > 0) : []
1322
+ state.searchResultIds = new Set(ids)
1323
+ setFocusedNodeIds(state.searchResultIds)
1324
+ if (state.renderWorker && state.workerReady) {
1325
+ state.renderWorker.postMessage({ type: 'highlight', ids })
1326
+ }
1327
+ if (ids.length > 0 && state.graphMode === 'far') {
1328
+ state.camera.scale = Math.max(state.camera.scale, 0.82)
1329
+ updateWorkerCamera()
1330
+ scheduleChunkFetch()
1331
+ }
1332
+ }
1333
+
1307
1334
  const loadAgents = async () => {
1308
1335
  const response = await fetch('/api/agents')
1309
1336
  if (!response.ok) {
@@ -5,7 +5,7 @@ import { addVisualContextEdges } from '../domain/graph-contexts.js';
5
5
  import { createCauliflowerGraphLayout } from '../domain/graph-layout.js';
6
6
  import { indexStoragePath } from '../infrastructure/file-index.js';
7
7
  import { getGraphSummary } from './get-graph-summary.js';
8
- const graphLayoutVersion = 8;
8
+ const graphLayoutVersion = 9;
9
9
  const graphLayoutCache = new Map();
10
10
  const safeCacheSegment = (value, fallback) => value?.replace(/[^a-zA-Z0-9_-]/g, '_') || fallback;
11
11
  const graphLayoutStoragePath = (vaultPath, options) => {
@@ -25,6 +25,8 @@ export const inferExplicitVisualGraphContext = (node) => {
25
25
  return context('Git Workflow');
26
26
  if (includesAny(text, [/\bagent memory hub\b/]))
27
27
  return context('Agent Memory');
28
+ if (includesAny(text, [/pingu_ai_codding_pair_programming/, /\bpingu\b/]))
29
+ return context('Pingu');
28
30
  if (path.startsWith('github-repos/'))
29
31
  return context('GitHub Repositories');
30
32
  if (path.startsWith('github-org-repos/'))
@@ -41,21 +43,40 @@ export const inferExplicitVisualGraphContext = (node) => {
41
43
  return context('Nebula');
42
44
  if (includesAny(text, [/\bsnippets?\b/, /\bupgrader\b/, /\bversion-map\b/]))
43
45
  return context('Snippets');
44
- if (includesAny(text, [/\binkdrop\b/]))
45
- return context('Inkdrop');
46
46
  if (includesAny(text, [
47
47
  /\bpreference\b/,
48
+ /\bpreferences\b/,
48
49
  /\bpreferencia\b/,
49
50
  /\bpreferencias\b/,
51
+ /\bpreferência\b/,
52
+ /\bpreferências\b/,
50
53
  /\bplaybook\b/,
51
54
  /\bdirective\b/,
55
+ /\bdirectives\b/,
56
+ /\bdiretiva\b/,
57
+ /\bdiretivas\b/,
52
58
  /\bengineering-style\b/,
53
59
  /\bglobal-engineering\b/,
54
60
  /\bcoding-identity\b/,
55
- /\bagents\.md\b/
61
+ /\bagents\.md\b/,
62
+ /\bagents-md\b/,
63
+ /\bordem direta\b/,
64
+ /\bordem-direta\b/,
65
+ /\bman-in-the-loop\b/,
66
+ /\bconfig geral\b/,
67
+ /\bconfig-geral\b/,
68
+ /\bsync config_files\b/,
69
+ /\bsync-config-files\b/,
70
+ /\bregra operacional\b/,
71
+ /\bregras operacionais\b/,
72
+ /\boperational rule\b/,
73
+ /\boperational rules\b/,
74
+ /\boperational policy\b/
56
75
  ])) {
57
76
  return context('User Preferences');
58
77
  }
78
+ if (includesAny(text, [/\binkdrop\b/]))
79
+ return context('Inkdrop');
59
80
  if (includesAny(text, [/\blazyvim\b/, /\bneovim\b/, /\bnvim\b/, /\bmason\b/, /\bwrapper\b/]))
60
81
  return context('Neovim LazyVim');
61
82
  if (includesAny(text, [/\bgit-flow\b/, /\borigin-sync\b/, /\bgit-identidade\b/, /\bcommit\b/, /\bpush\b/]))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.158",
3
+ "version": "0.1.0-beta.159",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",