@andespindola/brainlink 0.1.0-beta.162 → 0.1.0-beta.163

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.
@@ -1,14 +1,14 @@
1
1
  export const createClientCss = () => `:root {
2
- color-scheme: dark;
3
- --bg: #0d0f12;
4
- --panel: #15191f;
5
- --panel-strong: #1c222b;
6
- --line: #29313c;
7
- --text: #edf2f7;
8
- --muted: #99a5b5;
9
- --accent: #35d0a2;
10
- --accent-weak: rgba(53, 208, 162, 0.14);
11
- --danger: #ff6b6b;
2
+ color-scheme: light;
3
+ --bg: #eef2f7;
4
+ --panel: #ffffff;
5
+ --panel-strong: #f7f9fc;
6
+ --line: #d9e1ec;
7
+ --text: #172033;
8
+ --muted: #657386;
9
+ --accent: #0b6fcb;
10
+ --accent-weak: rgba(11, 111, 203, 0.12);
11
+ --danger: #d84949;
12
12
  }
13
13
 
14
14
  * {
@@ -63,7 +63,8 @@ select {
63
63
  min-height: 72px;
64
64
  padding: 10px 16px;
65
65
  border-bottom: 1px solid var(--line);
66
- background: linear-gradient(180deg, rgba(17, 21, 27, 0.96) 0%, rgba(17, 21, 27, 0.86) 100%);
66
+ background: rgba(255, 255, 255, 0.96);
67
+ box-shadow: 0 1px 8px rgba(23, 32, 51, 0.08);
67
68
  backdrop-filter: blur(8px);
68
69
  }
69
70
 
@@ -82,8 +83,10 @@ select {
82
83
  width: 100%;
83
84
  height: 100%;
84
85
  background:
85
- radial-gradient(circle at 18% 20%, rgba(53, 208, 162, 0.12), transparent 28rem),
86
- linear-gradient(135deg, #0d0f12 0%, #12161c 55%, #0a0d10 100%);
86
+ linear-gradient(rgba(23, 32, 51, 0.035) 1px, transparent 1px),
87
+ linear-gradient(90deg, rgba(23, 32, 51, 0.035) 1px, transparent 1px),
88
+ #f6f8fb;
89
+ background-size: 28px 28px, 28px 28px, auto;
87
90
  overflow: hidden;
88
91
  }
89
92
 
@@ -126,23 +129,23 @@ select {
126
129
  .graph-label {
127
130
  position: absolute;
128
131
  max-width: 220px;
129
- transform: translate(-50%, calc(-100% - 10px));
130
- padding: 4px 7px;
131
- border: 1px solid rgba(129, 146, 170, 0.28);
132
+ transform: translate(-50%, calc(-100% - 12px));
133
+ padding: 4px 8px;
134
+ border: 1px solid rgba(101, 115, 134, 0.24);
132
135
  border-radius: 6px;
133
- background: rgba(13, 16, 20, 0.78);
136
+ background: rgba(255, 255, 255, 0.92);
134
137
  color: var(--text);
135
138
  font-size: 11px;
136
139
  line-height: 1.25;
137
140
  white-space: nowrap;
138
141
  overflow: hidden;
139
142
  text-overflow: ellipsis;
140
- box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28);
143
+ box-shadow: 0 8px 22px rgba(23, 32, 51, 0.12);
141
144
  }
142
145
 
143
146
  .graph-label.is-focused {
144
- border-color: rgba(53, 208, 162, 0.72);
145
- color: #dffbf3;
147
+ border-color: rgba(11, 111, 203, 0.56);
148
+ color: #0b4f92;
146
149
  }
147
150
 
148
151
  .graph-tooltip {
@@ -152,12 +155,12 @@ select {
152
155
  padding: 8px 10px;
153
156
  border: 1px solid var(--line);
154
157
  border-radius: 6px;
155
- background: rgba(13, 16, 20, 0.94);
158
+ background: rgba(255, 255, 255, 0.96);
156
159
  color: var(--text);
157
160
  font-size: 12px;
158
161
  line-height: 1.35;
159
162
  pointer-events: none;
160
- box-shadow: 0 16px 40px rgba(0, 0, 0, 0.38);
163
+ box-shadow: 0 16px 40px rgba(23, 32, 51, 0.18);
161
164
  }
162
165
 
163
166
  .graph-tooltip strong,
@@ -181,8 +184,8 @@ select {
181
184
  height: 120px;
182
185
  border: 1px solid rgba(129, 146, 170, 0.28);
183
186
  border-radius: 8px;
184
- background: rgba(13, 16, 20, 0.78);
185
- box-shadow: 0 16px 42px rgba(0, 0, 0, 0.38);
187
+ background: rgba(255, 255, 255, 0.88);
188
+ box-shadow: 0 16px 42px rgba(23, 32, 51, 0.16);
186
189
  }
187
190
 
188
191
  .eyebrow {
@@ -215,7 +218,7 @@ select {
215
218
  border: 1px solid var(--line);
216
219
  border-radius: 8px;
217
220
  outline: none;
218
- background: rgba(21, 25, 31, 0.88);
221
+ background: rgba(255, 255, 255, 0.94);
219
222
  color: var(--text);
220
223
  padding: 0 14px;
221
224
  }
@@ -236,7 +239,7 @@ select {
236
239
  height: 38px;
237
240
  border: 1px solid var(--line);
238
241
  border-radius: 8px;
239
- background: rgba(21, 25, 31, 0.88);
242
+ background: rgba(255, 255, 255, 0.94);
240
243
  color: var(--text);
241
244
  cursor: pointer;
242
245
  }
@@ -257,7 +260,7 @@ select {
257
260
  padding: 10px 12px;
258
261
  border: 1px solid var(--line);
259
262
  border-radius: 10px;
260
- background: rgba(21, 25, 31, 0.88);
263
+ background: rgba(255, 255, 255, 0.94);
261
264
  display: grid;
262
265
  gap: 3px;
263
266
  }
@@ -332,7 +335,7 @@ li small {
332
335
  padding: 12px;
333
336
  border: 1px solid var(--line);
334
337
  border-radius: 8px;
335
- background: #101419;
338
+ background: #f8fafc;
336
339
  color: var(--text);
337
340
  white-space: pre-wrap;
338
341
  overflow: auto;
@@ -362,7 +365,7 @@ li small {
362
365
  border-radius: 8px;
363
366
  background: var(--panel);
364
367
  color: var(--text);
365
- box-shadow: 0 24px 80px rgba(0, 0, 0, 0.48);
368
+ box-shadow: 0 24px 80px rgba(23, 32, 51, 0.22);
366
369
  overflow: hidden;
367
370
  }
368
371
 
@@ -466,7 +469,7 @@ li small {
466
469
  padding: 10px;
467
470
  border: 1px solid var(--line);
468
471
  border-radius: 8px;
469
- background: var(--panel-strong);
472
+ background: #f8fafc;
470
473
  display: grid;
471
474
  grid-template-rows: auto minmax(0, 1fr);
472
475
  gap: 8px;
@@ -369,14 +369,39 @@ const parseColor = (hex) => {
369
369
  }
370
370
 
371
371
  const graphTheme = {
372
- node: parseColor('#aeb8c5'),
373
- nodeCluster: parseColor('#6bb7e8'),
374
- nodeHighlight: parseColor('#f5c24a'),
375
- nodeSelected: parseColor('#ffffff'),
376
- edge: [0.58, 0.64, 0.74, 0.24],
377
- edgeHeavy: [0.78, 0.84, 0.92, 0.44],
378
- clear: parseColor('#0d0f12')
379
- }
372
+ node: parseColor('#4c8eda'),
373
+ nodeCluster: parseColor('#2f6fb4'),
374
+ nodeHighlight: parseColor('#f2b441'),
375
+ nodeSelected: parseColor('#172033'),
376
+ nodePalette: [
377
+ parseColor('#4c8eda'),
378
+ parseColor('#65b96e'),
379
+ parseColor('#f0a33a'),
380
+ parseColor('#d95f8d'),
381
+ parseColor('#8d72d9'),
382
+ parseColor('#55bfc4'),
383
+ parseColor('#ec6b56'),
384
+ parseColor('#9aa6b2'),
385
+ parseColor('#b78255'),
386
+ parseColor('#6f9fd8')
387
+ ],
388
+ edge: [0.23, 0.31, 0.42, 0.18],
389
+ edgeHeavy: [0.23, 0.31, 0.42, 0.34],
390
+ clear: parseColor('#f6f8fb')
391
+ }
392
+
393
+ const segmentPalette = ['#4c8eda', '#65b96e', '#f0a33a', '#d95f8d', '#8d72d9', '#55bfc4', '#ec6b56', '#9aa6b2', '#b78255', '#6f9fd8']
394
+
395
+ const segmentColorIndex = (segment) => {
396
+ const value = String(segment || '')
397
+ let hash = 0
398
+ for (let index = 0; index < value.length; index += 1) {
399
+ hash = ((hash << 5) - hash + value.charCodeAt(index)) | 0
400
+ }
401
+ return Math.abs(hash) % segmentPalette.length
402
+ }
403
+
404
+ const segmentColor = (segment) => segmentPalette[segmentColorIndex(segment)] || segmentPalette[0]
380
405
 
381
406
  const clampScale = (scale) => Math.max(zoomRange.min, Math.min(zoomRange.max, scale))
382
407
 
@@ -518,7 +543,7 @@ const drawFallback = () => {
518
543
  canvas.width = Math.floor(width * ratio)
519
544
  canvas.height = Math.floor(height * ratio)
520
545
  ctx2dFallback.setTransform(ratio, 0, 0, ratio, 0, 0)
521
- ctx2dFallback.fillStyle = '#0d0f12'
546
+ ctx2dFallback.fillStyle = '#f6f8fb'
522
547
  ctx2dFallback.fillRect(0, 0, width, height)
523
548
 
524
549
  const nodes = Array.isArray(state.chunk.nodes) ? state.chunk.nodes : []
@@ -528,7 +553,7 @@ const drawFallback = () => {
528
553
  nodeById.set(nodes[i][0], nodes[i])
529
554
  }
530
555
 
531
- ctx2dFallback.strokeStyle = 'rgba(150,165,190,0.2)'
556
+ ctx2dFallback.strokeStyle = 'rgba(59,79,108,0.18)'
532
557
  ctx2dFallback.lineWidth = 1
533
558
  for (let i = 0; i < edges.length; i += 1) {
534
559
  const edge = edges[i]
@@ -547,7 +572,7 @@ const drawFallback = () => {
547
572
  const node = nodes[i]
548
573
  const p = worldToScreen(node[2], node[3])
549
574
  const selected = state.selectedNodeId === node[0]
550
- const color = node[6] === 'cluster' ? '#6bb7e8' : '#aeb8c5'
575
+ const color = segmentColor(node[5] || node[4] || node[1])
551
576
  const radius = Math.max(2.4, Math.min(14, 4 + node[7] * 0.55))
552
577
 
553
578
  ctx2dFallback.beginPath()
@@ -556,7 +581,7 @@ const drawFallback = () => {
556
581
  ctx2dFallback.fill()
557
582
  }
558
583
 
559
- ctx2dFallback.fillStyle = '#edf2f7'
584
+ ctx2dFallback.fillStyle = '#172033'
560
585
  ctx2dFallback.font = '12px Inter, system-ui, sans-serif'
561
586
  ctx2dFallback.textAlign = 'center'
562
587
  ctx2dFallback.fillText('Fallback canvas mode', Math.max(width, 320) / 2, 24)
@@ -718,7 +743,7 @@ const drawMiniMap = () => {
718
743
  miniMap.height = Math.floor(height * ratio)
719
744
  ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
720
745
  ctx.clearRect(0, 0, width, height)
721
- ctx.fillStyle = 'rgba(13, 16, 20, 0.86)'
746
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.88)'
722
747
  ctx.fillRect(0, 0, width, height)
723
748
 
724
749
  const xs = nodes.map((node) => Number(node[2])).filter(Number.isFinite)
@@ -738,7 +763,7 @@ const drawMiniMap = () => {
738
763
  })
739
764
  state.miniMapView = { minX, minY, scale, offsetX, offsetY, width, height }
740
765
 
741
- ctx.fillStyle = 'rgba(174, 184, 197, 0.62)'
766
+ ctx.fillStyle = 'rgba(76, 142, 218, 0.62)'
742
767
  nodes.forEach((node) => {
743
768
  const point = toMini(Number(node[2]), Number(node[3]))
744
769
  ctx.fillRect(point.x - 1, point.y - 1, 2, 2)
@@ -748,7 +773,7 @@ const drawMiniMap = () => {
748
773
  const worldBottomRight = screenToWorld(state.viewport.width, state.viewport.height)
749
774
  const topLeft = toMini(Math.min(worldTopLeft.x, worldBottomRight.x), Math.min(worldTopLeft.y, worldBottomRight.y))
750
775
  const bottomRight = toMini(Math.max(worldTopLeft.x, worldBottomRight.x), Math.max(worldTopLeft.y, worldBottomRight.y))
751
- ctx.strokeStyle = 'rgba(53, 208, 162, 0.86)'
776
+ ctx.strokeStyle = 'rgba(11, 111, 203, 0.86)'
752
777
  ctx.lineWidth = 1
753
778
  ctx.strokeRect(topLeft.x, topLeft.y, Math.max(3, bottomRight.x - topLeft.x), Math.max(3, bottomRight.y - topLeft.y))
754
779
  }
@@ -14,6 +14,7 @@ const state = {
14
14
  y: new Float32Array(0),
15
15
  relevance: new Float32Array(0),
16
16
  radius: new Float32Array(0),
17
+ colorIndex: new Uint8Array(0),
17
18
  visible: new Uint8Array(0),
18
19
  highlighted: new Uint8Array(0),
19
20
  focused: new Uint8Array(0),
@@ -39,13 +40,25 @@ let pointPositionsBuffer = new Float32Array(0)
39
40
  let pointSizesBuffer = new Float32Array(0)
40
41
 
41
42
  const defaultTheme = {
42
- node: [0.68, 0.72, 0.78, 1],
43
- nodeCluster: [0.42, 0.76, 0.92, 1],
44
- nodeHighlight: [0.95, 0.76, 0.22, 1],
45
- nodeSelected: [0.99, 0.99, 1, 1],
46
- edge: [0.58, 0.64, 0.74, 0.24],
47
- edgeHeavy: [0.78, 0.84, 0.92, 0.44],
48
- clear: [0.05, 0.06, 0.08, 1]
43
+ node: [0.30, 0.56, 0.85, 1],
44
+ nodeCluster: [0.18, 0.44, 0.71, 1],
45
+ nodeHighlight: [0.95, 0.70, 0.25, 1],
46
+ nodeSelected: [0.09, 0.13, 0.20, 1],
47
+ nodePalette: [
48
+ [0.30, 0.56, 0.85, 1],
49
+ [0.40, 0.73, 0.43, 1],
50
+ [0.94, 0.64, 0.23, 1],
51
+ [0.85, 0.37, 0.55, 1],
52
+ [0.55, 0.45, 0.85, 1],
53
+ [0.33, 0.75, 0.77, 1],
54
+ [0.93, 0.42, 0.34, 1],
55
+ [0.60, 0.65, 0.70, 1],
56
+ [0.72, 0.51, 0.33, 1],
57
+ [0.44, 0.62, 0.85, 1]
58
+ ],
59
+ edge: [0.23, 0.31, 0.42, 0.18],
60
+ edgeHeavy: [0.23, 0.31, 0.42, 0.34],
61
+ clear: [0.96, 0.97, 0.98, 1]
49
62
  }
50
63
 
51
64
  const theme = { ...defaultTheme }
@@ -181,6 +194,7 @@ const ensureNodeCapacity = (count) => {
181
194
  state.y = new Float32Array(nextCapacity)
182
195
  state.relevance = new Float32Array(nextCapacity)
183
196
  state.radius = new Float32Array(nextCapacity)
197
+ state.colorIndex = new Uint8Array(nextCapacity)
184
198
  state.visible = new Uint8Array(nextCapacity)
185
199
  state.highlighted = new Uint8Array(nextCapacity)
186
200
  state.focused = new Uint8Array(nextCapacity)
@@ -199,11 +213,21 @@ const ensureEdgeCapacity = (count) => {
199
213
  }
200
214
 
201
215
  const nodeRadius = (relevance, kind) => {
202
- const base = kind === 'cluster' ? 7.8 : 4.6
203
- const modifier = Math.min(4.8, Math.max(0, relevance * 0.55))
216
+ const base = kind === 'cluster' ? 8.8 : 5.4
217
+ const modifier = Math.min(5.6, Math.max(0, relevance * 0.62))
204
218
  return base + modifier
205
219
  }
206
220
 
221
+ const segmentColorIndex = (segment) => {
222
+ const value = String(segment || '')
223
+ let hash = 0
224
+ for (let index = 0; index < value.length; index += 1) {
225
+ hash = ((hash << 5) - hash + value.charCodeAt(index)) | 0
226
+ }
227
+ const palette = Array.isArray(theme.nodePalette) && theme.nodePalette.length > 0 ? theme.nodePalette : [theme.node]
228
+ return Math.abs(hash) % palette.length
229
+ }
230
+
207
231
  const loadChunk = (chunk) => {
208
232
  const nodes = Array.isArray(chunk?.nodes) ? chunk.nodes : []
209
233
  const edges = Array.isArray(chunk?.edges) ? chunk.edges : []
@@ -223,6 +247,7 @@ const loadChunk = (chunk) => {
223
247
  const title = typeof row?.[1] === 'string' ? row[1] : id
224
248
  const x = Number.isFinite(row?.[2]) ? Number(row[2]) : 0
225
249
  const y = Number.isFinite(row?.[3]) ? Number(row[3]) : 0
250
+ const segment = typeof row?.[5] === 'string' ? row[5] : ''
226
251
  const kind = row?.[6] === 'cluster' ? 'cluster' : 'node'
227
252
  const relevance = Number.isFinite(row?.[7]) ? Number(row[7]) : 0
228
253
 
@@ -233,6 +258,7 @@ const loadChunk = (chunk) => {
233
258
  state.y[index] = y
234
259
  state.relevance[index] = relevance
235
260
  state.radius[index] = nodeRadius(relevance, kind)
261
+ state.colorIndex[index] = segmentColorIndex(segment || title)
236
262
  state.visible[index] = 0
237
263
  state.highlighted[index] = highlightedIds.has(id) ? 1 : 0
238
264
  state.focused[index] = focusedIds.has(id) ? 1 : 0
@@ -349,6 +375,13 @@ const drawNodeLayer = (predicate, color, radiusBoost = 1) => {
349
375
  gl.drawArrays(gl.POINTS, 0, positionCursor / 2)
350
376
  }
351
377
 
378
+ const drawColoredNodeLayer = (predicate, radiusBoost = 1) => {
379
+ const palette = Array.isArray(theme.nodePalette) && theme.nodePalette.length > 0 ? theme.nodePalette : [theme.node]
380
+ for (let colorIndex = 0; colorIndex < palette.length; colorIndex += 1) {
381
+ drawNodeLayer((index) => predicate(index) && state.colorIndex[index] === colorIndex, palette[colorIndex], radiusBoost)
382
+ }
383
+ }
384
+
352
385
  const clear = () => {
353
386
  if (!gl || !canvas) return
354
387
  gl.viewport(0, 0, canvas.width, canvas.height)
@@ -398,15 +431,13 @@ const renderFrame = (now) => {
398
431
  scheduleSettledRender(now)
399
432
  }
400
433
 
401
- drawNodeLayer(
402
- (index) => state.visible[index] === 1 && state.selected[index] === 0 && state.highlighted[index] === 0,
403
- theme.node,
434
+ drawColoredNodeLayer(
435
+ (index) => state.visible[index] === 1 && state.kinds[index] !== 'cluster' && state.selected[index] === 0 && state.highlighted[index] === 0 && state.focused[index] === 0,
404
436
  1
405
437
  )
406
438
 
407
- drawNodeLayer(
439
+ drawColoredNodeLayer(
408
440
  (index) => state.visible[index] === 1 && state.kinds[index] === 'cluster' && state.selected[index] === 0,
409
- theme.nodeCluster,
410
441
  1.15
411
442
  )
412
443
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.162",
3
+ "version": "0.1.0-beta.163",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",