@andespindola/brainlink 0.1.0-beta.146 → 0.1.0-beta.148

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.
@@ -258,12 +258,12 @@ li small {
258
258
 
259
259
  .content-dialog {
260
260
  position: fixed;
261
- top: 84px;
262
- right: 12px;
261
+ top: 74px;
262
+ right: 16px;
263
263
  margin: 0;
264
- width: min(440px, calc(100vw - 24px));
265
- height: min(calc(100svh - 124px), 820px);
266
- max-height: calc(100svh - 124px);
264
+ width: min(760px, calc(100vw - 32px));
265
+ height: min(calc(100svh - 96px), 920px);
266
+ max-height: calc(100svh - 96px);
267
267
  padding: 0;
268
268
  border: 1px solid var(--line);
269
269
  border-radius: 8px;
@@ -331,7 +331,7 @@ li small {
331
331
 
332
332
  .content-meta {
333
333
  display: grid;
334
- grid-template-columns: repeat(3, minmax(0, 1fr));
334
+ grid-template-columns: repeat(2, minmax(0, 1fr));
335
335
  gap: 10px;
336
336
  padding: 14px 22px;
337
337
  border-bottom: 1px solid var(--line);
@@ -358,12 +358,16 @@ li small {
358
358
 
359
359
  .content-meta-section ul,
360
360
  .content-meta-section .tags {
361
- max-height: 220px;
361
+ max-height: 280px;
362
362
  overflow: auto;
363
363
  align-content: flex-start;
364
364
  padding-right: 4px;
365
365
  }
366
366
 
367
+ .content-meta-section:last-child {
368
+ grid-column: span 2;
369
+ }
370
+
367
371
  .content-dialog .note-content {
368
372
  max-height: none;
369
373
  min-height: 0;
@@ -421,10 +425,10 @@ li small {
421
425
  top: auto;
422
426
  right: 12px;
423
427
  left: 12px;
424
- bottom: 38px;
428
+ bottom: 28px;
425
429
  width: auto;
426
- height: min(calc(100svh - 170px), 640px);
427
- max-height: calc(100svh - 170px);
430
+ height: min(calc(100svh - 150px), 760px);
431
+ max-height: calc(100svh - 150px);
428
432
  }
429
433
 
430
434
  .metric-chip {
@@ -61,6 +61,7 @@ const state = {
61
61
  fetchTimer: null,
62
62
  cameraSyncScheduled: false,
63
63
  lastDragFetchAt: 0,
64
+ lastWheelAt: 0,
64
65
  lastVisibleNodes: 0,
65
66
  lastVisibleEdges: 0,
66
67
  totals: {
@@ -303,7 +304,7 @@ const extractContextLinks = (content) => {
303
304
  const lines = content.split(/\\r?\\n/)
304
305
  let start = -1
305
306
  for (let index = 0; index < lines.length; index += 1) {
306
- if (/^##\\s+context\\s+links\\b/i.test(lines[index].trim())) {
307
+ if (/^#{1,6}\\s+(?:context\\s+links?|links?\\s+de\\s+contexto)\\b/i.test(lines[index].trim())) {
307
308
  start = index + 1
308
309
  break
309
310
  }
@@ -313,6 +314,7 @@ const extractContextLinks = (content) => {
313
314
  }
314
315
 
315
316
  const links = []
317
+ const seenTitles = new Set()
316
318
  for (let index = start; index < lines.length; index += 1) {
317
319
  const line = lines[index].trim()
318
320
  if (!line) {
@@ -321,17 +323,21 @@ const extractContextLinks = (content) => {
321
323
  if (/^#{1,6}\\s+/.test(line)) {
322
324
  break
323
325
  }
324
- const match = line.match(/\\[\\[([^\\]]+)\\]\\]/)
325
- if (!match) {
326
- continue
327
- }
328
- const title = match[1].trim()
329
- if (!title) {
326
+ const matches = Array.from(line.matchAll(/\\[\\[([^\\]]+)\\]\\]/g))
327
+ if (matches.length === 0) {
330
328
  continue
331
329
  }
332
330
  const priorityMatch = line.match(/#(critical|important)\\b|priority:\\s*(high|critical)/i)
333
331
  const priority = priorityMatch ? String(priorityMatch[1] || priorityMatch[2] || 'normal').toLowerCase() : 'normal'
334
- links.push({ title, priority })
332
+
333
+ for (let matchIndex = 0; matchIndex < matches.length; matchIndex += 1) {
334
+ const title = String(matches[matchIndex][1] || '').trim()
335
+ if (!title || seenTitles.has(title.toLowerCase())) {
336
+ continue
337
+ }
338
+ seenTitles.add(title.toLowerCase())
339
+ links.push({ title, priority })
340
+ }
335
341
  }
336
342
  return links
337
343
  }
@@ -539,7 +545,9 @@ const scheduleChunkFetch = ({ fit } = { fit: false }) => {
539
545
  clearTimeout(state.fetchTimer)
540
546
  }
541
547
 
542
- const delay = fit ? 0 : (state.pointer.down ? 80 : 32)
548
+ const now = performance.now()
549
+ const recentlyWheeling = now - state.lastWheelAt < 180
550
+ const delay = fit ? 0 : (state.pointer.down ? 120 : (recentlyWheeling ? 140 : 48))
543
551
  state.fetchTimer = setTimeout(() => {
544
552
  state.fetchTimer = null
545
553
  fetchChunk({ fit }).catch((error) => {
@@ -557,7 +565,43 @@ const setViewportFromCanvas = () => {
557
565
  drawFallback()
558
566
  }
559
567
 
568
+ const pickFallbackNodeId = (screenX, screenY) => {
569
+ const nodes = normalizeList(state.chunk.nodes)
570
+ if (nodes.length === 0) {
571
+ return ''
572
+ }
573
+
574
+ let bestId = ''
575
+ let bestDistance = Infinity
576
+ for (let index = 0; index < nodes.length; index += 1) {
577
+ const node = nodes[index]
578
+ const id = typeof node[0] === 'string' ? node[0] : ''
579
+ if (!id) continue
580
+ const x = Number(node[2])
581
+ const y = Number(node[3])
582
+ const weight = Number(node[7])
583
+ if (!Number.isFinite(x) || !Number.isFinite(y)) continue
584
+ const point = worldToScreen(x, y)
585
+ const radius = Math.max(2.4, Math.min(14, 4 + (Number.isFinite(weight) ? weight : 0) * 0.55))
586
+ const distance = Math.hypot(screenX - point.x, screenY - point.y)
587
+ if (distance <= radius && distance < bestDistance) {
588
+ bestDistance = distance
589
+ bestId = id
590
+ }
591
+ }
592
+
593
+ return bestId
594
+ }
595
+
560
596
  const pickAt = (screenX, screenY) => {
597
+ if (state.rendererMode === 'fallback') {
598
+ const nodeId = pickFallbackNodeId(screenX, screenY)
599
+ if (nodeId) {
600
+ loadNodeDetails(nodeId).catch((error) => console.error(error))
601
+ }
602
+ return
603
+ }
604
+
561
605
  if (!state.renderWorker || !state.workerReady) {
562
606
  return
563
607
  }
@@ -594,6 +638,7 @@ const setupInput = () => {
594
638
 
595
639
  canvas.addEventListener('wheel', (event) => {
596
640
  event.preventDefault()
641
+ state.lastWheelAt = performance.now()
597
642
  const pointer = resolvePointer(event)
598
643
  const exponent = Math.max(-0.05, Math.min(0.05, -event.deltaY * 0.001))
599
644
  zoomAtPoint(pointer.x, pointer.y, Math.exp(exponent))
@@ -644,13 +689,6 @@ const setupInput = () => {
644
689
  return
645
690
  }
646
691
 
647
- if (state.renderWorker && state.workerReady) {
648
- state.renderWorker.postMessage({
649
- type: 'pointer',
650
- x: pointer.x,
651
- y: pointer.y
652
- })
653
- }
654
692
  })
655
693
 
656
694
  canvas.addEventListener('pointerup', (event) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.146",
3
+ "version": "0.1.0-beta.148",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",