@andespindola/brainlink 0.1.0-beta.98 → 1.0.0

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.
Files changed (46) hide show
  1. package/AGENTS.md +6 -6
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +186 -38
  4. package/dist/application/add-note.js +13 -44
  5. package/dist/application/analyze-vault.js +1 -1
  6. package/dist/application/auto-migrate-configured-vault.js +37 -0
  7. package/dist/application/build-context.js +119 -20
  8. package/dist/application/canonical-context-links.js +209 -0
  9. package/dist/application/frontend/client-css.js +212 -42
  10. package/dist/application/frontend/client-html.js +42 -28
  11. package/dist/application/frontend/client-js.js +1294 -3217
  12. package/dist/application/frontend/client-render-worker-js.js +676 -0
  13. package/dist/application/get-graph-contexts.js +33 -0
  14. package/dist/application/get-graph-layout.js +62 -8
  15. package/dist/application/get-graph-stream-chunk.js +326 -0
  16. package/dist/application/get-graph-view.js +246 -0
  17. package/dist/application/graph-view-state.js +66 -0
  18. package/dist/application/import-legacy-sqlite.js +3 -33
  19. package/dist/application/index-vault.js +35 -22
  20. package/dist/application/migrate-context-links.js +79 -0
  21. package/dist/application/search-graph-node-ids.js +63 -3
  22. package/dist/application/server/routes.js +197 -12
  23. package/dist/cli/commands/read-commands.js +39 -3
  24. package/dist/cli/commands/vault-commands.js +182 -0
  25. package/dist/cli/commands/write-commands.js +147 -12
  26. package/dist/cli/main.js +2 -0
  27. package/dist/cli/runtime.js +10 -2
  28. package/dist/domain/context.js +1 -0
  29. package/dist/domain/graph-contexts.js +180 -0
  30. package/dist/domain/graph-layout.js +347 -21
  31. package/dist/domain/markdown.js +53 -9
  32. package/dist/infrastructure/config.js +105 -6
  33. package/dist/infrastructure/context-packs.js +122 -0
  34. package/dist/infrastructure/file-index.js +6 -3
  35. package/dist/infrastructure/index-state.js +2 -0
  36. package/dist/infrastructure/vault-migration-state.js +69 -0
  37. package/dist/infrastructure/volatile-memory.js +100 -0
  38. package/dist/mcp/http-server.js +97 -0
  39. package/dist/mcp/runtime.js +20 -0
  40. package/dist/mcp/server.js +36 -13
  41. package/dist/mcp/tools.js +203 -14
  42. package/docs/AGENT_USAGE.md +50 -5
  43. package/docs/ARCHITECTURE.md +11 -0
  44. package/docs/QUICKSTART.md +3 -1
  45. package/docs/RELEASE.md +4 -3
  46. package/package.json +3 -1
