@andespindola/brainlink 0.1.0-beta.97 → 0.1.0-beta.99
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.
- package/README.md +1 -1
- package/dist/application/frontend/client-js.js +52 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -602,7 +602,7 @@ The graph UI shows:
|
|
|
602
602
|
- WebGL node and edge acceleration when supported, falling back to Canvas 2D without changing graph behavior
|
|
603
603
|
- compact macro-to-micro density progression so reset keeps the graph mass oriented and zoom-in separates local neighborhoods progressively
|
|
604
604
|
- graph camera treats hub-centered navigation as structural only when the hub is dominant; diffuse stress graphs reset and zoom around the full graph mass
|
|
605
|
-
- graph LOD progression: graphs up to 1000 notes render directly; larger graphs use one recursive model where each visible level targets up to 999 non-hub nodes, starts from a memory-hub-centered mesh, and each supernode can expand into another same-shape subgraph level (again up to 999 children) with latent fade-in, aggregated real links and local sibling mesh links so org-heavy and stress-50k follow the same structure at different depths
|
|
605
|
+
- graph LOD progression: graphs up to 1000 notes render directly; larger graphs use one recursive model where each visible level targets up to 999 non-hub nodes, starts from a memory-hub-centered mesh, and each supernode can expand into another same-shape subgraph level (again up to 999 children) with latent fade-in, aggregated real links and local sibling mesh links so org-heavy and stress-50k follow the same structure at different depths; for massive graphs the first expansion starts much deeper in zoom and low-size child levels use slower easing so the view stays as one compact graph longer
|
|
606
606
|
|
|
607
607
|
The server indexes before starting by default. Use `--no-index` to skip that step:
|
|
608
608
|
|
|
@@ -782,17 +782,20 @@ const buildEcosystemExpansionLevels = (levelSizes, nodeCount) => {
|
|
|
782
782
|
if (levelSizes.length <= 1) {
|
|
783
783
|
return []
|
|
784
784
|
}
|
|
785
|
-
const
|
|
785
|
+
const isMassive = nodeCount > massiveGraphNodeThreshold
|
|
786
|
+
const maxScale = isMassive
|
|
786
787
|
? massiveEcosystemClusterScaleThreshold
|
|
787
788
|
: ecosystemClusterScaleThreshold
|
|
788
|
-
const startScale = 0.
|
|
789
|
+
const startScale = isMassive ? 0.82 : 0.18
|
|
789
790
|
const transitionCount = levelSizes.length - 1
|
|
790
791
|
const usableScale = Math.max(0.08, maxScale - startScale)
|
|
791
792
|
const step = usableScale / transitionCount
|
|
793
|
+
const stride = isMassive ? 0.9 : 0.78
|
|
794
|
+
const overlap = isMassive ? 1.28 : 1.75
|
|
792
795
|
const levels = []
|
|
793
796
|
for (let index = 0; index < transitionCount; index += 1) {
|
|
794
|
-
const start = startScale + step * index *
|
|
795
|
-
const end = Math.min(maxScale, start + step *
|
|
797
|
+
const start = startScale + step * index * stride
|
|
798
|
+
const end = Math.min(maxScale, start + step * overlap)
|
|
796
799
|
levels.push({
|
|
797
800
|
parentSize: levelSizes[index],
|
|
798
801
|
childSize: levelSizes[index + 1],
|
|
@@ -936,6 +939,10 @@ const filterEcosystemClustersByViewport = (clusters, viewport) => {
|
|
|
936
939
|
}
|
|
937
940
|
|
|
938
941
|
const ecosystemFocusPoint = () => {
|
|
942
|
+
const cursorPoint = cursorWorldPoint()
|
|
943
|
+
if (cursorPoint) {
|
|
944
|
+
return cursorPoint
|
|
945
|
+
}
|
|
939
946
|
const now = performance.now()
|
|
940
947
|
if (now - state.lastZoomFocus.at <= 1800) {
|
|
941
948
|
return { x: state.lastZoomFocus.x, y: state.lastZoomFocus.y }
|
|
@@ -947,7 +954,11 @@ const nearestEcosystemParentIds = (clusters, focusPoint, limit) =>
|
|
|
947
954
|
clusters
|
|
948
955
|
.map(cluster => ({
|
|
949
956
|
cluster,
|
|
950
|
-
distance: Math.
|
|
957
|
+
distance: Math.max(
|
|
958
|
+
0,
|
|
959
|
+
Math.hypot(cluster.x - focusPoint.x, cluster.y - focusPoint.y) -
|
|
960
|
+
clusterRadiusPx(cluster) / Math.max(state.transform.scale, 0.0001)
|
|
961
|
+
)
|
|
951
962
|
}))
|
|
952
963
|
.sort((left, right) => left.distance - right.distance)
|
|
953
964
|
.slice(0, limit)
|
|
@@ -962,7 +973,8 @@ const zoomProgress = (scale, start, end) =>
|
|
|
962
973
|
smoothStep((scale - start) / Math.max(end - start, 0.0001))
|
|
963
974
|
|
|
964
975
|
const semanticZoomSpread = (progress, childSize) => {
|
|
965
|
-
const
|
|
976
|
+
const spreadExponent = childSize <= Math.ceil(ecosystemLevelNodeCap / 12) ? 5.6 : 4.2
|
|
977
|
+
const curve = Math.pow(progress, spreadExponent)
|
|
966
978
|
if (childSize >= Math.ceil(ecosystemLevelNodeCap / 2)) {
|
|
967
979
|
return 0.12 + curve * 0.88
|
|
968
980
|
}
|
|
@@ -970,7 +982,8 @@ const semanticZoomSpread = (progress, childSize) => {
|
|
|
970
982
|
}
|
|
971
983
|
|
|
972
984
|
const opacityForProgress = (progress, childSize) => {
|
|
973
|
-
const
|
|
985
|
+
const opacityExponent = childSize <= Math.ceil(ecosystemLevelNodeCap / 12) ? 2.8 : 2.1
|
|
986
|
+
const eased = Math.pow(progress, opacityExponent)
|
|
974
987
|
if (childSize >= Math.ceil(ecosystemLevelNodeCap / 2)) {
|
|
975
988
|
return 0.22 + eased * 0.78
|
|
976
989
|
}
|
|
@@ -1293,6 +1306,29 @@ const viewportCenterWorldPoint = () => {
|
|
|
1293
1306
|
}
|
|
1294
1307
|
}
|
|
1295
1308
|
|
|
1309
|
+
const screenToWorldPoint = (screenX, screenY) => ({
|
|
1310
|
+
x: (screenX - state.transform.x) / state.transform.scale,
|
|
1311
|
+
y: (screenY - state.transform.y) / state.transform.scale
|
|
1312
|
+
})
|
|
1313
|
+
|
|
1314
|
+
const cursorWorldPoint = () => {
|
|
1315
|
+
if (!state.cursor.inCanvas) {
|
|
1316
|
+
return null
|
|
1317
|
+
}
|
|
1318
|
+
const rect = canvas.getBoundingClientRect()
|
|
1319
|
+
const screenX = state.cursor.x - rect.left
|
|
1320
|
+
const screenY = state.cursor.y - rect.top
|
|
1321
|
+
const width = Math.max(rect.width, 320)
|
|
1322
|
+
const height = Math.max(rect.height, 320)
|
|
1323
|
+
if (!Number.isFinite(screenX) || !Number.isFinite(screenY)) {
|
|
1324
|
+
return null
|
|
1325
|
+
}
|
|
1326
|
+
if (screenX < 0 || screenX > width || screenY < 0 || screenY > height) {
|
|
1327
|
+
return null
|
|
1328
|
+
}
|
|
1329
|
+
return screenToWorldPoint(screenX, screenY)
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1296
1332
|
const visibilityScaleBucket = (scale) => {
|
|
1297
1333
|
const safeScale = Math.max(zoomRange.min, scale)
|
|
1298
1334
|
if (safeScale < 0.01) return Math.round(safeScale * 300_000)
|
|
@@ -1348,11 +1384,12 @@ const selectStableSampleNodes = (sourceNodes, limit) => {
|
|
|
1348
1384
|
}
|
|
1349
1385
|
|
|
1350
1386
|
const now = performance.now()
|
|
1387
|
+
const cursorPoint = cursorWorldPoint()
|
|
1351
1388
|
const recentZoomFocus =
|
|
1352
1389
|
now - state.lastZoomFocus.at <= 1500
|
|
1353
1390
|
? { x: state.lastZoomFocus.x, y: state.lastZoomFocus.y }
|
|
1354
1391
|
: null
|
|
1355
|
-
const anchor = recentZoomFocus ?? viewportCenterWorldPoint()
|
|
1392
|
+
const anchor = cursorPoint ?? recentZoomFocus ?? viewportCenterWorldPoint()
|
|
1356
1393
|
const previousIds = new Set(state.renderNodes.map((node) => node.id))
|
|
1357
1394
|
const preferAnchorDistance = state.visibleNodes.length > massiveGraphNodeThreshold && state.transform.scale >= 0.28
|
|
1358
1395
|
|
|
@@ -1386,11 +1423,12 @@ const selectAccessBridgeNodes = (sourceNodes, limit) => {
|
|
|
1386
1423
|
}
|
|
1387
1424
|
|
|
1388
1425
|
const now = performance.now()
|
|
1426
|
+
const cursorPoint = cursorWorldPoint()
|
|
1389
1427
|
const recentZoomFocus =
|
|
1390
1428
|
now - state.lastZoomFocus.at <= 1200
|
|
1391
1429
|
? { x: state.lastZoomFocus.x, y: state.lastZoomFocus.y }
|
|
1392
1430
|
: null
|
|
1393
|
-
const anchor = recentZoomFocus ?? viewportCenterWorldPoint()
|
|
1431
|
+
const anchor = cursorPoint ?? recentZoomFocus ?? viewportCenterWorldPoint()
|
|
1394
1432
|
return [...sourceNodes]
|
|
1395
1433
|
.sort((left, right) => {
|
|
1396
1434
|
const leftDistance = Math.hypot(left.x - anchor.x, left.y - anchor.y)
|
|
@@ -2468,10 +2506,7 @@ const tick = delta => {
|
|
|
2468
2506
|
|
|
2469
2507
|
const worldPoint = event => {
|
|
2470
2508
|
const rect = canvas.getBoundingClientRect()
|
|
2471
|
-
return
|
|
2472
|
-
x: (event.clientX - rect.left - state.transform.x) / state.transform.scale,
|
|
2473
|
-
y: (event.clientY - rect.top - state.transform.y) / state.transform.scale
|
|
2474
|
-
}
|
|
2509
|
+
return screenToWorldPoint(event.clientX - rect.left, event.clientY - rect.top)
|
|
2475
2510
|
}
|
|
2476
2511
|
|
|
2477
2512
|
const connectedNodeIdsFor = (nodeId) => {
|
|
@@ -3218,8 +3253,9 @@ const zoomAtPoint = (screenX, screenY, factor, source = 'generic') => {
|
|
|
3218
3253
|
if (nextScale === state.transform.scale) {
|
|
3219
3254
|
return
|
|
3220
3255
|
}
|
|
3221
|
-
const
|
|
3222
|
-
const
|
|
3256
|
+
const worldPointAtCursor = screenToWorldPoint(screenX, screenY)
|
|
3257
|
+
const worldX = worldPointAtCursor.x
|
|
3258
|
+
const worldY = worldPointAtCursor.y
|
|
3223
3259
|
state.lastZoomFocus = {
|
|
3224
3260
|
x: worldX,
|
|
3225
3261
|
y: worldY,
|
|
@@ -3266,6 +3302,7 @@ const handleWheelZoom = event => {
|
|
|
3266
3302
|
const rawCursorY = Number.isFinite(event.offsetY) ? event.offsetY : event.clientY - rect.top
|
|
3267
3303
|
const cursorX = Math.max(0, Math.min(Math.max(rect.width, 320), rawCursorX))
|
|
3268
3304
|
const cursorY = Math.max(0, Math.min(Math.max(rect.height, 320), rawCursorY))
|
|
3305
|
+
state.cursor = { x: event.clientX, y: event.clientY, inCanvas: true }
|
|
3269
3306
|
const factor = wheelZoomFactor(event)
|
|
3270
3307
|
|
|
3271
3308
|
if (!Number.isFinite(factor) || factor <= 0 || factor === 1) {
|
package/package.json
CHANGED