Skip to content

feat(desktop): onboarding file indexing & knowledge graph improvements#4900

Merged
kodjima33 merged 3 commits intomainfrom
feat/onboarding-file-indexing-improvements
Feb 19, 2026
Merged

feat(desktop): onboarding file indexing & knowledge graph improvements#4900
kodjima33 merged 3 commits intomainfrom
feat/onboarding-file-indexing-improvements

Conversation

@kodjima33
Copy link
Copy Markdown
Collaborator

Summary

  • Streamline file indexing onboarding: remove consent screen, start scanning immediately on appear with loading animation and progress bar
  • Prevent duplicate file indexing by setting flag early and adding pipeline guard
  • Improve 3D knowledge graph: text labels, connection-based node sizing, colored edges, smooth glow halos, auto-fit camera
  • Adaptive physics for graph layout based on graph size (small/medium/large)

Test plan

  • Fresh onboarding flow: verify step 4 shows loading animation immediately (no consent screen)
  • Verify skip button works during file scanning
  • Verify brain map appears after loading completes with 3D graph
  • Verify existing user sheet (DesktopHomeView) also shows the new flow
  • Verify no duplicate loading screens appear
  • Check knowledge graph page renders with text labels, sized nodes, and colored edges

🤖 Generated with Claude Code

kodjima33 and others added 3 commits February 19, 2026 17:48
Remove consent screen and start file scanning immediately on appear.
Add pipelineStarted guard to prevent duplicate pipeline runs.
Set hasCompletedFileIndexing flag early to prevent duplicate sheet
from DesktopHomeView. Add isBrainMapPhase binding for full-bleed
layout in OnboardingView. Update loading UI with clearer messaging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add connectionCount tracking to GraphNode3D. Change physics
parameters to var for runtime adaptation. Adjust repulsion,
attraction, centerGravity, and restLength based on graph size
(small/medium/large) for better visual layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add text labels, connection-based node sizing, colored edges,
smooth glow halos (48 segments), and auto-fit camera. Nodes
scale by connection count for visual hierarchy. Edge colors
blend source and target node colors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kodjima33 kodjima33 merged commit 87baf54 into main Feb 19, 2026
1 check passed
@kodjima33 kodjima33 deleted the feat/onboarding-file-indexing-improvements branch February 19, 2026 22:48
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant improvements to the onboarding flow and the knowledge graph visualization. The onboarding process is now more streamlined, and a potential race condition that could cause duplicate file indexing has been fixed. The 3D knowledge graph has been greatly enhanced with new visual features like text labels, dynamic node sizing, colored edges, and an auto-fitting camera, which greatly improves its usability and aesthetics.

My review focuses on potential performance and correctness issues in the new graph rendering logic. I've identified a performance regression where geometries are no longer shared, and a minor issue in the camera auto-fitting logic that could lead to visual clipping. Addressing these will ensure the new features are both robust and performant.

