Skip to content

Refine onboarding follow-up flow#6041

Merged
kodjima33 merged 1 commit into
mainfrom
worktree-fix-onboarding-part2
Mar 25, 2026
Merged

Refine onboarding follow-up flow#6041
kodjima33 merged 1 commit into
mainfrom
worktree-fix-onboarding-part2

Conversation

@kodjima33
Copy link
Copy Markdown
Collaborator

Summary

  • improve the second half of desktop onboarding by making the shortcut and voice follow-up steps less confusing
  • start onboarding enrichment earlier after file access and keep the graph styling aligned with the newer black/white onboarding work
  • start screen monitoring as soon as screen recording is granted so Rewind has content immediately after onboarding

Verification

  • swift build --package-path desktop/Desktop
  • rebuilt and launched /Applications/onboarding-part2.app
  • connected with agent-swift to confirm the app launched

Notes

  • checked the separate onboarding-fixes worktree before landing this
  • pulled in the safe overlap from that worktree (earlier background enrichment, private-relay search fix, and style harmonization) without taking the unrelated chat-only settings change

@kodjima33 kodjima33 merged commit 67a82b4 into main Mar 25, 2026
3 checks passed
@kodjima33 kodjima33 deleted the worktree-fix-onboarding-part2 branch March 25, 2026 21:46
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR refines the second half of the desktop onboarding flow: it splits the combined voice-shortcut+demo step into two focused steps, adds a global key monitor to the floating-bar shortcut step so the hotkey is caught from any focused app, starts screen monitoring as soon as screen-recording permission is granted, and harmonises all accent colours from purple to white/neutral. Three issues are worth addressing before merging:

  • Stale error causes premature ContinueresetFloatingBarConversation() in OnboardingVoiceDemoView resets barState properties but not chatProvider.errorMessage. If a non-nil error is left over from a previous step, the secondary poll condition (!isSending && observedShortcutPress && errorMessage != nil) can fire immediately after PTT finishes, showing Continue before Omi has responded.
  • PTT shortcut detection drops when focus shiftsOnboardingVoiceShortcutStepView.installKeyMonitor() registers only a local flagsChanged monitor, while the floating-bar step in this same PR was explicitly upgraded to add a global monitor for the same reason. If the user's focus moves to another app the key press is silently missed.
  • buildSuggestedGoals called before scan snapshot is readystartBackgroundInsightsIfNeeded() now runs in a detached Task parallel to the file scan. If Gmail or calendar data arrives before the scan populates scanSnapshot, goal suggestions are generated without any file context. The old sequential order guaranteed scanSnapshot was populated first.
  • Duplicated resetFloatingBarConversation() — the method is copy-pasted verbatim into both OnboardingVoiceDemoView and OnboardingVoiceShortcutStepView and should be extracted to a shared helper.

Confidence Score: 4/5

  • Safe to merge with one targeted fix: clear chatProvider.errorMessage on voice-demo appear to prevent premature Continue unlock.
  • The bulk of the PR is low-risk UI theming and well-contained UX flow changes. The global-monitor addition in OnboardingFloatingBarShortcutStepView is a clear improvement. The main runtime concern is the stale errorMessage path in OnboardingVoiceDemoView, which could make the voice demo appear to succeed when it hasn't — a degraded but not broken onboarding experience. The scan/insights race is an accepted trade-off per the PR description and degrades goal suggestions rather than crashing. The missing global monitor in OnboardingVoiceShortcutStepView is a minor UX issue (step stays waiting) rather than a blocker.
  • OnboardingVoiceDemoView.swift (stale error), OnboardingVoiceShortcutStepView.swift (local-only monitor), OnboardingPagedIntroCoordinator.swift (parallel insights / scan race).

Important Files Changed

Filename Overview
desktop/Desktop/Sources/OnboardingVoiceDemoView.swift Refactored voice demo step: sets .live PTT mode on appear and restores it on disappear, resets bar state, warms up bridge, and tightens polling to 20 s / 0.25 s; stale chatProvider.errorMessage can prematurely show Continue, and resetFloatingBarConversation() is duplicated.
desktop/Desktop/Sources/OnboardingVoiceShortcutStepView.swift Simplified to key-detection only (no PTT session): hides floating bar on appear, installs a flagsChanged local monitor to detect the shortcut and unlock Continue; only a local monitor is used, unlike the floating-bar step which added a global one, so key presses when focus is elsewhere are silently dropped.
desktop/Desktop/Sources/OnboardingPagedIntroCoordinator.swift Background insights now launch as a parallel Task before the file scan, which may cause buildSuggestedGoals to run with a nil scanSnapshot if Gmail/Calendar data arrives before the scan finishes; also adds privaterelay.appleid.com / privaterelay.icloud.com to the public-domain exclusion list (correct fix).
desktop/Desktop/Sources/OnboardingView.swift Extracts startMonitoringIfNeeded() to also set screenAnalysisEnabled = true, and calls it earlier (immediately after screen-recording permission is granted) so Rewind has content right away; overall logic is clean.
desktop/Desktop/Sources/OnboardingFloatingBarShortcutStepView.swift Adds a global key monitor alongside the local one to catch the shortcut from any focused app, removes the "Change shortcut" button to simplify the step, and switches active-state styling from purple to white.
desktop/Desktop/Sources/OnboardingFloatingBarDemoView.swift Reorders instructional text so the shortcut hint shows before bar is activated and the MacLineupPreview appears after, making the step less confusing; button colours updated to the new white theme.
desktop/Desktop/Sources/OnboardingFileScanStepView.swift Removes the OnboardingInsightCard that showed mapped projects/technologies during scanning; title copy updated; no logic concerns.
desktop/Desktop/Sources/OnboardingStepScaffold.swift Replaces all purple accent colours with white/neutral tones across shared scaffold, progress dots, cards, and chip components to align with the new black-and-white onboarding theme.

Sequence Diagram

sequenceDiagram
    participant User
    participant OV as OnboardingView
    participant FS as FileScanStep
    participant OPC as OnboardingPagedIntroCoordinator
    participant BIS as BackgroundInsights(Gmail/Cal)
    participant VSS as VoiceShortcutStep
    participant VD as VoiceDemoView
    participant FBS as FloatingBarShortcutStep
    participant FBD as FloatingBarDemoView

    OV->>FS: show (step ~8)
    User->>FS: grants file access
    FS->>OPC: startScan()
    OPC->>BIS: Task { startBackgroundInsightsIfNeeded() }
    Note over OPC,BIS: NEW: insights start in parallel with scan
    OPC->>OPC: executeTool("scan_files")
    BIS-->>OPC: buildSuggestedGoals(scanSnapshot?) [may be nil]
    OPC-->>FS: scanState = .complete

    OV->>FBS: show (floating bar shortcut step)
    FBS->>FBS: installLocalKeyMonitor + installGlobalKeyMonitor
    User->>FBS: presses shortcut (from any app)
    FBS-->>FBS: shortcutDetected = true → show Continue

    OV->>FBD: show (floating bar demo)
    User->>FBD: opens bar, types question, sees response

    OV->>VSS: show (PTT shortcut step)
    VSS->>VSS: hide floating bar, cleanup PTT, installLocalKeyMonitor
    User->>VSS: presses modifier key (must be focused)
    VSS-->>VSS: shortcutDetected = true → show Continue

    OV->>VD: show (voice demo step)
    VD->>VD: resetBarState, set .live PTT, warmupBridge()
    User->>VD: holds PTT key, speaks
    VD->>VD: poll 0.25s×80 for AI response
    VD-->>OV: showContinue → onComplete

    OV->>OV: screen recording granted → startMonitoringIfNeeded()
    Note over OV: NEW: monitoring starts earlier, not just at onboarding end
Loading

Reviews (1): Last reviewed commit: "Refine onboarding follow-up flow" | Re-trigger Greptile