@@ -1,13 +1,13 @@
1
1
  export const createClientCss = () => `:root {
2
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);
3
+ --bg: #071019;
4
+ --panel: #0d1823;
5
+ --panel-strong: #112130;
6
+ --line: rgba(143, 172, 204, 0.18);
7
+ --text: #edf4ff;
8
+ --muted: #97a9bd;
9
+ --accent: #5aa8ff;
10
+ --accent-weak: rgba(90, 168, 255, 0.18);
11
11
  --danger: #ff6b6b;
12
12
  }
13
13
 
@@ -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(9, 18, 28, 0.92);
67
+ box-shadow: 0 1px 14px rgba(1, 6, 13, 0.42);
67
68
  backdrop-filter: blur(8px);
68
69
  }
69
70
 
@@ -81,33 +82,117 @@ select {
81
82
  position: relative;
82
83
  width: 100%;
83
84
  height: 100%;
85
+ touch-action: none;
86
+ user-select: none;
87
+ -webkit-user-select: none;
84
88
  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%);
89
+ linear-gradient(rgba(164, 197, 230, 0.05) 1px, transparent 1px),
90
+ linear-gradient(90deg, rgba(164, 197, 230, 0.05) 1px, transparent 1px),
91
+ radial-gradient(circle at top, rgba(43, 93, 143, 0.22), transparent 44%),
92
+ #08131d;
93
+ background-size: 28px 28px, 28px 28px, auto;
87
94
  overflow: hidden;
88
95
  }
89
96
 
90
- #graph,
91
- #graphGl {
97
+ #graph {
92
98
  display: block;
93
99
  position: absolute;
94
100
  inset: 0;
95
101
  width: 100%;
96
102
  height: 100%;
103
+ touch-action: none;
97
104
  }
98
105
 
99
106
  #graph {
100
107
  cursor: grab;
101
108
  }
102
109
 
103
- #graphGl {
104
- pointer-events: none;
110
+ #graph.is-node-hover {
111
+ cursor: pointer;
112
+ }
113
+
114
+ #graph.is-node-dragging {
115
+ cursor: move;
105
116
  }
106
117
 
107
118
  #graph:active {
108
119
  cursor: grabbing;
109
120
  }
110
121
 
122
+ .graph-labels {
123
+ position: absolute;
124
+ inset: 0;
125
+ pointer-events: none;
126
+ overflow: hidden;
127
+ transition: opacity 120ms ease;
128
+ }
129
+
130
+ .graph-labels.is-stale {
131
+ opacity: 0;
132
+ }
133
+
134
+ .graph-label {
135
+ position: absolute;
136
+ max-width: 220px;
137
+ transform: translate(-50%, calc(-100% - 12px));
138
+ padding: 4px 8px;
139
+ border: 1px solid rgba(143, 172, 204, 0.18);
140
+ border-radius: 6px;
141
+ background: rgba(7, 16, 25, 0.92);
142
+ color: var(--text);
143
+ font-size: 11px;
144
+ line-height: 1.25;
145
+ white-space: nowrap;
146
+ overflow: hidden;
147
+ text-overflow: ellipsis;
148
+ box-shadow: 0 12px 28px rgba(1, 6, 13, 0.36);
149
+ }
150
+
151
+ .graph-label.is-focused {
152
+ border-color: rgba(90, 168, 255, 0.56);
153
+ color: #d7e9ff;
154
+ }
155
+
156
+ .graph-tooltip {
157
+ position: absolute;
158
+ z-index: 4;
159
+ max-width: min(320px, calc(100vw - 32px));
160
+ padding: 8px 10px;
161
+ border: 1px solid var(--line);
162
+ border-radius: 6px;
163
+ background: rgba(9, 18, 28, 0.96);
164
+ color: var(--text);
165
+ font-size: 12px;
166
+ line-height: 1.35;
167
+ pointer-events: none;
168
+ box-shadow: 0 18px 44px rgba(1, 6, 13, 0.42);
169
+ }
170
+
171
+ .graph-tooltip strong,
172
+ .graph-tooltip small {
173
+ display: block;
174
+ overflow: hidden;
175
+ text-overflow: ellipsis;
176
+ }
177
+
178
+ .graph-tooltip small {
179
+ margin-top: 3px;
180
+ color: var(--muted);
181
+ }
182
+
183
+ .mini-map {
184
+ position: absolute;
185
+ right: 14px;
186
+ bottom: 14px;
187
+ z-index: 3;
188
+ width: 180px;
189
+ height: 120px;
190
+ border: 1px solid rgba(143, 172, 204, 0.18);
191
+ border-radius: 8px;
192
+ background: rgba(8, 19, 29, 0.84);
193
+ box-shadow: 0 18px 40px rgba(1, 6, 13, 0.36);
194
+ }
195
+
111
196
  .eyebrow {
112
197
  color: var(--muted);
113
198
  font-size: 12px;
@@ -118,7 +203,8 @@ select {
118
203
  min-width: 220px;
119
204
  }
120
205
 
121
- .agent-filter {
206
+ .agent-filter,
207
+ .context-filter {
122
208
  width: min(220px, 28vw);
123
209
  }
124
210
 
@@ -130,19 +216,21 @@ select {
130
216
  }
131
217
 
132
218
  .search input,
133
- .agent-filter select {
219
+ .agent-filter select,
220
+ .context-filter select {
134
221
  width: 100%;
135
222
  height: 40px;
136
223
  border: 1px solid var(--line);
137
224
  border-radius: 8px;
138
225
  outline: none;
139
- background: rgba(21, 25, 31, 0.88);
226
+ background: rgba(12, 24, 36, 0.94);
140
227
  color: var(--text);
141
228
  padding: 0 14px;
142
229
  }
143
230
 
144
231
  .search input:focus,
145
- .agent-filter select:focus {
232
+ .agent-filter select:focus,
233
+ .context-filter select:focus {
146
234
  border-color: var(--accent);
147
235
  }
148
236
 
@@ -156,7 +244,7 @@ select {
156
244
  height: 38px;
157
245
  border: 1px solid var(--line);
158
246
  border-radius: 8px;
159
- background: rgba(21, 25, 31, 0.88);
247
+ background: rgba(12, 24, 36, 0.94);
160
248
  color: var(--text);
161
249
  cursor: pointer;
162
250
  }
@@ -177,7 +265,7 @@ select {
177
265
  padding: 10px 12px;
178
266
  border: 1px solid var(--line);
179
267
  border-radius: 10px;
180
- background: rgba(21, 25, 31, 0.88);
268
+ background: rgba(12, 24, 36, 0.94);
181
269
  display: grid;
182
270
  gap: 3px;
183
271
  }
@@ -252,7 +340,7 @@ li small {
252
340
  padding: 12px;
253
341
  border: 1px solid var(--line);
254
342
  border-radius: 8px;
255
- background: #101419;
343
+ background: #091521;
256
344
  color: var(--text);
257
345
  white-space: pre-wrap;
258
346
  overflow: auto;
@@ -262,34 +350,58 @@ li small {
262
350
  }
263
351
 
264
352
  .content-dialog {
265
- width: min(1240px, calc(100vw - 24px));
266
- max-height: calc(100svh - 20px);
353
+ position: absolute;
354
+ z-index: 6;
355
+ top: 12px;
356
+ right: 12px;
357
+ bottom: 12px;
358
+ left: 12px;
359
+ top: max(12px, env(safe-area-inset-top));
360
+ right: max(12px, env(safe-area-inset-right));
361
+ bottom: max(12px, env(safe-area-inset-bottom));
362
+ left: max(12px, calc(100% - 780px));
363
+ margin: 0;
364
+ width: auto;
365
+ height: auto;
366
+ max-width: none;
367
+ max-height: none;
267
368
  padding: 0;
268
369
  border: 1px solid var(--line);
269
370
  border-radius: 8px;
270
371
  background: var(--panel);
271
372
  color: var(--text);
272
- box-shadow: 0 24px 80px rgba(0, 0, 0, 0.48);
373
+ box-shadow: 0 24px 80px rgba(23, 32, 51, 0.22);
374
+ backdrop-filter: blur(10px);
375
+ overflow: hidden;
273
376
  }
274
377
 
275
- .content-dialog::backdrop {
276
- background: rgba(4, 7, 10, 0.72);
277
- backdrop-filter: blur(4px);
378
+ .content-dialog[hidden] {
379
+ display: none;
278
380
  }
279
381
 
280
382
  .content-dialog article {
281
383
  display: grid;
282
384
  grid-template-rows: auto auto minmax(0, 1fr);
283
- max-height: calc(100svh - 22px);
385
+ height: 100%;
386
+ max-height: 100%;
387
+ min-height: 0;
284
388
  }
285
389
 
286
390
  .content-dialog header {
287
391
  display: flex;
392
+ position: sticky;
393
+ top: 0;
394
+ z-index: 1;
288
395
  align-items: flex-start;
289
396
  justify-content: space-between;
290
397
  gap: 18px;
291
398
  padding: 22px;
292
399
  border-bottom: 1px solid var(--line);
400
+ background: var(--panel);
401
+ }
402
+
403
+ .content-dialog header > div {
404
+ min-width: 0;
293
405
  }
294
406
 
295
407
  .content-dialog h2,
@@ -299,7 +411,7 @@ li small {
299
411
 
300
412
  .content-dialog h2 {
301
413
  margin-top: 6px;
302
- font-size: 24px;
414
+ font-size: 19px;
303
415
  line-height: 1.15;
304
416
  overflow-wrap: anywhere;
305
417
  }
@@ -310,27 +422,50 @@ li small {
310
422
  overflow-wrap: anywhere;
311
423
  }
312
424
 
313
- .content-dialog button {
425
+ #contentClose {
314
426
  flex: 0 0 auto;
427
+ width: 38px;
315
428
  height: 38px;
316
- padding: 0 14px;
429
+ padding: 0;
317
430
  border: 1px solid var(--line);
318
431
  border-radius: 8px;
319
432
  background: var(--panel-strong);
320
433
  color: var(--text);
321
434
  cursor: pointer;
435
+ font-size: 22px;
436
+ line-height: 1;
322
437
  }
323
438
 
324
- .content-dialog button:hover,
325
- .content-dialog button:focus {
439
+ #contentClose:hover,
440
+ #contentClose:focus {
326
441
  border-color: var(--accent);
327
442
  color: var(--accent);
328
443
  }
329
444
 
445
+ .content-dialog li button {
446
+ width: 100%;
447
+ height: auto;
448
+ padding: 0;
449
+ border: 0;
450
+ border-radius: 0;
451
+ background: transparent;
452
+ color: var(--text);
453
+ text-align: left;
454
+ }
455
+
456
+ .content-dialog li button:hover,
457
+ .content-dialog li button:focus {
458
+ color: var(--accent);
459
+ }
460
+
330
461
  .content-meta {
331
462
  display: grid;
332
- grid-template-columns: repeat(3, minmax(0, 1fr));
463
+ grid-template-columns: repeat(2, minmax(0, 1fr));
333
464
  gap: 10px;
465
+ min-height: 0;
466
+ max-height: min(42vh, 360px);
467
+ max-height: min(42dvh, 360px);
468
+ overflow: auto;
334
469
  padding: 14px 22px;
335
470
  border-bottom: 1px solid var(--line);
336
471
  }
@@ -340,7 +475,7 @@ li small {
340
475
  padding: 10px;
341
476
  border: 1px solid var(--line);
342
477
  border-radius: 8px;
343
- background: var(--panel-strong);
478
+ background: #091521;
344
479
  display: grid;
345
480
  grid-template-rows: auto minmax(0, 1fr);
346
481
  gap: 8px;
@@ -356,18 +491,22 @@ li small {
356
491
 
357
492
  .content-meta-section ul,
358
493
  .content-meta-section .tags {
359
- max-height: 220px;
494
+ max-height: 280px;
360
495
  overflow: auto;
361
496
  align-content: flex-start;
362
497
  padding-right: 4px;
363
498
  }
364
499
 
500
+ .content-meta-section:last-child {
501
+ grid-column: span 2;
502
+ }
503
+
365
504
  .content-dialog .note-content {
366
505
  max-height: none;
367
506
  min-height: 0;
368
507
  border: 0;
369
508
  border-radius: 0;
370
- padding: 22px;
509
+ padding: 14px;
371
510
  }
372
511
 
373
512
  .app-footer {
@@ -376,7 +515,7 @@ li small {
376
515
  display: flex;
377
516
  align-items: center;
378
517
  justify-content: center;
379
- background: transparent;
518
+ background: linear-gradient(180deg, rgba(7, 16, 25, 0), rgba(7, 16, 25, 0.84));
380
519
  }
381
520
 
382
521
  .app-footer small {
@@ -399,7 +538,8 @@ li small {
399
538
  order: 3;
400
539
  }
401
540
 
402
- .agent-filter {
541
+ .agent-filter,
542
+ .context-filter {
403
543
  width: 100%;
404
544
  }
405
545
 
@@ -410,9 +550,27 @@ li small {
410
550
  order: 4;
411
551
  }
412
552
 
553
+ .content-dialog {
554
+ inset: 8px;
555
+ inset: max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) max(8px, env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left));
556
+ width: auto;
557
+ height: auto;
558
+ max-width: none;
559
+ max-height: none;
560
+ }
561
+
413
562
  .content-dialog header {
414
- align-items: stretch;
415
- flex-direction: column;
563
+ align-items: flex-start;
564
+ gap: 12px;
565
+ padding: 14px;
566
+ }
567
+
568
+ .content-dialog h2 {
569
+ font-size: 16px;
570
+ }
571
+
572
+ .content-dialog p {
573
+ font-size: 12px;
416
574
  }
417
575
 
418
576
  .metric-chip {
@@ -421,5 +579,17 @@ li small {
421
579
 
422
580
  .content-meta {
423
581
  grid-template-columns: 1fr;
582
+ max-height: 34vh;
583
+ max-height: 34dvh;
584
+ overflow: auto;
585
+ padding: 10px 14px;
586
+ }
587
+
588
+ .content-meta-section:last-child {
589
+ grid-column: auto;
590
+ }
591
+
592
+ .content-dialog .note-content {
593
+ padding: 12px;
424
594
  }
425
595
  }`;
