@andespindola/brainlink 0.1.0-beta.159 → 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.
|
@@ -116,6 +116,11 @@ select {
|
|
|
116
116
|
inset: 0;
|
|
117
117
|
pointer-events: none;
|
|
118
118
|
overflow: hidden;
|
|
119
|
+
transition: opacity 120ms ease;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.graph-labels.is-stale {
|
|
123
|
+
opacity: 0;
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
.graph-label {
|
|
@@ -338,12 +343,14 @@ li small {
|
|
|
338
343
|
|
|
339
344
|
.content-dialog {
|
|
340
345
|
position: fixed;
|
|
341
|
-
top:
|
|
342
|
-
right:
|
|
346
|
+
top: max(12px, env(safe-area-inset-top));
|
|
347
|
+
right: max(12px, env(safe-area-inset-right));
|
|
343
348
|
margin: 0;
|
|
344
349
|
width: min(760px, calc(100vw - 32px));
|
|
345
|
-
height: min(calc(
|
|
346
|
-
|
|
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);
|
|
347
354
|
padding: 0;
|
|
348
355
|
border: 1px solid var(--line);
|
|
349
356
|
border-radius: 8px;
|
|
@@ -363,6 +370,7 @@ li small {
|
|
|
363
370
|
grid-template-rows: auto auto minmax(0, 1fr);
|
|
364
371
|
height: 100%;
|
|
365
372
|
max-height: 100%;
|
|
373
|
+
min-height: 0;
|
|
366
374
|
}
|
|
367
375
|
|
|
368
376
|
.content-dialog header {
|
|
@@ -372,6 +380,7 @@ li small {
|
|
|
372
380
|
gap: 18px;
|
|
373
381
|
padding: 22px;
|
|
374
382
|
border-bottom: 1px solid var(--line);
|
|
383
|
+
background: var(--panel);
|
|
375
384
|
}
|
|
376
385
|
|
|
377
386
|
.content-dialog h2,
|
|
@@ -392,23 +401,42 @@ li small {
|
|
|
392
401
|
overflow-wrap: anywhere;
|
|
393
402
|
}
|
|
394
403
|
|
|
395
|
-
|
|
404
|
+
#contentClose {
|
|
396
405
|
flex: 0 0 auto;
|
|
406
|
+
width: 38px;
|
|
397
407
|
height: 38px;
|
|
398
|
-
padding: 0
|
|
408
|
+
padding: 0;
|
|
399
409
|
border: 1px solid var(--line);
|
|
400
410
|
border-radius: 8px;
|
|
401
411
|
background: var(--panel-strong);
|
|
402
412
|
color: var(--text);
|
|
403
413
|
cursor: pointer;
|
|
414
|
+
font-size: 22px;
|
|
415
|
+
line-height: 1;
|
|
404
416
|
}
|
|
405
417
|
|
|
406
|
-
|
|
407
|
-
|
|
418
|
+
#contentClose:hover,
|
|
419
|
+
#contentClose:focus {
|
|
408
420
|
border-color: var(--accent);
|
|
409
421
|
color: var(--accent);
|
|
410
422
|
}
|
|
411
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
|
+
|
|
412
440
|
.content-meta {
|
|
413
441
|
display: grid;
|
|
414
442
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
@@ -497,19 +525,27 @@ li small {
|
|
|
497
525
|
order: 4;
|
|
498
526
|
}
|
|
499
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;
|
|
535
|
+
}
|
|
536
|
+
|
|
500
537
|
.content-dialog header {
|
|
501
|
-
align-items:
|
|
502
|
-
|
|
538
|
+
align-items: flex-start;
|
|
539
|
+
gap: 12px;
|
|
540
|
+
padding: 14px;
|
|
503
541
|
}
|
|
504
542
|
|
|
505
|
-
.content-dialog {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
height: min(calc(100svh - 150px), 760px);
|
|
512
|
-
max-height: calc(100svh - 150px);
|
|
543
|
+
.content-dialog h2 {
|
|
544
|
+
font-size: 16px;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.content-dialog p {
|
|
548
|
+
font-size: 12px;
|
|
513
549
|
}
|
|
514
550
|
|
|
515
551
|
.metric-chip {
|
|
@@ -518,5 +554,16 @@ li small {
|
|
|
518
554
|
|
|
519
555
|
.content-meta {
|
|
520
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;
|
|
521
568
|
}
|
|
522
569
|
}`;
|
|
@@ -66,7 +66,7 @@ export const createClientHtml = () => `<!doctype html>
|
|
|
66
66
|
<h2 id="contentTitle">Selected note</h2>
|
|
67
67
|
<p id="contentPath"></p>
|
|
68
68
|
</div>
|
|
69
|
-
<button id="contentClose" type="button"
|
|
69
|
+
<button id="contentClose" type="button" aria-label="Close node details" title="Close node details">×</button>
|
|
70
70
|
</header>
|
|
71
71
|
<div class="content-meta">
|
|
72
72
|
<section class="content-meta-section">
|
|
@@ -75,6 +75,7 @@ const state = {
|
|
|
75
75
|
miniMapView: null,
|
|
76
76
|
miniMapDirty: true,
|
|
77
77
|
overlayScheduled: false,
|
|
78
|
+
overlayIdleTimer: null,
|
|
78
79
|
chunk: {
|
|
79
80
|
nodes: [],
|
|
80
81
|
edges: []
|
|
@@ -86,6 +87,7 @@ const state = {
|
|
|
86
87
|
fetchToken: 0,
|
|
87
88
|
fetchTimer: null,
|
|
88
89
|
fetchAbortController: null,
|
|
90
|
+
lastChunkRequestKey: '',
|
|
89
91
|
cameraSyncScheduled: false,
|
|
90
92
|
lastWheelAt: 0,
|
|
91
93
|
lastVisibleNodes: 0,
|
|
@@ -396,6 +398,30 @@ const getZoomEdgeBudget = () => {
|
|
|
396
398
|
return 26000
|
|
397
399
|
}
|
|
398
400
|
|
|
401
|
+
const zoomDetailBand = () => {
|
|
402
|
+
const scale = state.camera.scale
|
|
403
|
+
if (scale < 0.06) return 'far'
|
|
404
|
+
if (scale < 0.12) return 'wide'
|
|
405
|
+
if (scale < 0.24) return 'mid'
|
|
406
|
+
if (scale < 0.7) return 'near'
|
|
407
|
+
return 'detail'
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const graphStreamRequestKey = ({ x, y, w, h }) => {
|
|
411
|
+
const grid = Math.max(80, Math.min(720, Math.max(w, h) / 6))
|
|
412
|
+
return [
|
|
413
|
+
state.agentId || '*',
|
|
414
|
+
state.contextId || '*',
|
|
415
|
+
zoomDetailBand(),
|
|
416
|
+
getZoomNodeBudget(),
|
|
417
|
+
getZoomEdgeBudget(),
|
|
418
|
+
Math.round(x / grid),
|
|
419
|
+
Math.round(y / grid),
|
|
420
|
+
Math.round(w / grid),
|
|
421
|
+
Math.round(h / grid)
|
|
422
|
+
].join(':')
|
|
423
|
+
}
|
|
424
|
+
|
|
399
425
|
const screenToWorld = (screenX, screenY) => ({
|
|
400
426
|
x: (screenX - state.camera.x) / state.camera.scale,
|
|
401
427
|
y: (screenY - state.camera.y) / state.camera.scale
|
|
@@ -727,6 +753,8 @@ const drawMiniMap = () => {
|
|
|
727
753
|
ctx.strokeRect(topLeft.x, topLeft.y, Math.max(3, bottomRight.x - topLeft.x), Math.max(3, bottomRight.y - topLeft.y))
|
|
728
754
|
}
|
|
729
755
|
|
|
756
|
+
const shouldDeferGraphOverlays = () => state.pointer.down || performance.now() - state.lastWheelAt < 150
|
|
757
|
+
|
|
730
758
|
const updateGraphOverlays = () => {
|
|
731
759
|
if (state.overlayScheduled) {
|
|
732
760
|
return
|
|
@@ -734,6 +762,17 @@ const updateGraphOverlays = () => {
|
|
|
734
762
|
state.overlayScheduled = true
|
|
735
763
|
requestAnimationFrame(() => {
|
|
736
764
|
state.overlayScheduled = false
|
|
765
|
+
if (shouldDeferGraphOverlays()) {
|
|
766
|
+
elements.labels?.classList.add('is-stale')
|
|
767
|
+
if (!state.overlayIdleTimer) {
|
|
768
|
+
state.overlayIdleTimer = setTimeout(() => {
|
|
769
|
+
state.overlayIdleTimer = null
|
|
770
|
+
updateGraphOverlays()
|
|
771
|
+
}, 170)
|
|
772
|
+
}
|
|
773
|
+
return
|
|
774
|
+
}
|
|
775
|
+
elements.labels?.classList.remove('is-stale')
|
|
737
776
|
drawLabels()
|
|
738
777
|
if (state.miniMapDirty) {
|
|
739
778
|
drawMiniMap()
|
|
@@ -947,6 +986,11 @@ const fetchChunk = async ({ fit } = { fit: false }) => {
|
|
|
947
986
|
params.set('context', state.contextId)
|
|
948
987
|
}
|
|
949
988
|
|
|
989
|
+
const requestKey = graphStreamRequestKey({ x, y, w, h })
|
|
990
|
+
if (!fit && state.lastChunkRequestKey === requestKey && state.chunk.nodes.length > 0) {
|
|
991
|
+
return
|
|
992
|
+
}
|
|
993
|
+
|
|
950
994
|
const response = await fetch('/api/graph-stream?' + params.toString(), { signal: controller.signal })
|
|
951
995
|
if (!response.ok) {
|
|
952
996
|
throw new Error('Failed to fetch graph stream chunk')
|
|
@@ -961,6 +1005,7 @@ const fetchChunk = async ({ fit } = { fit: false }) => {
|
|
|
961
1005
|
}
|
|
962
1006
|
|
|
963
1007
|
state.graphSignature = typeof chunk.signature === 'string' ? chunk.signature : ''
|
|
1008
|
+
state.lastChunkRequestKey = requestKey
|
|
964
1009
|
ensureNodePositionsLoaded()
|
|
965
1010
|
await syncNodePositionsFromServer()
|
|
966
1011
|
state.graphMode = typeof chunk.mode === 'string' ? chunk.mode : 'near'
|
|
@@ -1000,8 +1045,9 @@ const scheduleChunkFetch = ({ fit } = { fit: false }) => {
|
|
|
1000
1045
|
}
|
|
1001
1046
|
|
|
1002
1047
|
const now = performance.now()
|
|
1003
|
-
const recentlyWheeling = now - state.lastWheelAt <
|
|
1004
|
-
const
|
|
1048
|
+
const recentlyWheeling = now - state.lastWheelAt < 320
|
|
1049
|
+
const heavyScene = state.lastVisibleNodes > 1200 || state.lastVisibleEdges > 3500
|
|
1050
|
+
const delay = fit ? 0 : (state.pointer.down ? 320 : (recentlyWheeling ? (heavyScene ? 420 : 300) : (heavyScene ? 120 : 72)))
|
|
1005
1051
|
state.fetchTimer = setTimeout(() => {
|
|
1006
1052
|
state.fetchTimer = null
|
|
1007
1053
|
fetchChunk({ fit }).catch((error) => {
|
package/package.json
CHANGED