@andespindola/brainlink 0.1.0-beta.160 → 0.1.0-beta.162
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.
|
@@ -342,15 +342,21 @@ li small {
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
.content-dialog {
|
|
345
|
-
position:
|
|
345
|
+
position: absolute;
|
|
346
|
+
z-index: 6;
|
|
347
|
+
top: 12px;
|
|
348
|
+
right: 12px;
|
|
349
|
+
bottom: 12px;
|
|
350
|
+
left: 12px;
|
|
346
351
|
top: max(12px, env(safe-area-inset-top));
|
|
347
352
|
right: max(12px, env(safe-area-inset-right));
|
|
353
|
+
bottom: max(12px, env(safe-area-inset-bottom));
|
|
354
|
+
left: max(12px, calc(100% - 780px));
|
|
348
355
|
margin: 0;
|
|
349
|
-
width:
|
|
350
|
-
height:
|
|
351
|
-
|
|
352
|
-
max-height:
|
|
353
|
-
max-height: calc(100dvh - 24px);
|
|
356
|
+
width: auto;
|
|
357
|
+
height: auto;
|
|
358
|
+
max-width: none;
|
|
359
|
+
max-height: none;
|
|
354
360
|
padding: 0;
|
|
355
361
|
border: 1px solid var(--line);
|
|
356
362
|
border-radius: 8px;
|
|
@@ -360,9 +366,8 @@ li small {
|
|
|
360
366
|
overflow: hidden;
|
|
361
367
|
}
|
|
362
368
|
|
|
363
|
-
.content-dialog
|
|
364
|
-
|
|
365
|
-
backdrop-filter: blur(4px);
|
|
369
|
+
.content-dialog[hidden] {
|
|
370
|
+
display: none;
|
|
366
371
|
}
|
|
367
372
|
|
|
368
373
|
.content-dialog article {
|
|
@@ -375,6 +380,9 @@ li small {
|
|
|
375
380
|
|
|
376
381
|
.content-dialog header {
|
|
377
382
|
display: flex;
|
|
383
|
+
position: sticky;
|
|
384
|
+
top: 0;
|
|
385
|
+
z-index: 1;
|
|
378
386
|
align-items: flex-start;
|
|
379
387
|
justify-content: space-between;
|
|
380
388
|
gap: 18px;
|
|
@@ -383,6 +391,10 @@ li small {
|
|
|
383
391
|
background: var(--panel);
|
|
384
392
|
}
|
|
385
393
|
|
|
394
|
+
.content-dialog header > div {
|
|
395
|
+
min-width: 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
386
398
|
.content-dialog h2,
|
|
387
399
|
.content-dialog p {
|
|
388
400
|
margin: 0;
|
|
@@ -441,6 +453,10 @@ li small {
|
|
|
441
453
|
display: grid;
|
|
442
454
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
443
455
|
gap: 10px;
|
|
456
|
+
min-height: 0;
|
|
457
|
+
max-height: min(42vh, 360px);
|
|
458
|
+
max-height: min(42dvh, 360px);
|
|
459
|
+
overflow: auto;
|
|
444
460
|
padding: 14px 22px;
|
|
445
461
|
border-bottom: 1px solid var(--line);
|
|
446
462
|
}
|
|
@@ -554,6 +570,7 @@ li small {
|
|
|
554
570
|
|
|
555
571
|
.content-meta {
|
|
556
572
|
grid-template-columns: 1fr;
|
|
573
|
+
max-height: 34vh;
|
|
557
574
|
max-height: 34dvh;
|
|
558
575
|
overflow: auto;
|
|
559
576
|
padding: 10px 14px;
|
|
@@ -52,47 +52,47 @@ export const createClientHtml = () => `<!doctype html>
|
|
|
52
52
|
<div id="graphLabels" class="graph-labels" aria-hidden="true"></div>
|
|
53
53
|
<div id="graphTooltip" class="graph-tooltip" role="tooltip" hidden></div>
|
|
54
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">×</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>
|
|
55
90
|
</div>
|
|
56
91
|
</section>
|
|
57
92
|
</main>
|
|
58
93
|
<footer class="app-footer" aria-label="Copyright notice">
|
|
59
94
|
<small>Copyright © 2026 Substructa</small>
|
|
60
95
|
</footer>
|
|
61
|
-
<dialog id="contentDialog" class="content-dialog" aria-labelledby="contentTitle">
|
|
62
|
-
<article>
|
|
63
|
-
<header>
|
|
64
|
-
<div>
|
|
65
|
-
<span class="eyebrow">Node details</span>
|
|
66
|
-
<h2 id="contentTitle">Selected note</h2>
|
|
67
|
-
<p id="contentPath"></p>
|
|
68
|
-
</div>
|
|
69
|
-
<button id="contentClose" type="button" aria-label="Close node details" title="Close node details">×</button>
|
|
70
|
-
</header>
|
|
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>
|
|
80
|
-
<section class="content-meta-section">
|
|
81
|
-
<h3>Tags</h3>
|
|
82
|
-
<div id="contentTags" class="tags"></div>
|
|
83
|
-
</section>
|
|
84
|
-
<section class="content-meta-section">
|
|
85
|
-
<h3>Outgoing</h3>
|
|
86
|
-
<ul id="contentOutgoing"></ul>
|
|
87
|
-
</section>
|
|
88
|
-
<section class="content-meta-section">
|
|
89
|
-
<h3>Backlinks</h3>
|
|
90
|
-
<ul id="contentIncoming"></ul>
|
|
91
|
-
</section>
|
|
92
|
-
</div>
|
|
93
|
-
<pre id="contentBody" class="note-content"></pre>
|
|
94
|
-
</article>
|
|
95
|
-
</dialog>
|
|
96
96
|
<script src="/app.js"></script>
|
|
97
97
|
</body>
|
|
98
98
|
</html>`;
|
|
@@ -866,10 +866,11 @@ const linkedNodes = (node) => {
|
|
|
866
866
|
}
|
|
867
867
|
|
|
868
868
|
const openContentDialog = () => {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
869
|
+
elements.contentDialog.hidden = false
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const closeContentDialog = () => {
|
|
873
|
+
elements.contentDialog.hidden = true
|
|
873
874
|
}
|
|
874
875
|
|
|
875
876
|
const loadNodeDetails = async (nodeId) => {
|
|
@@ -1282,6 +1283,10 @@ const setupInput = () => {
|
|
|
1282
1283
|
})
|
|
1283
1284
|
|
|
1284
1285
|
window.addEventListener('keydown', (event) => {
|
|
1286
|
+
if (event.key === 'Escape' && !elements.contentDialog.hidden) {
|
|
1287
|
+
closeContentDialog()
|
|
1288
|
+
return
|
|
1289
|
+
}
|
|
1285
1290
|
if (event.key === '+') {
|
|
1286
1291
|
zoomAtPoint(state.viewport.width / 2, state.viewport.height / 2, 1.06)
|
|
1287
1292
|
return
|
|
@@ -1323,12 +1328,12 @@ const setupControls = () => {
|
|
|
1323
1328
|
})
|
|
1324
1329
|
|
|
1325
1330
|
elements.contentClose.addEventListener('click', () => {
|
|
1326
|
-
|
|
1331
|
+
closeContentDialog()
|
|
1327
1332
|
})
|
|
1328
1333
|
|
|
1329
1334
|
elements.contentDialog.addEventListener('click', (event) => {
|
|
1330
1335
|
if (event.target === elements.contentDialog) {
|
|
1331
|
-
|
|
1336
|
+
closeContentDialog()
|
|
1332
1337
|
}
|
|
1333
1338
|
})
|
|
1334
1339
|
|
|
@@ -32,6 +32,8 @@ let hoverX = null
|
|
|
32
32
|
let hoverY = null
|
|
33
33
|
let lastFrameAt = 0
|
|
34
34
|
let lastVisibleEdges = 0
|
|
35
|
+
let interactionUntil = 0
|
|
36
|
+
let settledRenderTimer = null
|
|
35
37
|
let edgePositionsBuffer = new Float32Array(0)
|
|
36
38
|
let pointPositionsBuffer = new Float32Array(0)
|
|
37
39
|
let pointSizesBuffer = new Float32Array(0)
|
|
@@ -354,6 +356,20 @@ const clear = () => {
|
|
|
354
356
|
gl.clear(gl.COLOR_BUFFER_BIT)
|
|
355
357
|
}
|
|
356
358
|
|
|
359
|
+
const isCameraInteracting = (now) => now < interactionUntil
|
|
360
|
+
|
|
361
|
+
const scheduleSettledRender = (now) => {
|
|
362
|
+
if (settledRenderTimer) {
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
const delay = Math.max(32, interactionUntil - now + 16)
|
|
366
|
+
settledRenderTimer = setTimeout(() => {
|
|
367
|
+
settledRenderTimer = null
|
|
368
|
+
dirty = true
|
|
369
|
+
requestRender()
|
|
370
|
+
}, delay)
|
|
371
|
+
}
|
|
372
|
+
|
|
357
373
|
const renderFrame = (now) => {
|
|
358
374
|
renderScheduled = false
|
|
359
375
|
if (!dirty) {
|
|
@@ -374,7 +390,13 @@ const renderFrame = (now) => {
|
|
|
374
390
|
|
|
375
391
|
cullVisibleNodes()
|
|
376
392
|
clear()
|
|
377
|
-
|
|
393
|
+
const cameraInteracting = isCameraInteracting(now)
|
|
394
|
+
if (!cameraInteracting || state.edgeCount < 1200) {
|
|
395
|
+
drawEdges()
|
|
396
|
+
} else {
|
|
397
|
+
lastVisibleEdges = 0
|
|
398
|
+
scheduleSettledRender(now)
|
|
399
|
+
}
|
|
378
400
|
|
|
379
401
|
drawNodeLayer(
|
|
380
402
|
(index) => state.visible[index] === 1 && state.selected[index] === 0 && state.highlighted[index] === 0,
|
|
@@ -442,6 +464,7 @@ const setCamera = (nextCamera) => {
|
|
|
442
464
|
camera.x = Number.isFinite(nextCamera.x) ? Number(nextCamera.x) : camera.x
|
|
443
465
|
camera.y = Number.isFinite(nextCamera.y) ? Number(nextCamera.y) : camera.y
|
|
444
466
|
camera.scale = Number.isFinite(nextCamera.scale) ? Math.max(0.0002, Math.min(8, Number(nextCamera.scale))) : camera.scale
|
|
467
|
+
interactionUntil = performance.now() + 140
|
|
445
468
|
dirty = true
|
|
446
469
|
requestRender()
|
|
447
470
|
}
|
package/package.json
CHANGED