feat: onboarding flow with permission-gated stage machine#65
Merged
quiet-node merged 8 commits intomainfrom Apr 8, 2026
Merged
feat: onboarding flow with permission-gated stage machine#65quiet-node merged 8 commits intomainfrom
quiet-node merged 8 commits intomainfrom
Conversation
Adds a full onboarding experience gated on macOS Accessibility and Screen Recording permissions, with a post-grant intro screen. Key design decisions: - Startup reads DB stage to detect Complete; then checks live permission APIs (AXIsProcessTrusted + CGWindowListCopyWindowInfo) to determine Permissions vs. Intro. This avoids dependency on DB write durability across process restarts and works around macOS 15 API unreliability. - quit_and_relaunch writes "intro" stage to DB and issues PRAGMA wal_checkpoint(FULL) before exit to survive std::process::exit. - PermissionsStep owns all runtime permission detection via mount-time checks and polling. Startup never gates on permission APIs beyond the initial Permissions-vs-Intro decision. - Onboarding window: 460x640px, centered, native shadow re-enabled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Startup now checks permissions unconditionally on every launch: - Both missing: reset stage to "permissions", show permissions screen - Both granted + stage not "complete": advance to "intro", show intro screen - Both granted + stage "complete": show Ask Bar directly - Permission revoked after completion: resets to "permissions" flow finish_onboarding moved from commands.rs to lib.rs so it can restore NSPanel to overlay mode and call show_overlay immediately after the user clicks "Get Started" — no relaunch needed. quit_and_relaunch simplified to just app_handle.restart(); the stage stays "permissions" across the restart and startup handles the transition. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Match card background, border, border-radius, padding, and box-shadow to PermissionsStep for a consistent onboarding UI. Rename title from "Good to know" to "Before you dive in". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…ith CGPreflightScreenCaptureAccess The screen recording permission check was returning true immediately after opening System Settings, before the user actually granted access. Root cause: CGWindowListCopyWindowInfo(0, 0) with option 0 always returns a non-null array because it includes the caller's own windows, making it useless as a TCC permission check. Fix: check_screen_recording_tcc_granted now delegates to is_screen_recording_granted (CGPreflightScreenCaptureAccess), a genuine TCC snapshot that correctly returns false when not granted. The startup check in notify_frontend_ready also switched to is_screen_recording_granted, reserving the faster path for launch. Removed the ObjC/ScreenCaptureKit compilation path (screen_recording_check.m) that was attempted as an alternative: SCShareableContent.getWithCompletionHandler does not resolve under the macOS 26 SDK used by this build environment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Owner
Author
Code reviewFound 3 issues:
Lines 378 to 413 in e634285
thuki/src-tauri/src/permissions.rs Lines 83 to 97 in e634285
Lines 424 to 460 in e634285 🤖 Generated with Claude Code If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…ermission APIs Three issues from code review on PR #65: 1. notify_frontend_ready now calls onboarding::compute_startup_stage instead of CGPreflightScreenCaptureAccess. Permission APIs return stale results immediately after a process restart on macOS 15+, which could trap users in an infinite permissions loop. The DB stage is the single source of truth at startup. 2. quit_and_relaunch now writes "intro" to the DB before restarting. compute_startup_stage reads this on the next launch and shows the intro screen without needing to call any permission API. The previous approach relied on the live permission check (unreliable post-restart) to advance the stage; the DB write eliminates that dependency. 3. finish_onboarding now delegates the DB write to onboarding::mark_complete, a testable pure function, making the Tauri command a thin wrapper as required by CLAUDE.md. Two new tests cover mark_complete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…etection The previous commit (trust DB exclusively) broke the case where a user revokes a permission after completing onboarding: the DB stage is "complete" so compute_startup_stage returns None, and the app goes straight to the Ask Bar with non-functional features. Fix: check live permissions for the "permissions" and "complete" stages as before, but skip the check specifically for the "intro" stage. "intro" is written by quit_and_relaunch immediately before restarting, which proves the user just granted all permissions. Skipping the live check there prevents CGPreflightScreenCaptureAccess from returning a stale false negative on macOS 15+ and looping the user back to the permissions screen. Stage-by-stage behaviour at startup: permissions -> check live permissions (standard first-launch path) intro -> trust DB, show intro (post-grant restart, macOS 15 safe) complete -> check live permissions (revocation detection) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…lback - Delete src/view/OnboardingView.tsx: it was a temporary re-export shim whose comment said "App.tsx will be updated in the next step". That update already shipped, leaving the file unreachable and unexercisable, causing 0% coverage on all metrics for the file. - Add App test: "dismisses onboarding and shows ask bar when onComplete is called" — exercises the inline arrow on line 1152 of App.tsx that calls setOnboardingStage(null) after the user clicks "Get Started". Restores 100% frontend coverage across lines, functions, branches, and statements. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…t_config error path Covers the two lines that blocked CI at 100% line coverage: - onboarding.rs: add set_and_get_stage_round_trips_permissions to exercise the OnboardingStage::Permissions arm of set_stage - database.rs: add set_config_returns_error_when_table_missing to hit the error branch of conn.execute() inside set_config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CGWindowListCopyWindowInfo(0, 0)always returns non-null; fix:CGPreflightScreenCaptureAccess)show_overlaydirectly)Flow
Test plan
bun run test && bun run validate-buildpass clean🤖 Generated with Claude Code