@andespindola/brainlink 0.1.0-beta.16 → 0.1.0-beta.160

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 (50) hide show
  1. package/AGENTS.md +9 -6
  2. package/CHANGELOG.md +27 -0
  3. package/COPYRIGHT.md +5 -0
  4. package/README.md +177 -20
  5. package/dist/application/add-note.js +13 -44
  6. package/dist/application/auto-migrate-configured-vault.js +37 -0
  7. package/dist/application/build-context.js +64 -3
  8. package/dist/application/canonical-context-links.js +209 -0
  9. package/dist/application/dedupe-notes.js +226 -0
  10. package/dist/application/frontend/client-css.js +241 -51
  11. package/dist/application/frontend/client-html.js +50 -27
  12. package/dist/application/frontend/client-js.js +1369 -605
  13. package/dist/application/frontend/client-render-worker-js.js +622 -0
  14. package/dist/application/frontend/client-worker-js.js +66 -0
  15. package/dist/application/get-graph-contexts.js +33 -0
  16. package/dist/application/get-graph-layout.js +62 -8
  17. package/dist/application/get-graph-stream-chunk.js +326 -0
  18. package/dist/application/get-graph-view.js +246 -0
  19. package/dist/application/graph-view-state.js +66 -0
  20. package/dist/application/import-legacy-sqlite.js +266 -0
  21. package/dist/application/index-vault.js +262 -23
  22. package/dist/application/migrate-context-links.js +79 -0
  23. package/dist/application/offline-pack-backup.js +44 -0
  24. package/dist/application/search-graph-node-ids.js +63 -3
  25. package/dist/application/server/routes.js +247 -7
  26. package/dist/application/start-server.js +75 -4
  27. package/dist/application/watch-vault.js +23 -2
  28. package/dist/cli/commands/agent-commands.js +7 -0
  29. package/dist/cli/commands/write-commands.js +924 -14
  30. package/dist/cli/runtime.js +10 -2
  31. package/dist/domain/context.js +54 -11
  32. package/dist/domain/graph-contexts.js +180 -0
  33. package/dist/domain/graph-layout.js +389 -18
  34. package/dist/domain/markdown.js +53 -9
  35. package/dist/domain/middle-out.js +18 -0
  36. package/dist/infrastructure/config.js +121 -4
  37. package/dist/infrastructure/file-index.js +76 -6
  38. package/dist/infrastructure/file-system-vault.js +15 -0
  39. package/dist/infrastructure/index-state.js +58 -0
  40. package/dist/infrastructure/private-pack-codec.js +71 -10
  41. package/dist/infrastructure/search-packs.js +286 -15
  42. package/dist/infrastructure/vault-migration-state.js +69 -0
  43. package/dist/infrastructure/volatile-memory.js +100 -0
  44. package/dist/mcp/runtime.js +20 -0
  45. package/dist/mcp/server.js +39 -11
  46. package/dist/mcp/tools.js +183 -7
  47. package/docs/AGENT_USAGE.md +96 -5
  48. package/docs/ARCHITECTURE.md +8 -0
  49. package/docs/QUICKSTART.md +7 -0
  50. package/package.json +7 -2
@@ -25,6 +25,13 @@ body {
25
25
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
26
26
  }
27
27
 
28
+ body {
29
+ display: flex;
30
+ flex-direction: column;
31
+ min-height: 100vh;
32
+ min-height: 100dvh;
33
+ }
34
+
28
35
  button,
29
36
  input,
30
37
  select {
@@ -32,12 +39,15 @@ select {
32
39
  }
33
40
 
34
41
  .shell {
42
+ flex: 1 1 auto;
35
43
  width: 100%;
36
- height: 100svh;
44
+ min-height: 0;
37
45
  overflow: hidden;
38
46
  }
39
47
 
40
48
  .workspace {
49
+ display: grid;
50
+ grid-template-rows: auto minmax(0, 1fr);
41
51
  position: relative;
42
52
  width: 100%;
43
53
  height: 100%;
@@ -45,39 +55,134 @@ select {
45
55
  min-height: 0;
46
56
  }
47
57
 
48
- #graph {
49
- display: block;
58
+ .graph-header {
59
+ z-index: 5;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 12px;
63
+ min-height: 72px;
64
+ padding: 10px 16px;
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%);
67
+ backdrop-filter: blur(8px);
68
+ }
69
+
70
+ .brand-block {
71
+ display: grid;
72
+ gap: 2px;
73
+ min-width: max-content;
74
+ }
75
+
76
+ .brand-block strong {
77
+ font-size: 18px;
78
+ }
79
+
80
+ .graph-stage {
81
+ position: relative;
50
82
  width: 100%;
51
83
  height: 100%;
52
84
  background:
53
85
  radial-gradient(circle at 18% 20%, rgba(53, 208, 162, 0.12), transparent 28rem),
54
86
  linear-gradient(135deg, #0d0f12 0%, #12161c 55%, #0a0d10 100%);
87
+ overflow: hidden;
88
+ }
89
+
90
+ #graph {
91
+ display: block;
92
+ position: absolute;
93
+ inset: 0;
94
+ width: 100%;
95
+ height: 100%;
96
+ }
97
+
98
+ #graph {
55
99
  cursor: grab;
56
100
  }
57
101
 
102
+ #graph.is-node-hover {
103
+ cursor: pointer;
104
+ }
105
+
106
+ #graph.is-node-dragging {
107
+ cursor: move;
108
+ }
109
+
58
110
  #graph:active {
59
111
  cursor: grabbing;
60
112
  }
61
113
 