Comment on lines 274 to +318
for node in simulation.nodes {
let scnNode: SCNNode
let radius = nodeRadius(for: node)
let containerNode = SCNNode()
containerNode.position = SCNVector3(node.position)
containerNode.name = node.id

// Core sphere
let sphere = SCNSphere(radius: radius)
sphere.segmentCount = node.isFixed ? 24 : 16
let mat = SCNMaterial()
if node.isFixed {
// User node — larger and white
let userSphere = SCNSphere(radius: 30)
userSphere.segmentCount = 16
let mat = SCNMaterial()
mat.diffuse.contents = NSColor.white
mat.emission.contents = NSColor.white.withAlphaComponent(0.6)
mat.lightingModel = .constant
userSphere.materials = [mat]
scnNode = SCNNode(geometry: userSphere)
mat.emission.contents = NSColor.white.withAlphaComponent(0.8)
} else {
let sphere = getSharedSphere(for: node.nodeType)
scnNode = SCNNode(geometry: sphere)
mat.diffuse.contents = node.nodeType.nsColor
mat.emission.contents = node.nodeType.nsColor.withAlphaComponent(0.5)
}
scnNode.position = SCNVector3(node.position)
scnNode.name = node.id
mat.lightingModel = .constant
sphere.materials = [mat]
let sphereNode = SCNNode(geometry: sphere)
containerNode.addChildNode(sphereNode)

// Glow halo (larger semi-transparent sphere around node)
let glowRadius = radius * 2.5
let glowSphere = SCNSphere(radius: glowRadius)
glowSphere.segmentCount = 48
let glowMat = SCNMaterial()
let glowColor = node.isFixed ? NSColor.white : node.nodeType.nsColor
glowMat.diffuse.contents = glowColor.withAlphaComponent(0.03)
glowMat.emission.contents = glowColor.withAlphaComponent(0.025)
glowMat.lightingModel = .constant
glowMat.isDoubleSided = true
glowMat.blendMode = .add
glowSphere.materials = [glowMat]
let glowNode = SCNNode(geometry: glowSphere)
containerNode.addChildNode(glowNode)

// Text label (billboard — always faces camera)
let labelNode = createLabelNode(text: node.label, nodeRadius: radius, isFixed: node.isFixed)
labelNode.constraints = [billboardConstraint]
containerNode.addChildNode(labelNode)

scene.rootNode.addChildNode(containerNode)
nodeSceneNodes[node.id] = containerNode
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The new implementation for creating scene nodes in createSceneNodes creates a new SCNSphere geometry and SCNMaterial for each node's core and glow halo. This is a performance regression from the previous implementation which used shared geometries to reduce memory usage and improve rendering speed. For large graphs, creating unique geometries and materials for every node can lead to significant performance degradation and higher memory consumption.

To optimize this, you can reintroduce geometry and material sharing.
1.  **Share Geometries:** Create shared unit `SCNSphere` geometries (radius 1.0) - one for regular nodes and one for fixed nodes (due to different `segmentCount`). For each node in the scene, create an `SCNNode` with the appropriate shared geometry and then set its `scale` property to match the desired radius (e.g., `sphereNode.scale = SCNVector3(radius, radius, radius)`). This applies to both the core sphere and the glow halo.
2.  **Share Materials:** Cache and reuse `SCNMaterial` instances instead of creating a new one for each node. You can use a dictionary `[NSColor: SCNMaterial]` to store materials based on their diffuse color.

Here's a conceptual example for sharing the sphere geometry:
```swift
// At the class level
private let sharedSphere = SCNSphere(radius: 1.0)
private let sharedFixedSphere = SCNSphere(radius: 1.0) // with different segmentCount if needed

// Inside createSceneNodes() for each node
let radius = nodeRadius(for: node)
let sphereNode = SCNNode(geometry: node.isFixed ? sharedFixedSphere : sharedSphere)
sphereNode.scale = SCNVector3(radius, radius, radius)
// ... assign a shared/cached material to sphereNode.geometry
containerNode.addChildNode(sphereNode)

Comment on lines +371 to 387
private func autoFitCamera() {
guard !simulation.nodes.isEmpty else { return }

var maxDist: Float = 0
for node in simulation.nodes {
let dist = simd_length(node.position)
if dist > maxDist { maxDist = dist }
}

// Camera needs to be far enough to see the outermost node
// Account for field of view (60°) — distance = maxDist / tan(fov/2) + padding
let fovRadians: Float = 60.0 * Float.pi / 180.0
let minDistance = maxDist / tan(fovRadians / 2) * 1.3 // 30% padding
let cameraZ = max(minDistance, 1200) // minimum distance for very small graphs

cameraNode.position = SCNVector3(0, 0, cameraZ)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The autoFitCamera logic calculates the required camera distance based on the maximum distance of a node's center from the origin. However, it doesn't account for the radius of the nodes themselves. A large node near the edge of the view frustum might be partially clipped because its outer edge extends beyond the calculated maxDist. The 30% padding is a good attempt to mitigate this, but a more precise calculation would be more robust.

private func autoFitCamera() {
    guard !simulation.nodes.isEmpty else { return }

    var maxDistPlusRadius: Float = 0
    for node in simulation.nodes {
        let dist = simd_length(node.position)
        let radius = Float(nodeRadius(for: node))
        if dist + radius > maxDistPlusRadius {
            maxDistPlusRadius = dist + radius
        }
    }

    // Camera needs to be far enough to see the outermost node's edge
    // Account for field of view (60°) — distance = maxDist / tan(fov/2) + padding
    let fovRadians: Float = 60.0 * Float.pi / 180.0
    // A smaller padding is sufficient with the more accurate calculation
    let minDistance = maxDistPlusRadius / tan(fovRadians / 2) * 1.1
    let cameraZ = max(minDistance, 1200) // minimum distance for very small graphs

    cameraNode.position = SCNVector3(0, 0, cameraZ)
}

ellaaicare pushed a commit to ellaaicare/omi that referenced this pull request Apr 12, 2026
BasedHardware#4900)

## Summary
- Streamline file indexing onboarding: remove consent screen, start
scanning immediately on appear with loading animation and progress bar
- Prevent duplicate file indexing by setting flag early and adding
pipeline guard
- Improve 3D knowledge graph: text labels, connection-based node sizing,
colored edges, smooth glow halos, auto-fit camera
- Adaptive physics for graph layout based on graph size
(small/medium/large)

## Test plan
- [ ] Fresh onboarding flow: verify step 4 shows loading animation
immediately (no consent screen)
- [ ] Verify skip button works during file scanning
- [ ] Verify brain map appears after loading completes with 3D graph
- [ ] Verify existing user sheet (DesktopHomeView) also shows the new
flow
- [ ] Verify no duplicate loading screens appear
- [ ] Check knowledge graph page renders with text labels, sized nodes,
and colored edges

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Glucksberg pushed a commit to Glucksberg/omi-local that referenced this pull request Apr 28, 2026
BasedHardware#4900)

## Summary
- Streamline file indexing onboarding: remove consent screen, start
scanning immediately on appear with loading animation and progress bar
- Prevent duplicate file indexing by setting flag early and adding
pipeline guard
- Improve 3D knowledge graph: text labels, connection-based node sizing,
colored edges, smooth glow halos, auto-fit camera
- Adaptive physics for graph layout based on graph size
(small/medium/large)

## Test plan
- [ ] Fresh onboarding flow: verify step 4 shows loading animation
immediately (no consent screen)
- [ ] Verify skip button works during file scanning
- [ ] Verify brain map appears after loading completes with 3D graph
- [ ] Verify existing user sheet (DesktopHomeView) also shows the new
flow
- [ ] Verify no duplicate loading screens appear
- [ ] Check knowledge graph page renders with text labels, sized nodes,
and colored edges

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant