@andespindola/brainlink 0.1.0-beta.136 → 0.1.0-beta.137
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.
|
@@ -89,6 +89,7 @@ const state = {
|
|
|
89
89
|
lastHoverHitAt: 0,
|
|
90
90
|
lastManualZoomAt: 0,
|
|
91
91
|
lastZoomFocus: { x: 0, y: 0, at: 0 },
|
|
92
|
+
leafFocusRootNodeId: null,
|
|
92
93
|
hierarchyFocusGroupId: null,
|
|
93
94
|
hierarchyFocusStack: [],
|
|
94
95
|
hierarchyRevealFocusGroupId: null,
|
|
@@ -1337,6 +1338,68 @@ const arrangeChildGraphNodes = (nodes, group, origin = group) => {
|
|
|
1337
1338
|
return arranged
|
|
1338
1339
|
}
|
|
1339
1340
|
|
|
1341
|
+
const projectNodesIntoChildGraph = (nodes, focusRenderNode, group) => {
|
|
1342
|
+
if (nodes.length <= 1) {
|
|
1343
|
+
return nodes.map(node => ({ ...node, x: focusRenderNode.x, y: focusRenderNode.y }))
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
let minX = Number.POSITIVE_INFINITY
|
|
1347
|
+
let maxX = Number.NEGATIVE_INFINITY
|
|
1348
|
+
let minY = Number.POSITIVE_INFINITY
|
|
1349
|
+
let maxY = Number.NEGATIVE_INFINITY
|
|
1350
|
+
|
|
1351
|
+
for (let index = 0; index < nodes.length; index += 1) {
|
|
1352
|
+
const node = nodes[index]
|
|
1353
|
+
minX = Math.min(minX, node.x)
|
|
1354
|
+
maxX = Math.max(maxX, node.x)
|
|
1355
|
+
minY = Math.min(minY, node.y)
|
|
1356
|
+
maxY = Math.max(maxY, node.y)
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
const centerX = (minX + maxX) / 2
|
|
1360
|
+
const centerY = (minY + maxY) / 2
|
|
1361
|
+
const spanX = Math.max(1, maxX - minX)
|
|
1362
|
+
const spanY = Math.max(1, maxY - minY)
|
|
1363
|
+
const targetDiameter = Math.max(1, childGraphRenderRadius(group) * 2 * 0.9)
|
|
1364
|
+
const scale = targetDiameter / Math.max(spanX, spanY)
|
|
1365
|
+
|
|
1366
|
+
return nodes.map(node => ({
|
|
1367
|
+
...node,
|
|
1368
|
+
x: focusRenderNode.x + (node.x - centerX) * scale,
|
|
1369
|
+
y: focusRenderNode.y + (node.y - centerY) * scale
|
|
1370
|
+
}))
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
const recursiveLeafSubgraphNodes = (rootNodeId, maxNodes = renderNodeBudget) => {
|
|
1374
|
+
const root = state.nodeById.get(rootNodeId)
|
|
1375
|
+
if (!root) {
|
|
1376
|
+
return []
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
const visited = new Set([rootNodeId])
|
|
1380
|
+
const queue = [rootNodeId]
|
|
1381
|
+
|
|
1382
|
+
while (queue.length > 0 && visited.size < maxNodes) {
|
|
1383
|
+
const currentId = queue.shift()
|
|
1384
|
+
const edges = [...(state.visibleEdgeByNode.get(currentId) ?? [])]
|
|
1385
|
+
.filter(edge => edge.target)
|
|
1386
|
+
.sort((left, right) => edgeWeight(right) - edgeWeight(left))
|
|
1387
|
+
for (let edgeIndex = 0; edgeIndex < edges.length && visited.size < maxNodes; edgeIndex += 1) {
|
|
1388
|
+
const edge = edges[edgeIndex]
|
|
1389
|
+
const nextId = edge.source === currentId ? edge.target : edge.source
|
|
1390
|
+
if (!nextId || visited.has(nextId) || !state.nodeById.has(nextId)) {
|
|
1391
|
+
continue
|
|
1392
|
+
}
|
|
1393
|
+
visited.add(nextId)
|
|
1394
|
+
queue.push(nextId)
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
return [...visited]
|
|
1399
|
+
.map(id => state.nodeById.get(id))
|
|
1400
|
+
.filter(Boolean)
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1340
1403
|
const arrangeChildGroupNodes = (groups, parentGroup, origin) => {
|
|
1341
1404
|
if (groups.length <= 1) {
|
|
1342
1405
|
return groups.map(group => ({
|
|
@@ -1502,6 +1565,7 @@ const updateHierarchyFocusGroup = (groups, _viewport) => {
|
|
|
1502
1565
|
state.selected = null
|
|
1503
1566
|
}
|
|
1504
1567
|
}
|
|
1568
|
+
state.leafFocusRootNodeId = null
|
|
1505
1569
|
state.hierarchyFocusGroupId = null
|
|
1506
1570
|
return null
|
|
1507
1571
|
}
|
|
@@ -1622,12 +1686,23 @@ const computeHierarchyRenderVisibility = (viewport) => {
|
|
|
1622
1686
|
const focusChildGroups = focus.childGroupIds
|
|
1623
1687
|
.map(groupId => state.groupById.get(groupId))
|
|
1624
1688
|
.filter(Boolean)
|
|
1689
|
+
if (focusChildGroups.length > 0) {
|
|
1690
|
+
state.leafFocusRootNodeId = null
|
|
1691
|
+
} else if (state.leafFocusRootNodeId && !focusIds.has(state.leafFocusRootNodeId)) {
|
|
1692
|
+
state.leafFocusRootNodeId = null
|
|
1693
|
+
}
|
|
1625
1694
|
const childTargetLimit = Math.min(renderNodeBudget, Math.max(1, Math.floor(renderNodeBudget * revealProgress)))
|
|
1626
1695
|
const childLimit = updateHierarchyChildRevealBudget(focus.id, childTargetLimit)
|
|
1627
1696
|
const focusRenderNode = groupNodes.find(node => node.groupId === focus.id) ?? createGroupRenderNode(focus)
|
|
1628
|
-
const arrangedChildren =
|
|
1629
|
-
|
|
1630
|
-
|
|
1697
|
+
const arrangedChildren = (() => {
|
|
1698
|
+
if (focusChildGroups.length > 0) {
|
|
1699
|
+
return arrangeChildGroupNodes(focusChildGroups, focus, focusRenderNode)
|
|
1700
|
+
}
|
|
1701
|
+
const recursiveNodes = state.leafFocusRootNodeId
|
|
1702
|
+
? recursiveLeafSubgraphNodes(state.leafFocusRootNodeId, renderNodeBudget)
|
|
1703
|
+
: rawChildNodes
|
|
1704
|
+
return projectNodesIntoChildGraph(recursiveNodes, focusRenderNode, focus)
|
|
1705
|
+
})()
|
|
1631
1706
|
const childNodes = selectStableSampleNodes(arrangedChildren, childLimit)
|
|
1632
1707
|
.map(node => interpolateNodeFromGroup(node, focusRenderNode, revealProgress))
|
|
1633
1708
|
const childIds = new Set(childNodes.map(node => node.id))
|
|
@@ -2121,6 +2196,7 @@ const fitView = (options = { useFiltered: true, preferHubCenter: true }) => {
|
|
|
2121
2196
|
const resetView = () => fitView({ useFiltered: false, preferHubCenter: false })
|
|
2122
2197
|
|
|
2123
2198
|
const resetHierarchyFocus = () => {
|
|
2199
|
+
state.leafFocusRootNodeId = null
|
|
2124
2200
|
state.hierarchyFocusGroupId = null
|
|
2125
2201
|
state.hierarchyFocusStack = []
|
|
2126
2202
|
state.hierarchyRevealFocusGroupId = null
|
|
@@ -3128,6 +3204,22 @@ const expandGroupNode = node => {
|
|
|
3128
3204
|
}
|
|
3129
3205
|
}
|
|
3130
3206
|
|
|
3207
|
+
const expandLeafNodeGraph = node => {
|
|
3208
|
+
if (!node || node.isGroupNode) return
|
|
3209
|
+
state.selected = node
|
|
3210
|
+
state.leafFocusRootNodeId = node.id
|
|
3211
|
+
state.zoomTransition = {
|
|
3212
|
+
active: true,
|
|
3213
|
+
source: 'group',
|
|
3214
|
+
screenX: state.viewport.width / 2,
|
|
3215
|
+
screenY: state.viewport.height / 2,
|
|
3216
|
+
worldX: node.x,
|
|
3217
|
+
worldY: node.y,
|
|
3218
|
+
targetScale: clampScale(Math.max(state.transform.scale * 1.08, hierarchyMicroExitScale * 1.02))
|
|
3219
|
+
}
|
|
3220
|
+
state.lastZoomFocus = { x: node.x, y: node.y, at: performance.now() }
|
|
3221
|
+
markRenderDirty()
|
|
3222
|
+
}
|
|
3131
3223
|
const handleGroupNodePrimaryClick = node => {
|
|
3132
3224
|
if (!node?.isGroupNode) return
|
|
3133
3225
|
const alreadyFocused = state.selected?.isGroupNode && state.selected.groupId === node.groupId
|
|
@@ -3138,8 +3230,19 @@ const handleGroupNodePrimaryClick = node => {
|
|
|
3138
3230
|
expandGroupNode(node)
|
|
3139
3231
|
}
|
|
3140
3232
|
|
|
3233
|
+
const handleLeafNodePrimaryClick = node => {
|
|
3234
|
+
if (!node || node.isGroupNode) return
|
|
3235
|
+
const sameFocusedNode = state.leafFocusRootNodeId === node.id
|
|
3236
|
+
if (!sameFocusedNode) {
|
|
3237
|
+
expandLeafNodeGraph(node)
|
|
3238
|
+
return
|
|
3239
|
+
}
|
|
3240
|
+
selectNode(node, { openContent: true })
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3141
3243
|
const selectNode = (node, options = { openContent: false }) => {
|
|
3142
3244
|
if (!node) {
|
|
3245
|
+
state.leafFocusRootNodeId = null
|
|
3143
3246
|
state.selected = null
|
|
3144
3247
|
if (elements.contentDialog.open) {
|
|
3145
3248
|
elements.contentDialog.close()
|
|
@@ -3376,6 +3479,8 @@ const bindEvents = () => {
|
|
|
3376
3479
|
const clickedNode = draggedNode ?? state.hovered
|
|
3377
3480
|
if (clickedNode?.isGroupNode) {
|
|
3378
3481
|
handleGroupNodePrimaryClick(clickedNode)
|
|
3482
|
+
} else if (clickedNode && state.hierarchyFocusGroupId) {
|
|
3483
|
+
handleLeafNodePrimaryClick(clickedNode)
|
|
3379
3484
|
} else {
|
|
3380
3485
|
selectNode(clickedNode, { openContent: true })
|
|
3381
3486
|
}
|
|
@@ -3465,6 +3570,7 @@ const loadGraph = async (options = { reset: false }) => {
|
|
|
3465
3570
|
state.graph = graph
|
|
3466
3571
|
state.nodes = layout.nodes
|
|
3467
3572
|
state.groups = layout.groups
|
|
3573
|
+
state.leafFocusRootNodeId = null
|
|
3468
3574
|
state.hierarchyFocusGroupId = null
|
|
3469
3575
|
state.hierarchyFocusStack = []
|
|
3470
3576
|
state.hierarchyRevealFocusGroupId = null
|
package/package.json
CHANGED