62
- .topbar {
114
+ .graph-labels {
63
115
  position: absolute;
64
- top: 18px;
65
- left: 18px;
66
- right: 18px;
67
- display: flex;
68
- align-items: center;
69
- justify-content: space-between;
70
- gap: 18px;
116
+ inset: 0;
71
117
  pointer-events: none;
118
+ overflow: hidden;
119
+ transition: opacity 120ms ease;
72
120
  }
73
121
 
74
- .topbar > div {
75
- display: flex;
76
- align-items: center;
122
+ .graph-labels.is-stale {
123
+ opacity: 0;
77
124
  }
78
125
 
79
- .topbar strong {
80
- font-size: 18px;
126
+ .graph-label {
127
+ position: absolute;
128
+ 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
+ border-radius: 6px;
133
+ background: rgba(13, 16, 20, 0.78);
134
+ color: var(--text);
135
+ font-size: 11px;
136
+ line-height: 1.25;
137
+ white-space: nowrap;
138
+ overflow: hidden;
139
+ text-overflow: ellipsis;
140
+ box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28);
141
+ }
142
+
143
+ .graph-label.is-focused {
144
+ border-color: rgba(53, 208, 162, 0.72);
145
+ color: #dffbf3;
146
+ }
147
+
148
+ .graph-tooltip {
149
+ position: absolute;
150
+ z-index: 4;
151
+ max-width: min(320px, calc(100vw - 32px));
152
+ padding: 8px 10px;
153
+ border: 1px solid var(--line);
154
+ border-radius: 6px;
155
+ background: rgba(13, 16, 20, 0.94);
156
+ color: var(--text);
157
+ font-size: 12px;
158
+ line-height: 1.35;
159
+ pointer-events: none;
160
+ box-shadow: 0 16px 40px rgba(0, 0, 0, 0.38);
161
+ }
162
+
163
+ .graph-tooltip strong,
164
+ .graph-tooltip small {
165
+ display: block;
166
+ overflow: hidden;
167
+ text-overflow: ellipsis;
168
+ }
169
+
170
+ .graph-tooltip small {
171
+ margin-top: 3px;
172
+ color: var(--muted);
173
+ }
174
+
175
+ .mini-map {
176
+ position: absolute;
177
+ right: 14px;
178
+ bottom: 14px;
179
+ z-index: 3;
180
+ width: 180px;
181
+ height: 120px;
182
+ border: 1px solid rgba(129, 146, 170, 0.28);
183
+ border-radius: 8px;
184
+ background: rgba(13, 16, 20, 0.78);
185
+ box-shadow: 0 16px 42px rgba(0, 0, 0, 0.38);
81
186
  }
82
187
 
83
188
  .eyebrow {
@@ -86,17 +191,25 @@ select {
86
191
  }
87
192
 
88
193
  .search {
89
- width: min(420px, 42vw);
90
- pointer-events: auto;
194
+ flex: 1 1 320px;
195
+ min-width: 220px;
91
196
  }
92
197
 
93
- .agent-filter {
198
+ .agent-filter,
199
+ .context-filter {
94
200
  width: min(220px, 28vw);
95
- pointer-events: auto;
201
+ }
202
+
203
+ .header-actions {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 10px;
207
+ margin-left: auto;
96
208
  }
97
209
 
98
210
  .search input,
99
- .agent-filter select {
211
+ .agent-filter select,
212
+ .context-filter select {
100
213
  width: 100%;
101
214
  height: 40px;
102
215
  border: 1px solid var(--line);
@@ -108,14 +221,12 @@ select {
108
221
  }
109
222
 
110
223
  .search input:focus,
111
- .agent-filter select:focus {
224
+ .agent-filter select:focus,
225
+ .context-filter select:focus {
112
226
  border-color: var(--accent);
113
227
  }
114
228
 
115
229
  .toolbar {
116
- position: absolute;
117
- left: 18px;
118
- bottom: 18px;
119
230
  display: flex;
120
231
  gap: 8px;
121
232
  }
@@ -136,12 +247,9 @@ select {
136
247
  }
137
248
 
138
249
  .floating-metrics {
139
- position: absolute;
140
- top: 66px;
141
- left: 18px;
142
250
  display: flex;
143
251
  gap: 10px;
144
- pointer-events: none;
252
+ flex-wrap: wrap;
145
253
  }
146
254
 
147
255
  .metric-chip {
@@ -234,14 +342,22 @@ li small {
234
342
  }
235
343
 
236
344
  .content-dialog {
237
- width: min(920px, calc(100vw - 32px));
238
- max-height: calc(100svh - 32px);
345
+ position: fixed;
346
+ top: max(12px, env(safe-area-inset-top));
347
+ right: max(12px, env(safe-area-inset-right));
348
+ margin: 0;
349
+ width: min(760px, calc(100vw - 32px));
350
+ height: min(calc(100vh - 24px), 920px);
351
+ height: min(calc(100dvh - 24px), 920px);
352
+ max-height: calc(100vh - 24px);
353
+ max-height: calc(100dvh - 24px);
239
354
  padding: 0;
240
355
  border: 1px solid var(--line);
241
356
  border-radius: 8px;
242
357
  background: var(--panel);
243
358
  color: var(--text);
244
359
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.48);
360
+ overflow: hidden;
245
361
  }
246
362
 
247
363
  .content-dialog::backdrop {
@@ -252,7 +368,9 @@ li small {
252
368
  .content-dialog article {
253
369
  display: grid;
254
370
  grid-template-rows: auto auto minmax(0, 1fr);
255
- max-height: calc(100svh - 34px);
371
+ height: 100%;
372
+ max-height: 100%;
373
+ min-height: 0;
256
374
  }
257
375
 
258
376
  .content-dialog header {
@@ -262,6 +380,7 @@ li small {
262
380
  gap: 18px;
263
381
  padding: 22px;
264
382
  border-bottom: 1px solid var(--line);
383
+ background: var(--panel);
265
384
  }
266
385
 
267
386
  .content-dialog h2,
@@ -271,7 +390,7 @@ li small {
271
390
 
272
391
  .content-dialog h2 {
273
392
  margin-top: 6px;
274
- font-size: 24px;
393
+ font-size: 19px;
275
394
  line-height: 1.15;
276
395
  overflow-wrap: anywhere;
277
396
  }
@@ -282,26 +401,45 @@ li small {
282
401
  overflow-wrap: anywhere;
283
402
  }
284
403
 
285
- .content-dialog button {
404
+ #contentClose {
286
405
  flex: 0 0 auto;
406
+ width: 38px;
287
407
  height: 38px;
288
- padding: 0 14px;
408
+ padding: 0;
289
409
  border: 1px solid var(--line);
290
410
  border-radius: 8px;
291
411
  background: var(--panel-strong);
292
412
  color: var(--text);
293
413
  cursor: pointer;
414
+ font-size: 22px;
415
+ line-height: 1;
294
416
  }
295
417
 
296
- .content-dialog button:hover,
297
- .content-dialog button:focus {
418
+ #contentClose:hover,
419
+ #contentClose:focus {
298
420
  border-color: var(--accent);
299
421
  color: var(--accent);
300
422
  }
301
423
 
424
+ .content-dialog li button {
425
+ width: 100%;
426
+ height: auto;
427
+ padding: 0;
428
+ border: 0;
429
+ border-radius: 0;
430
+ background: transparent;
431
+ color: var(--text);
432
+ text-align: left;
433
+ }
434
+
435
+ .content-dialog li button:hover,
436
+ .content-dialog li button:focus {
437
+ color: var(--accent);
438
+ }
439
+
302
440
  .content-meta {
303
441
  display: grid;
304
- grid-template-columns: repeat(3, minmax(0, 1fr));
442
+ grid-template-columns: repeat(2, minmax(0, 1fr));
305
443
  gap: 10px;
306
444
  padding: 14px 22px;
307
445
  border-bottom: 1px solid var(--line);
@@ -328,45 +466,86 @@ li small {
328
466
 
329
467
  .content-meta-section ul,
330
468
  .content-meta-section .tags {
331
- max-height: 140px;
469
+ max-height: 280px;
332
470
  overflow: auto;
333
471
  align-content: flex-start;
334
472
  padding-right: 4px;
335
473
  }
336
474
 
475
+ .content-meta-section:last-child {
476
+ grid-column: span 2;
477
+ }
478
+
337
479
  .content-dialog .note-content {
338
480
  max-height: none;
339
481
  min-height: 0;
340
482
  border: 0;
341
483
  border-radius: 0;
342
- padding: 22px;
484
+ padding: 14px;
485
+ }
486
+
487
+ .app-footer {
488
+ flex: 0 0 28px;
489
+ height: 28px;
490
+ display: flex;
491
+ align-items: center;
492
+ justify-content: center;
493
+ background: transparent;
494
+ }
495
+
496
+ .app-footer small {
497
+ color: var(--muted);
498
+ font-size: 11px;
499
+ letter-spacing: 0.02em;
343
500
  }
344
501
 
345
502
  @media (max-width: 860px) {
346
- .topbar {
503
+ .graph-header {
347
504
  align-items: stretch;
348
- flex-direction: column;
505
+ flex-wrap: wrap;
506
+ padding: 10px 12px;
507
+ min-height: 0;
349
508
  }
350
509
 
351
510
  .search {
352
511
  width: 100%;
512
+ flex-basis: 100%;
513
+ order: 3;
514
+ }
515
+
516
+ .agent-filter,
517
+ .context-filter {
518
+ width: 100%;
353
519
  }
354
520
 
355
- .agent-filter {
521
+ .header-actions {
356
522
  width: 100%;
523
+ margin-left: 0;
524
+ justify-content: space-between;
525
+ order: 4;
526
+ }
527
+
528
+ .content-dialog {
529
+ inset: 8px;
530
+ 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));
531
+ width: auto;
532
+ height: auto;
533
+ max-width: none;
534
+ max-height: none;
357
535
  }
358
536
 
359
537
  .content-dialog header {
360
- align-items: stretch;
361
- flex-direction: column;
538
+ align-items: flex-start;
539
+ gap: 12px;
540
+ padding: 14px;
362
541
  }
363
542
 
364
- .floating-metrics {
365
- top: 116px;
366
- right: 18px;
367
- left: 18px;
368
- justify-content: flex-start;
369
- flex-wrap: wrap;
543
+ .content-dialog h2 {
544
+ font-size: 16px;
545
+ }
546
+
547
+ .content-dialog p {
548
+ font-size: 12px;
370
549
  }
371
550
 
372
551
  .metric-chip {
@@ -375,5 +554,16 @@ li small {
375
554
 
376
555
  .content-meta {
377
556
  grid-template-columns: 1fr;
557
+ max-height: 34dvh;
558
+ overflow: auto;
559
+ padding: 10px 14px;
560
+ }
561
+
562
+ .content-meta-section:last-child {
563
+ grid-column: auto;
564
+ }
565
+
566
+ .content-dialog .note-content {
567
+ padding: 12px;
378
568
  }
379
569
  }`;
@@ -9,51 +9,74 @@ export const createClientHtml = () => `<!doctype html>
9
9
  <body>
10
10
  <main class="shell">
11
11
  <section class="workspace" aria-label="Knowledge graph">
12
- <canvas id="graph" aria-label="Brainlink knowledge graph"></canvas>
13
- <div class="topbar">
14
- <div>
12
+ <header class="graph-header" aria-label="Graph actions">
13
+ <div class="brand-block">
15
14
  <strong>Brainlink</strong>
15
+ <span class="eyebrow">Knowledge Graph</span>
16
+ </div>
17
+ <div class="floating-metrics" aria-label="Graph totals">
18
+ <div class="metric-chip">
19
+ <strong id="nodeCount">0</strong>
20
+ <small>Notes</small>
21
+ </div>
22
+ <div class="metric-chip">
23
+ <strong id="edgeCount">0</strong>
24
+ <small>Links</small>
25
+ </div>
26
+ <div class="metric-chip">
27
+ <strong id="tagCount">0</strong>
28
+ <small>Tags</small>
29
+ </div>
16
30
  </div>
17
31
  <label class="search">
18
32
  <input id="search" type="search" placeholder="Filter notes, tags or paths" autocomplete="off" />
19
33
  </label>
20
- <label class="agent-filter">
21
- <select id="agent"></select>
22
- </label>
23
- </div>
24
- <div class="floating-metrics" aria-label="Graph totals">
25
- <div class="metric-chip">
26
- <strong id="nodeCount">0</strong>
27
- <small>Notes</small>
28
- </div>
29
- <div class="metric-chip">
30
- <strong id="edgeCount">0</strong>
31
- <small>Links</small>
32
- </div>
33
- <div class="metric-chip">
34
- <strong id="tagCount">0</strong>
35
- <small>Tags</small>
34
+ <div class="header-actions">
35
+ <label class="agent-filter">
36
+ <select id="agent"></select>
37
+ </label>
38
+ <label class="context-filter">
39
+ <select id="context"></select>
40
+ </label>
41
+ <div class="toolbar" aria-label="Graph controls">
42
+ <button id="zoomIn" type="button" title="Zoom in">+</button>
43
+ <button id="zoomOut" type="button" title="Zoom out">-</button>
44
+ <button id="fit" type="button" title="Focus central hub">◎</button>
45
+ <button id="releaseNode" type="button" title="Release selected node">◇</button>
46
+ <button id="reset" type="button" title="Reset view">⌂</button>
47
+ </div>
36
48
  </div>
37
- </div>
38
- <div class="toolbar" aria-label="Graph controls">
39
- <button id="zoomIn" type="button" title="Zoom in">+</button>
40
- <button id="zoomOut" type="button" title="Zoom out">-</button>
41
- <button id="fit" type="button" title="Fit visible nodes">◎</button>
42
- <button id="reset" type="button" title="Reset view">⌂</button>
49
+ </header>
50
+ <div class="graph-stage">
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>
43
55
  </div>
44
56
  </section>
45
57
  </main>
58
+ <footer class="app-footer" aria-label="Copyright notice">
59
+ <small>Copyright © 2026 Substructa</small>
60
+ </footer>
46
61
  <dialog id="contentDialog" class="content-dialog" aria-labelledby="contentTitle">
47
62
  <article>
48
63
  <header>
49
64
  <div>
50
- <span class="eyebrow">Markdown content</span>
65
+ <span class="eyebrow">Node details</span>
51
66
  <h2 id="contentTitle">Selected note</h2>
52
67
  <p id="contentPath"></p>
53
68
  </div>
54
- <button id="contentClose" type="button">Close</button>
69
+ <button id="contentClose" type="button" aria-label="Close node details" title="Close node details">&times;</button>
55
70
  </header>
56
71
  <div class="content-meta">
72
+ <section class="content-meta-section">
73
+ <h3>Facts</h3>
74
+ <ul id="contentFacts"></ul>
75
+ </section>
76
+ <section class="content-meta-section">
77
+ <h3>Context Links</h3>
78
+ <ul id="contentContextLinks"></ul>
79
+ </section>
57
80
  <section class="content-meta-section">
58
81
  <h3>Tags</h3>
59
82
  <div id="contentTags" class="tags"></div>