@@ -35,50 +35,64 @@ export const createClientHtml = () => `<!doctype html>
35
35
  <label class="agent-filter">
36
36
  <select id="agent"></select>
37
37
  </label>
38
+ <label class="context-filter">
39
+ <select id="context"></select>
40
+ </label>
38
41
  <div class="toolbar" aria-label="Graph controls">
39
42
  <button id="zoomIn" type="button" title="Zoom in">+</button>
40
43
  <button id="zoomOut" type="button" title="Zoom out">-</button>
41
44
  <button id="fit" type="button" title="Focus central hub">◎</button>
45
+ <button id="releaseNode" type="button" title="Release selected node">◇</button>
42
46
  <button id="reset" type="button" title="Reset view">⌂</button>
43
47
  </div>
44
48
  </div>
45
49
  </header>
46
50
  <div class="graph-stage">
47
- <canvas id="graphGl" aria-hidden="true"></canvas>
48
51
  <canvas id="graph" aria-label="Brainlink knowledge graph"></canvas>
52
+ <div id="graphLabels" class="graph-labels" aria-hidden="true"></div>
53
+ <div id="graphTooltip" class="graph-tooltip" role="tooltip" hidden></div>
54
+ <canvas id="miniMap" class="mini-map" aria-label="Graph overview"></canvas>
55
+ <aside id="contentDialog" class="content-dialog" role="dialog" aria-labelledby="contentTitle" hidden>
56
+ <article>
57
+ <header>
58
+ <div>
59
+ <span class="eyebrow">Node details</span>
60
+ <h2 id="contentTitle">Selected note</h2>
61
+ <p id="contentPath"></p>
62
+ </div>
63
+ <button id="contentClose" type="button" aria-label="Close node details" title="Close node details">&times;</button>
64
+ </header>
65
+ <div class="content-meta">
66
+ <section class="content-meta-section">
67
+ <h3>Facts</h3>
68
+ <ul id="contentFacts"></ul>
69
+ </section>
70
+ <section class="content-meta-section">
71
+ <h3>Context Links</h3>
72
+ <ul id="contentContextLinks"></ul>
73
+ </section>
74
+ <section class="content-meta-section">
75
+ <h3>Tags</h3>
76
+ <div id="contentTags" class="tags"></div>
77
+ </section>
78
+ <section class="content-meta-section">
79
+ <h3>Outgoing</h3>
80
+ <ul id="contentOutgoing"></ul>
81
+ </section>
82
+ <section class="content-meta-section">
83
+ <h3>Backlinks</h3>
84
+ <ul id="contentIncoming"></ul>
85
+ </section>
86
+ </div>
87
+ <pre id="contentBody" class="note-content"></pre>
88
+ </article>
89
+ </aside>
49
90
  </div>
50
91
  </section>
51
92
  </main>
52
93
  <footer class="app-footer" aria-label="Copyright notice">
53
94
  <small>Copyright © 2026 Substructa</small>
54
95
  </footer>
55
- <dialog id="contentDialog" class="content-dialog" aria-labelledby="contentTitle">
56
- <article>
57
- <header>
58
- <div>
59
- <span class="eyebrow">Markdown content</span>
60
- <h2 id="contentTitle">Selected note</h2>
61
- <p id="contentPath"></p>
62
- </div>
63
- <button id="contentClose" type="button">Close</button>
64
- </header>
65
- <div class="content-meta">
66
- <section class="content-meta-section">
67
- <h3>Tags</h3>
68
- <div id="contentTags" class="tags"></div>
69
- </section>
70
- <section class="content-meta-section">
71
- <h3>Outgoing</h3>
72
- <ul id="contentOutgoing"></ul>
73
- </section>
74
- <section class="content-meta-section">
75
- <h3>Backlinks</h3>
76
- <ul id="contentIncoming"></ul>
77
- </section>
78
- </div>
79
- <pre id="contentBody" class="note-content"></pre>
80
- </article>
81
- </dialog>
82
96
  <script src="/app.js"></script>
83
97
  </body>
84
98
  </html>`;