@andespindola/brainlink 0.1.0-beta.100 → 0.1.0-beta.101

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 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, 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 clampScale = value => Math.max(zoomRange.min, Math.min(currentZoomMax(), value))
2216
+ const zoomFloorByNodeCount = (nodeCount) => {
2217
+ if (nodeCount > massiveGraphNodeThreshold) return 0.0016
2218
+ if (nodeCount > largeGraphNodeThreshold) return 0.001
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,18 @@ 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
+
2273
2297
  const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: true }) => {
2274
2298
  const rect = canvas.getBoundingClientRect()
2275
2299
  const width = Math.max(rect.width, 320)
@@ -2307,6 +2331,12 @@ const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: t
2307
2331
  : nodes.length > massiveGraphNodeThreshold
2308
2332
  ? clampScale(Math.min(baselineScale, massiveAutoFitMacroScale))
2309
2333
  : baselineScale
2334
+ const macroFloorScale = options.macro
2335
+ ? clampScale(macroFaceToFaceScale(nodes.length, state.hubNeighborDistance))
2336
+ : 0
2337
+ const resolvedScale = options.macro
2338
+ ? clampScale(Math.max(scale, macroFloorScale))
2339
+ : scale
2310
2340
  const hubCenter =
2311
2341
  options.preferHubCenter && isDominantHub(state.primaryHub, nodes.length) && nodes.some((node) => node.id === state.primaryHub.id)
2312
2342
  ? state.primaryHub
@@ -2315,9 +2345,9 @@ const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: t
2315
2345
  const centerY = hubCenter ? hubCenter.y : (bounds.minY + bounds.maxY) / 2
2316
2346
 
2317
2347
  state.transform = {
2318
- x: clampTransformCoordinate(width / 2 - centerX * scale),
2319
- y: clampTransformCoordinate(height / 2 - centerY * scale),
2320
- scale: clampScale(scale)
2348
+ x: clampTransformCoordinate(width / 2 - centerX * resolvedScale),
2349
+ y: clampTransformCoordinate(height / 2 - centerY * resolvedScale),
2350
+ scale: clampScale(resolvedScale)
2321
2351
  }
2322
2352
  state.offscreenFrameCount = 0
2323
2353
  state.recoveringViewport = false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.100",
3
+ "version": "0.1.0-beta.101",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",