@andespindola/brainlink 0.1.0-beta.100 → 0.1.0-beta.102
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 -0
- package/dist/application/frontend/client-js.js +75 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -595,6 +595,7 @@ The graph UI shows:
|
|
|
595
595
|
- realtime refresh while `--watch` is enabled
|
|
596
596
|
- graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
|
|
597
597
|
- wheel zoom (including `cmd+scroll` and `ctrl+scroll`) anchored to cursor position for faster navigation in large graphs
|
|
598
|
+
- zoom-out floor for large and massive graphs, plus reset macro floor tied to hub-neighbor distance and first-level cluster spacing, so initial macro view stays closer to first particle layers instead of over-distancing
|
|
598
599
|
- keyboard shortcuts: `+` zoom in, `-` zoom out, `0` reset fit
|
|
599
600
|
- double-click on canvas zooms in at cursor position
|
|
600
601
|
- floating graph totals (notes, links, tags) below the Brainlink title
|
|
@@ -2213,7 +2213,19 @@ const currentZoomMax = () => {
|
|
|
2213
2213
|
return Math.max(zoomRange.min * 2, capped)
|
|
2214
2214
|
}
|
|
2215
2215
|
|
|
2216
|
-
const
|
|
2216
|
+
const zoomFloorByNodeCount = (nodeCount) => {
|
|
2217
|
+
if (nodeCount > massiveGraphNodeThreshold) return 0.0028
|
|
2218
|
+
if (nodeCount > largeGraphNodeThreshold) return 0.0014
|
|
2219
|
+
if (nodeCount > ecosystemActivationNodeThreshold) return 0.0006
|
|
2220
|
+
return zoomRange.min
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
const currentZoomMin = () => {
|
|
2224
|
+
const nodeCount = state.visibleNodes.length > 0 ? state.visibleNodes.length : state.nodes.length
|
|
2225
|
+
return Math.max(zoomRange.min, zoomFloorByNodeCount(nodeCount))
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
const clampScale = value => Math.max(currentZoomMin(), Math.min(currentZoomMax(), value))
|
|
2217
2229
|
const isFiniteNumber = value => Number.isFinite(value)
|
|
2218
2230
|
const isReasonableCoordinate = value => isFiniteNumber(value) && Math.abs(value) <= worldCoordinateLimit
|
|
2219
2231
|
const clampTransformCoordinate = value => {
|
|
@@ -2270,6 +2282,56 @@ const autoFitScaleRangeByNodeCount = nodeCount => {
|
|
|
2270
2282
|
return { min: 0.0012, max: 0.24 }
|
|
2271
2283
|
}
|
|
2272
2284
|
|
|
2285
|
+
const macroFaceToFaceScale = (nodeCount, hubDistance) => {
|
|
2286
|
+
if (!Number.isFinite(hubDistance) || hubDistance <= 0 || nodeCount <= ecosystemActivationNodeThreshold) {
|
|
2287
|
+
return 0
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
const rect = canvas.getBoundingClientRect()
|
|
2291
|
+
const viewportReference = Math.max(320, Math.min(rect.width, rect.height))
|
|
2292
|
+
const share = nodeCount > massiveGraphNodeThreshold ? 0.052 : 0.046
|
|
2293
|
+
const targetPx = Math.max(18, viewportReference * share)
|
|
2294
|
+
return targetPx / hubDistance
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
const nearestClusterNeighborDistance = (clusters) => {
|
|
2298
|
+
if (!Array.isArray(clusters) || clusters.length < 2) {
|
|
2299
|
+
return Number.POSITIVE_INFINITY
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
let nearestDistance = Number.POSITIVE_INFINITY
|
|
2303
|
+
for (let index = 0; index < clusters.length; index += 1) {
|
|
2304
|
+
const source = clusters[index]
|
|
2305
|
+
for (let neighborIndex = index + 1; neighborIndex < clusters.length; neighborIndex += 1) {
|
|
2306
|
+
const target = clusters[neighborIndex]
|
|
2307
|
+
const distance = Math.hypot(source.x - target.x, source.y - target.y)
|
|
2308
|
+
if (distance > 0 && distance < nearestDistance) {
|
|
2309
|
+
nearestDistance = distance
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
return nearestDistance
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
const macroEcosystemFaceScale = (nodeCount) => {
|
|
2318
|
+
if (nodeCount <= ecosystemActivationNodeThreshold) {
|
|
2319
|
+
return 0
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
const baseClusters = state.ecosystemClustersBySize.get(state.ecosystemBaseSize) ?? state.ecosystemClusters
|
|
2323
|
+
const siblingClusters = baseClusters.filter(cluster => !cluster.isHub)
|
|
2324
|
+
const nearestDistance = nearestClusterNeighborDistance(siblingClusters)
|
|
2325
|
+
if (!Number.isFinite(nearestDistance) || nearestDistance <= 0) {
|
|
2326
|
+
return 0
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
const rect = canvas.getBoundingClientRect()
|
|
2330
|
+
const viewportReference = Math.max(320, Math.min(rect.width, rect.height))
|
|
2331
|
+
const targetShare = nodeCount > massiveGraphNodeThreshold ? 0.068 : 0.058
|
|
2332
|
+
const targetPx = Math.max(22, viewportReference * targetShare)
|
|
2333
|
+
return targetPx / nearestDistance
|
|
2334
|
+
}
|
|
2273
2335
|
const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: true }) => {
|
|
2274
2336
|
const rect = canvas.getBoundingClientRect()
|
|
2275
2337
|
const width = Math.max(rect.width, 320)
|
|
@@ -2307,6 +2369,15 @@ const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: t
|
|
|
2307
2369
|
: nodes.length > massiveGraphNodeThreshold
|
|
2308
2370
|
? clampScale(Math.min(baselineScale, massiveAutoFitMacroScale))
|
|
2309
2371
|
: baselineScale
|
|
2372
|
+
const macroFloorScale = options.macro
|
|
2373
|
+
? clampScale(Math.max(
|
|
2374
|
+
macroFaceToFaceScale(nodes.length, state.hubNeighborDistance),
|
|
2375
|
+
macroEcosystemFaceScale(nodes.length)
|
|
2376
|
+
))
|
|
2377
|
+
: 0
|
|
2378
|
+
const resolvedScale = options.macro
|
|
2379
|
+
? clampScale(Math.max(scale, macroFloorScale))
|
|
2380
|
+
: scale
|
|
2310
2381
|
const hubCenter =
|
|
2311
2382
|
options.preferHubCenter && isDominantHub(state.primaryHub, nodes.length) && nodes.some((node) => node.id === state.primaryHub.id)
|
|
2312
2383
|
? state.primaryHub
|
|
@@ -2315,9 +2386,9 @@ const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: t
|
|
|
2315
2386
|
const centerY = hubCenter ? hubCenter.y : (bounds.minY + bounds.maxY) / 2
|
|
2316
2387
|
|
|
2317
2388
|
state.transform = {
|
|
2318
|
-
x: clampTransformCoordinate(width / 2 - centerX *
|
|
2319
|
-
y: clampTransformCoordinate(height / 2 - centerY *
|
|
2320
|
-
scale: clampScale(
|
|
2389
|
+
x: clampTransformCoordinate(width / 2 - centerX * resolvedScale),
|
|
2390
|
+
y: clampTransformCoordinate(height / 2 - centerY * resolvedScale),
|
|
2391
|
+
scale: clampScale(resolvedScale)
|
|
2321
2392
|
}
|
|
2322
2393
|
state.offscreenFrameCount = 0
|
|
2323
2394
|
state.recoveringViewport = false
|
package/package.json
CHANGED