tui: #143 first-run onboarding wizard #151
No reviewers
Labels
No labels
area:agents
area:ai
area:config
area:dashboard
area:design
area:design-review
area:devtools
area:entities
area:gallery
area:generate
area:image
area:infra
area:meta
area:model-browser
area:navigation
area:presets
area:security
area:sessions
area:settings
area:sharing
area:test
area:ux
area:webhook
area:workdir
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/loom!151
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "tui/onboarding-wizard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes charles/loom#143
Summary
OnboardingOverlay(Welcome / Backend / Plugins / Done) driven by a singleOverlayKind::Onboarding(OnboardingStep)variant. Shown on startup when~/.config/loom/settings.jsonis missing oronboarding_completedis false;Escskips with a notification pointing to the re-run entry.SetBackendBanneraction thatmain.rsfires from an asyncbridge.health_check()after bootstrap. Dismiss withb; jump to Settings withs.Actionssection with a "Re-run onboarding" entry that pushes the wizard.Test plan
cargo fmt --allcargo build(workspace)cargo test -p loom-tui— 161 lib tests + 18 integration tests pass, including:welcome_enter_advances_to_backenddefault_url_populated_on_emptyesc_on_any_step_dismissesplugins_space_toggles_selectiondone_enter_saves_and_popsis_first_run_when_settings_missingcompleting_onboarding_wizard_writes_valid_settings_file(integration)esc_dismisses_wizard_with_notification_hint(integration)~/.config/loom/settings.json, launchjust run-tui, verify wizard appears; complete it; verify settings.json is written withonboarding_completed: true.s/bwork.🤖 Generated with Claude Code
⚠ Recommending changes — one real UX bug.
Nice coverage: unit tests + integration test + a re-run entry in Settings.
Bug — health state is faked
OnboardingOverlay::handleon the Backend step:This sets
HealthState::Oksynchronously before the async probe returns. The render layer then shows "last check OK — press Enter again to continue" even when the backend is down. The notification eventually arrives, but the banner never reflects the actual result, and a user who taps Enter twice advances past a failed backend.Fix: keep
HealthState::Pendingafter kicking the probe, add anAppAction::OnboardingHealthResult(Ok/Bad)that the probe sends back, and only flip toOkon success. RequireOkto advance (or make a second Enter an explicit "skip check" with a clear label).Other
kick_health_checkwrites the URL intoAppSettingsviatry_writeon every Enter press, beforecommit(). If the probe fails and the user Escapes, the URL is already mutated in memory (not on disk). The wizard should be side-effect-free untilcommit.is_first_runreadsconfig_dir()which isLazyLock-memoised; the testis_first_run_when_settings_missingacknowledges this and only asserts path construction. Plumb in aconfig_dir: Fn()so behaviour can be tested.unsafe { std::env::set_var(...) }in tests is racy undercargo testparallelism. Gate with#[serial_test::serial]or move to an integration test.commit()overwritesbrowsing_pluginswith the full enabled set. Re-running the wizard erases a user's custom browsing subset. Consider only writingbrowsing_pluginson first run.scollides with the Generate preview's "save" shortcut. Current code routes banner keys early inhandle_event, so bannerswould intercept before Generate handles it. Pick a less conflicting key (gorCtrl+,).Thanks for the review — all flagged items addressed in
01e131c.Blocker (fake health state) — fixed
HealthStateis now driven purely by the async probe. AddedAppAction::OnboardingHealthResult(Result<(), String>), routed throughApp::drain_actionstoOnboardingOverlay::apply_health_result. Enter on Backend now:Pending), orPendingso tapping twice can't race past an unresolved result,Ok,Badand stays on Backend.Editing the URL invalidates any prior result (flips back to
Idle).Side-effect-free wizard — fixed
kick_health_checkno longer writes toAppSettings. The URL lives in overlay-local state (TextInput) untilcommit()runs. The probe now usesplain_http_probe(&url)directly instead of the bridge, so no settings mutation is required for it to see the right URL.Preserve
browsing_pluginson re-run — fixedcommit()only seedsbrowsing_pluginswhensettings.browsing_plugins.is_empty(). A user's custom subset is never clobbered by a re-run.Banner hotkey collision — fixed
sreplaced withCtrl+,.bstill dismisses. Banner hint string updated to"Ctrl+, settings (b) dismiss".Unsafe
set_varin tests — fixedRemoved the
unsafe { std::env::set_var(...) }calls from both theis_first_run_when_settings_missinganddone_enter_saves_and_popsunit tests. Thedone_enter_saves_and_popstest was replaced with targeted unit coverage that exercises the actual new behaviour without touching disk:backend_enter_does_not_advance_while_pendingbackend_advances_only_after_ok_resultbackend_bad_result_shows_error_and_blocks_advancekick_health_check_does_not_mutate_settingscommit_preserves_existing_browsing_pluginsThe integration test
completing_onboarding_wizard_dismisses_overlaydrives the wizard end-to-end and injects a syntheticOkresult via a newApp::dispatchhelper, so it no longer depends onHOMEoverride timing.cargo fmt --all,cargo build,cargo test -p loom-tui -p loom-core, andcargo clippy --workspace -- -D warnings(whatjust qaruns) are all green.01e131c26c761c580e06