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

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 +258 -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 +645 -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,27 @@ 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: 12px;
347
+ right: 12px;
348
+ bottom: 12px;
349
+ left: 12px;
350
+ top: max(12px, env(safe-area-inset-top));
351
+ right: max(12px, env(safe-area-inset-right));
352
+ bottom: max(12px, env(safe-area-inset-bottom));
353
+ left: max(12px, calc(100vw - 780px));
354
+ margin: 0;
355
+ width: auto;
356
+ height: auto;
357
+ max-width: none;
358
+ max-height: none;
239
359
  padding: 0;
240
360
  border: 1px solid var(--line);
241
361
  border-radius: 8px;
242
362
  background: var(--panel);
243
363
  color: var(--text);
244
364
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.48);
365
+ overflow: hidden;
245
366
  }
246
367
 
247
368
  .content-dialog::backdrop {
@@ -252,16 +373,26 @@ li small {
252
373
  .content-dialog article {
253
374
  display: grid;
254
375
  grid-template-rows: auto auto minmax(0, 1fr);
255
- max-height: calc(100svh - 34px);
376
+ height: 100%;
377
+ max-height: 100%;
378
+ min-height: 0;
256
379
  }
257
380
 
258
381
  .content-dialog header {
259
382
  display: flex;
383
+ position: sticky;
384
+ top: 0;
385
+ z-index: 1;
260
386
  align-items: flex-start;
261
387
  justify-content: space-between;
262
388
  gap: 18px;
263
389
  padding: 22px;
264
390
  border-bottom: 1px solid var(--line);
391
+ background: var(--panel);
392
+ }
393
+
394
+ .content-dialog header > div {
395
+ min-width: 0;
265
396
  }
266
397
 
267
398
  .content-dialog h2,
@@ -271,7 +402,7 @@ li small {
271
402
 
272
403
  .content-dialog h2 {
273
404
  margin-top: 6px;
274
- font-size: 24px;
405
+ font-size: 19px;
275
406
  line-height: 1.15;
276
407
  overflow-wrap: anywhere;
277
408
  }
@@ -282,27 +413,50 @@ li small {
282
413
  overflow-wrap: anywhere;
283
414
  }
284
415
 
285
- .content-dialog button {
416
+ #contentClose {
286
417
  flex: 0 0 auto;
418
+ width: 38px;
287
419
  height: 38px;
288
- padding: 0 14px;
420
+ padding: 0;
289
421
  border: 1px solid var(--line);
290
422
  border-radius: 8px;
291
423
  background: var(--panel-strong);
292
424
  color: var(--text);
293
425
  cursor: pointer;
426
+ font-size: 22px;
427
+ line-height: 1;
294
428
  }
295
429
 
296
- .content-dialog button:hover,
297
- .content-dialog button:focus {
430
+ #contentClose:hover,
431
+ #contentClose:focus {
298
432
  border-color: var(--accent);
299
433
  color: var(--accent);
300
434
  }
301
435
 
436
+ .content-dialog li button {
437
+ width: 100%;
438
+ height: auto;
439
+ padding: 0;
440
+ border: 0;
441
+ border-radius: 0;
442
+ background: transparent;
443
+ color: var(--text);
444
+ text-align: left;
445
+ }
446
+
447
+ .content-dialog li button:hover,
448
+ .content-dialog li button:focus {
449
+ color: var(--accent);
450
+ }
451
+
302
452
  .content-meta {
303
453
  display: grid;
304
- grid-template-columns: repeat(3, minmax(0, 1fr));
454
+ grid-template-columns: repeat(2, minmax(0, 1fr));
305
455
  gap: 10px;
456
+ min-height: 0;
457
+ max-height: min(42vh, 360px);
458
+ max-height: min(42dvh, 360px);
459
+ overflow: auto;
306
460
  padding: 14px 22px;
307
461
  border-bottom: 1px solid var(--line);
308
462
  }
@@ -328,45 +482,86 @@ li small {
328
482
 
329
483
  .content-meta-section ul,
330
484
  .content-meta-section .tags {
331
- max-height: 140px;
485
+ max-height: 280px;
332
486
  overflow: auto;
333
487
  align-content: flex-start;
334
488
  padding-right: 4px;
335
489
  }
336
490
 
491
+ .content-meta-section:last-child {
492
+ grid-column: span 2;
493
+ }
494
+
337
495
  .content-dialog .note-content {
338
496
  max-height: none;
339
497
  min-height: 0;
340
498
  border: 0;
341
499
  border-radius: 0;
342
- padding: 22px;
500
+ padding: 14px;
501
+ }
502
+
503
+ .app-footer {
504
+ flex: 0 0 28px;
505
+ height: 28px;
506
+ display: flex;
507
+ align-items: center;
508
+ justify-content: center;
509
+ background: transparent;
510
+ }
511
+
512
+ .app-footer small {
513
+ color: var(--muted);
514
+ font-size: 11px;
515
+ letter-spacing: 0.02em;
343
516
  }
344
517
 
345
518
  @media (max-width: 860px) {
346
- .topbar {
519
+ .graph-header {
347
520
  align-items: stretch;
348
- flex-direction: column;
521
+ flex-wrap: wrap;
522
+ padding: 10px 12px;
523
+ min-height: 0;
349
524
  }
350
525
 
351
526
  .search {
352
527
  width: 100%;
528
+ flex-basis: 100%;
529
+ order: 3;
530
+ }
531
+
532
+ .agent-filter,
533
+ .context-filter {
534
+ width: 100%;
353
535
  }
354
536
 
355
- .agent-filter {
537
+ .header-actions {
356
538
  width: 100%;
539
+ margin-left: 0;
540
+ justify-content: space-between;
541
+ order: 4;
542
+ }
543
+
544
+ .content-dialog {
545
+ inset: 8px;
546
+ 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));
547
+ width: auto;
548
+ height: auto;
549
+ max-width: none;
550
+ max-height: none;
357
551
  }
358
552
 
359
553
  .content-dialog header {
360
- align-items: stretch;
361
- flex-direction: column;
554
+ align-items: flex-start;
555
+ gap: 12px;
556
+ padding: 14px;
362
557
  }
363
558
 
364
- .floating-metrics {
365
- top: 116px;
366
- right: 18px;
367
- left: 18px;
368
- justify-content: flex-start;
369
- flex-wrap: wrap;
559
+ .content-dialog h2 {
560
+ font-size: 16px;
561
+ }
562
+
563
+ .content-dialog p {
564
+ font-size: 12px;
370
565
  }
371
566
 
372
567
  .metric-chip {
@@ -375,5 +570,17 @@ li small {
375
570
 
376
571
  .content-meta {
377
572
  grid-template-columns: 1fr;
573
+ max-height: 34vh;
574
+ max-height: 34dvh;
575
+ overflow: auto;
576
+ padding: 10px 14px;
577
+ }
578
+
579
+ .content-meta-section:last-child {
580
+ grid-column: auto;
581
+ }
582
+
583
+ .content-dialog .note-content {
584
+ padding: 12px;
378
585
  }
379
586
  }`;
@@ -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>