Comment on lines +147 to 152
if !chatProvider.isSending,
observedShortcutPress,
(chatProvider.errorMessage != nil || barState.currentAIMessage != nil) {
showContinueNow()
return
}
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.

P2 Stale chatProvider.errorMessage may prematurely unlock Continue

resetFloatingBarConversation() resets barState properties but does not clear chatProvider.errorMessage. If the user arrives at this step with a non-nil errorMessage left over from a previous interaction, the second branch of the poll can fire during the very first iteration (right after the PTT key is released and isSending returns to false), showing the Continue button before Omi has had a chance to respond.

The safest fix is to reset the error when the view appears:

.onAppear {
    FloatingControlBarManager.shared.setup(appState: appState, chatProvider: chatProvider)
    resetFloatingBarConversation()
    chatProvider.errorMessage = nil   // ← clear any stale error
    ...
}

Alternatively, tighten the poll condition so it only fires as a fallback when barState.currentAIMessage transitions from nil to non-nil after the send has started, rather than on any pre-existing non-nil error.

Comment on lines +209 to +220
private func installKeyMonitor() {
keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { event in
guard !shortcutDetected else { return event }
guard matchesCurrentPTTShortcut(event) else { return event }

shortcutDetected = true
withAnimation(.easeInOut(duration: 0.3)) {
showContinue = true
}
return nil
}
}
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.

P2 Only a local key monitor is installed — key press missed if focus shifts

installKeyMonitor registers only a local flagsChanged monitor, which only fires when the onboarding window is the key event target. OnboardingFloatingBarShortcutStepView was explicitly updated in this same PR to add both a local and a global monitor precisely to handle the case where focus moves to another app before the shortcut is pressed. The same scenario applies here: if the user clicks into another window (common when they're trying the shortcut while a different app is visible) the key press will not be detected and the step will stay stuck until they click back into the onboarding window.

Consider adding a paired global monitor following the same pattern used in OnboardingFloatingBarShortcutStepView:

@State private var globalKeyMonitor: Any?

private func installKeyMonitor() {
    localKeyMonitor = NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { event in
        if handleShortcutEvent(event) { return nil }
        return event
    }
    globalKeyMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { event in
        _ = handleShortcutEvent(event)
    }
}

And remove both on onDisappear.

scanState = .scanning
scanStatusText = "Scanning your projects and apps..."

Task { await startBackgroundInsightsIfNeeded() }
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.

P2 buildSuggestedGoals may be called with a nil scanSnapshot

startBackgroundInsightsIfNeeded() is now fired as a detached Task before executeTool("scan_files") runs. Inside that function the Gmail and Calendar tasks both call buildSuggestedGoals(from: self.scanSnapshot, ...) when they complete. If either task finishes before the file scan populates scanSnapshot, the goals are built without any file-context data, producing less relevant suggestions.

Previously the call was placed after refreshSnapshotIfAvailable(), guaranteeing scanSnapshot was populated first. If the intent is specifically to parallelise the network requests, consider buffering the goal build: hold off calling buildSuggestedGoals until both the insight data and scanSnapshot are available, or at minimum call it again inside refreshSnapshotIfAvailable() once the snapshot is ready.

Comment on lines +164 to +173
private func resetFloatingBarConversation() {
guard let barState = FloatingControlBarManager.shared.barState else { return }
barState.showingAIConversation = false
barState.showingAIResponse = false
barState.aiInputText = ""
barState.currentAIMessage = nil
barState.chatHistory = []
barState.isVoiceFollowUp = false
barState.voiceFollowUpTranscript = ""
}
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.

P2 resetFloatingBarConversation() duplicated across two views

This method is defined identically in both OnboardingVoiceDemoView (lines 164-173) and OnboardingVoiceShortcutStepView (lines 119-128). Future changes to the reset logic need to be applied in two places, which is easy to miss. Consider extracting it to a shared extension on FloatingControlBarManager or a free function in an OnboardingHelpers.swift file.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Glucksberg pushed a commit to Glucksberg/omi-local that referenced this pull request Apr 28, 2026
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