|
| 1 | +# Onboarding revamp — context bundle |
| 2 | + |
| 3 | +Frozen record of every decision David made before the plan was written. The plan |
| 4 | +lives in `onboarding-revamp-plan.md`; this file is the "why" the plan refers |
| 5 | +back to. Don't edit this once the plan is approved — re-open the discussion in |
| 6 | +the plan instead. |
| 7 | + |
| 8 | +## The change in one sentence |
| 9 | + |
| 10 | +Replace the single Full Disk Access (FDA) modal with a multi-step, soft-sheet |
| 11 | +onboarding wizard that covers ~90% of the viewport over the main UI, with two |
| 12 | +required steps (FDA decision, AI provider) and one optional step (Networking / |
| 13 | +Indexing / Updates / MTP), plus the ability to re-open from the menu and |
| 14 | +command palette. |
| 15 | + |
| 16 | +## David's draft copy (verbatim — do not paraphrase in implementation) |
| 17 | + |
| 18 | +### Step 1 — Full Disk Access |
| 19 | + |
| 20 | +``` |
| 21 | +Welcome to Cmdr! {about 20% larger than body font, NOT a giant hero} |
| 22 | +
|
| 23 | +**You probably just want to start using the app.** Sorry to bother you with this |
| 24 | +first, but it's needed. |
| 25 | +
|
| 26 | +You see, Cmdr is a file manager, and it needs to access your disk to see all |
| 27 | +your files. macOS doesn't automatically grant permission to this. |
| 28 | +
|
| 29 | +Would you like to give this app full disk access? Here's what that means: |
| 30 | +{revoked-state copy from FullDiskAccessPrompt.svelte if applicable} |
| 31 | +
|
| 32 | +- **Pro:** The app will access your entire disk without nagging you for |
| 33 | + permissions to each folder like Downloads, Documents, and Desktop. |
| 34 | +- **Con:** Full disk access is pretty powerful. It lets the app read any file |
| 35 | + on your Mac. Only grant this if you trust Cmdr. Cmdr uses this right |
| 36 | + respectfully, and is [source-available](https://github.com/vdavid/cmdr) if |
| 37 | + you feel unsure. |
| 38 | +
|
| 39 | +If you decide to allow: {from here, same content as FullDiskAccessPrompt.svelte} |
| 40 | +``` |
| 41 | + |
| 42 | +### Step 2 — AI |
| 43 | + |
| 44 | +Three branches depending on FDA outcome: |
| 45 | + |
| 46 | +- **FDA granted (detected via `checkFullDiskAccess()` on step transition):** |
| 47 | + `Thanks for granting Full Disk Access! Now, the app can access your disk. Great!` |
| 48 | +- **FDA denied:** |
| 49 | + `You chose not to enable Full Disk Access. We respect that. You'll then shortly |
| 50 | + get a few permission requests from macOS for Cmdr to access your Desktop, |
| 51 | + Downloads, and similar folders. Accept/reject these at will. You can change |
| 52 | + all of this later in your System Settings.` |
| 53 | +- **User clicked "Allow" but FDA still not granted (e.g. didn't toggle in |
| 54 | + Settings, or didn't restart):** |
| 55 | + `You said you wanted to enable Full Disk Access, but Cmdr doesn't seem to |
| 56 | + have gotten it. You might need to restart the app (do it now! We'll continue |
| 57 | + from here!) or go to your System Settings > Privacy & Security > [Full Disk |
| 58 | + Access](deep-link via openPrivacySettings) and find Cmdr or manually add it |
| 59 | + with the little "+" button at the bottom.` |
| 60 | + |
| 61 | +Then: |
| 62 | + |
| 63 | +``` |
| 64 | +Now, the last necessary step: AI stuff |
| 65 | +
|
| 66 | +Cmdr has a bunch of AI features that you _may_ want and may not want. AI is a |
| 67 | +controversial topic these days. |
| 68 | +
|
| 69 | +Here is how you do common actions with and without AI: |
| 70 | +
|
| 71 | +| Feature | With AI | Without AI | |
| 72 | +| ----------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | |
| 73 | +| Search | You say "my recent fish-related presentations", agent sets your filters | You type something like "*fish*.ppt", and select the "after 1st of this month" filter | |
| 74 | +| Mass-rename | You say "add ISO date prefix", agent sets your rename pattern, you review and apply at will | You use the batch rename UI to manually set the rename pattern, review and apply | |
| 75 | +| Select | You say "select all image files", agent suggests selection, you review and apply at will | You press `⌘+` and type something like "*.jpg,*.png,*.gif,*.heic,*.webp,*.jpeg", review and apply | |
| 76 | +
|
| 77 | +Based on this, do you want AI or not? |
| 78 | +- [ ] Yes, I want AI (recommended), and my AI provider is: {selected} |
| 79 | + - LEFT column: scrollable selector list with all 15 cloud providers from |
| 80 | + `cloud-providers.ts` (OpenAI, Anthropic, Google Gemini, Groq, Together AI, |
| 81 | + Fireworks, Mistral, OpenRouter, DeepSeek, xAI, Perplexity, Azure OpenAI, |
| 82 | + Ollama, LM Studio, Custom). All 15 present; smaller number visible at once; |
| 83 | + scrollable. Keyboard: arrow keys + type-to-jump. |
| 84 | + - RIGHT column: setup instructions for the selected provider, with the API |
| 85 | + key input, auto-tester, and model selector embedded as numbered tutorial |
| 86 | + steps. Steps get a checkmark as they're completed (e.g., "Step 3: Paste |
| 87 | + your API key here" gets a ✓ when the auto-check returns connected, then |
| 88 | + "Step 4: Pick a model" gets ✓ when one is selected). Live, no manual |
| 89 | + check button needed. |
| 90 | +- [ ] Yes, I want AI, but I want to be super private, and I don't mind a bit |
| 91 | + dumber model that takes up about 2 GB of space and a bit of CPU at every use. |
| 92 | + (Still an okay solution. No data leaves your machine. Cmdr tries to deliver |
| 93 | + updates for the best small local model available.) |
| 94 | +- [ ] Thanks but no thanks, no AI for me |
| 95 | +
|
| 96 | +Buttons: [Start using Cmdr!] [One more optional setup step] |
| 97 | + {The second button has the accent color, the first is bare/secondary. This is |
| 98 | + intentional: we want to nudge users toward step 3 without forcing them.} |
| 99 | +``` |
| 100 | + |
| 101 | +### Step 3 (optional) |
| 102 | + |
| 103 | +``` |
| 104 | +(Optional) Step 3 |
| 105 | +
|
| 106 | +Nice! You're _almost_ ready to use Cmdr, and you chose to do a detailed setup. |
| 107 | +So, here you go, a few easy choices — if you don't care too much, just click |
| 108 | +the button below, all of these are just options, and the defaults are for your |
| 109 | +benefit: |
| 110 | +
|
| 111 | +1. Do you want **Networking** _on_ or _off_? |
| 112 | + Having it _on_ means you can connect to SMB servers like company network |
| 113 | + shares, a home NAS, and the such. The only cost is a macOS permission |
| 114 | + dialog that pops up and asks you to allow "Local network access", and one |
| 115 | + for "Accepting incoming connections". Both dialogs are harmless, but if you |
| 116 | + don't know what these are, they might be scary or annoying. |
| 117 | + - [x] Networking on (recommended) |
| 118 | + - [ ] Networking off (can enable it in Settings later) |
| 119 | +
|
| 120 | +2. Do you want **Drive indexing**? |
| 121 | + Drive indexing is totally cool! Gives you two main things: 1. Instant |
| 122 | + search of your whole drive. Think Spotlight, but even faster. 2. Real-time |
| 123 | + folder sizes for your whole drive. You always know how much stuff you have |
| 124 | + in each folder. If you turn this off, you only get `<DIR>` for the sizes. |
| 125 | + The cost is a 300 MB index on your drive, but no extra CPU or memory use |
| 126 | + after the first 2–3 minutes of you first starting the app, or starting it |
| 127 | + after a long time. It's a cheap feature considering the benefits. |
| 128 | + - [x] Drive indexing on (recommended) |
| 129 | + - [ ] Drive indexing off (can turn it on later in Settings) |
| 130 | +
|
| 131 | +3. **Automatic updates**? |
| 132 | + If you enable this, Cmdr makes a tiny network request to a central license |
| 133 | + server at each app start plus once every 24 hours, and you always get the |
| 134 | + latest updates. If disabled, you'll keep your current version, and zero |
| 135 | + automated network requests (except for periodic license checks _if_ you |
| 136 | + have a Commercial license). |
| 137 | + - [x] Updates on (recommended) |
| 138 | + - [ ] Updates off |
| 139 | +
|
| 140 | +4. MTP? |
| 141 | + If you enable this, Cmdr can **connect to Android phones, Kindles, cameras**, |
| 142 | + some music players, and any other device that supports the protocols called |
| 143 | + MTP or PTP. The cost is that macOS _also_ wants to connect to these (and |
| 144 | + it usually fails — that's why you can't just use Finder to copy photos from |
| 145 | + Android phones), so Cmdr has to suppress that macOS process while it's |
| 146 | + running. When you quit Cmdr, this is politely restored. But it's a bit of |
| 147 | + a cost, so: |
| 148 | + - [x] MTP support on (recommended) |
| 149 | + - [ ] MTP support off |
| 150 | +
|
| 151 | +Button: [Start using Cmdr] |
| 152 | +``` |
| 153 | + |
| 154 | +## Resolved questions (with David's exact answers) |
| 155 | + |
| 156 | +### Pre-questions round 1 |
| 157 | + |
| 158 | +1. **AI default**: change from `'local'` to `'off'`. The existing post-FDA AI |
| 159 | + offer toast goes away — the wizard becomes the only path to enable AI on |
| 160 | + first launch. |
| 161 | +2. **Cloud providers in step 2**: show all 15 from `cloud-providers.ts`, |
| 162 | + scrollable list. Not a curated subset. |
| 163 | +3. **"Bring a Claude Code / ChatGPT subscription"**: out of scope for now. |
| 164 | + API key only. |
| 165 | +4. **FDA "allow but didn't grant" detection**: one-shot `checkFullDiskAccess()` |
| 166 | + call on step 2 entry, choose copy branch from the result. No polling. |
| 167 | +5. **Step 3 settings**: |
| 168 | + - Networking on/off: set `network.enabled`. Don't proactively trigger the |
| 169 | + macOS Local Network prompt (it fires on first SMB action via the existing |
| 170 | + `network.firstTriggerDone` flow). |
| 171 | + - Drive indexing on/off: set `indexing.enabled`. Settings-applier already |
| 172 | + starts/stops the runtime. Cache cleanup behaviour is out of scope here. |
| 173 | + - Auto updates: set `updates.autoCheck`. |
| 174 | + - MTP: set `fileOperations.mtpEnabled`. |
| 175 | + Step 3 is mostly about giving users a chance to turn things OFF with full |
| 176 | + context. Defaults stay on. |
| 177 | +6. **Soft dialog component**: new component `OnboardingWizard.svelte` (not a |
| 178 | + variant of `ModalDialog`). Own backdrop blur, no drag, no Escape, no × |
| 179 | + button, full-bleed rounded panel sized to ~90% of viewport. Adds |
| 180 | + `'onboarding'` to `SOFT_DIALOG_REGISTRY`. |
| 181 | +7. **Existing users on upgrade**: silent skip of the wizard (they have |
| 182 | + `isOnboarded: true`) PLUS a one-time `info` toast nudging them that the |
| 183 | + `Cmdr > Onboarding…` menu item now exists, so they can review the new |
| 184 | + options if curious. Toast fires once and never again (gated by a new hidden |
| 185 | + setting, e.g. `onboarding.upgradeNudgeShown`). |
| 186 | +8. **App behind wizard**: render the full app behind the wizard backdrop |
| 187 | + normally (no "white screen until wizard done"). First-launch lands on `~`, |
| 188 | + so what peeks through the edges is friendly. |
| 189 | +9. **Keyboard contract**: Tab cycles within step; Enter on primary advances; |
| 190 | + Escape disabled (no accidental dismiss); provider list supports arrows + |
| 191 | + type-to-jump. |
| 192 | +10. **Mount point**: `routes/(main)/+page.svelte`. Replace `showFdaPrompt` with |
| 193 | + `showOnboarding`, replace `handleFdaComplete` with the wizard's overall |
| 194 | + `onComplete`. |
| 195 | + |
| 196 | +### Pre-questions round 2 |
| 197 | + |
| 198 | +1. **Re-invocation from menu / palette**: always start at step 1. If FDA is |
| 199 | + already granted, step 1 copy reflects that ("Cmdr currently has Full Disk |
| 200 | + Access. You can revoke any time in System Settings.") with a single "Next" |
| 201 | + button. |
| 202 | +2. **Back button + FDA**: leave the FDA buttons live on step 1. User can change |
| 203 | + their mind any time. "Allow" still requires the real macOS grant + restart; |
| 204 | + the wizard cannot fake it. |
| 205 | +3. **Step 3 indexing cache cleanup**: out of scope. Step 3 only flips |
| 206 | + `indexing.enabled`; existing settings-applier handles the runtime stop. |
| 207 | +4. **Subscription auth placeholder**: leave out entirely. No "Coming soon" |
| 208 | + affordance. |
| 209 | + |
| 210 | +### Pre-questions round 3 |
| 211 | + |
| 212 | +1. **Local-model download timing**: kick off download in the **background as |
| 213 | + soon as the user picks the private/local option in step 2**, with no |
| 214 | + in-wizard progress UI. If they switch away, cancel; if they switch back, |
| 215 | + re-`startAiDownload()` and let the existing HTTP-Range resume pick up. The |
| 216 | + existing toast can show in the corner if it does — fine either way; do NOT |
| 217 | + add suppression logic for it. |
| 218 | +2. **Mid-flow crash recovery**: each step persists its decision on advance. |
| 219 | + `isOnboarded` only flips on full completion. Next launch starts at the |
| 220 | + first not-yet-decided step. After step 1 + restart, the user lands directly |
| 221 | + on step 2 (which is the dominant "Allow + restart" flow). |
| 222 | +3. **Linux**: skip step 1 entirely. Step 2 leads with `Welcome to Cmdr!` and |
| 223 | + no FDA-related copy. |
| 224 | +4. **Step indicator**: subtle dot row at the top, with the optional step's |
| 225 | + dot styled distinctly (open / muted) so users see "2 mandatory + 1 |
| 226 | + optional", not endless. |
| 227 | +5. **Back button**: `←` button bottom-left with tooltip `Back`. Always lets the |
| 228 | + user return to a previous step. |
| 229 | +6. **Re-entry points**: add `Cmdr > Onboarding…` menu item (place under "Check |
| 230 | + for updates" in the app menu) and add a command-palette command for the |
| 231 | + same. Both routes call the same trigger. |
| 232 | + |
| 233 | +### Pre-questions round 4 |
| 234 | + |
| 235 | +1. **Menu re-invocation lands on step 1.** |
| 236 | +2. **Back from step 2 leaves FDA buttons live** (the test for FDA-already-granted |
| 237 | + collapses step 1 to a single-Next variant, see round 2 #1). |
| 238 | +3. **Step 3 indexing-cache cleanup out of scope** (confirmed twice). |
| 239 | +4. **Subscription placeholder excluded** (confirmed twice). |
| 240 | +5. **Design shift sanctioned**: lift cues from the recently redesigned Settings |
| 241 | + (more rounded, macOS-sheet vibe, frosted backdrop). Any new tokens or |
| 242 | + patterns introduced for the wizard go into `docs/design-system.md`. Don't |
| 243 | + leave them stranded in the wizard's scoped styles. |
| 244 | + |
| 245 | +## Things to preserve from `FullDiskAccessPrompt.svelte` |
| 246 | + |
| 247 | +- The TCC re-probe before `openPrivacySettings()` (without it Cmdr doesn't |
| 248 | + appear in the FDA list — critical). |
| 249 | +- Ventura vs older copy switch via `getMacosMajorVersion()`. |
| 250 | +- The "Tip: click '+' button at the bottom" fallback for the macOS 26 Tahoe |
| 251 | + regression. Lives in step 1's instructions and the "didn't get FDA" branch |
| 252 | + copy on step 2. |
| 253 | +- `systemStrings.systemSettings` for the localized System Settings pane name. |
| 254 | +- `startIndexingAfterFdaDecision()` on Deny. |
| 255 | +- The "Cmdr is source-available" GitHub link in the Con bullet. |
| 256 | + |
| 257 | +## AI toast machinery cleanup (scope) |
| 258 | + |
| 259 | +The post-FDA "Download AI?" offer toast must go away: |
| 260 | + |
| 261 | +- Delete the `'offer'` state in `AiToastContent.svelte` (and its switch case). |
| 262 | +- Delete the `pendingOffer` field and `notifyAiOnboardingComplete()` from |
| 263 | + `ai-state.svelte.ts`. |
| 264 | +- Delete the `onboarded` gate that suppresses Offer at startup (the wizard now |
| 265 | + owns first-launch AI consent). |
| 266 | +- Stop calling `notifyAiOnboardingComplete()` from `routes/(main)/+page.svelte`. |
| 267 | +- Keep the runtime toast states (`downloading`, `installing`, `ready`, |
| 268 | + `starting`) — they're useful while the local model downloads after a wizard |
| 269 | + pick. |
| 270 | +- The `dismissAiOffer` and `optOutAi` Tauri commands may have no callers after |
| 271 | + this cleanup. Decide per-call-site whether to delete them or keep them for |
| 272 | + future settings-side use. |
| 273 | + |
| 274 | +## Testing strategy |
| 275 | + |
| 276 | +- **Env vars** (mirror the `CMDR_MOCK_LICENSE` pattern): |
| 277 | + - `CMDR_FORCE_ONBOARDING=1` (frontend, in `routes/(main)/+page.svelte`) — |
| 278 | + override the `isOnboarded` gate so the wizard always shows. |
| 279 | + - `CMDR_MOCK_FDA=granted|denied|notgranted` (backend, in |
| 280 | + `permissions.rs::check_full_disk_access`) — override the TCC probe so all |
| 281 | + four step-2 branches can be tested without ever opening real System |
| 282 | + Settings. |
| 283 | +- **Tier 3 Vitest** (component a11y + behaviour): one file per step. Mount, |
| 284 | + walk keyboard, assert. |
| 285 | +- **Tier 2 Playwright**: one spec walking the full happy path (Allow + grant, |
| 286 | + pick cloud → enter mock API key → pick model → step 3 → finish) and the |
| 287 | + edge-case branches (Allow + didn't grant, Deny, Linux skip-step-1, |
| 288 | + re-entry from menu). |
| 289 | +- **Real-API smoke**: David's OpenAI key lives in macOS Keychain: |
| 290 | + `security find-generic-password -s OPENAI_API_KEY -a veszelovszki -w`. |
| 291 | + Use `gpt-5.5` model. He has $2500 credits expiring in a week — go wild. |
| 292 | + Use this to verify the cloud connection-check pipeline ends-to-ends with a |
| 293 | + real provider, at least once per milestone that touches AI. |
| 294 | +- **Each E2E test ≤ 1–2 s**. If a wizard spec takes longer, restructure. |
| 295 | +- Don't fix the pre-existing failing test `File viewer selection and copy › |
| 296 | + drag within viewport selects the dragged range` — another agent has it. |
| 297 | + |
| 298 | +## Notes for the planning agent |
| 299 | + |
| 300 | +- The plan goes in `docs/specs/onboarding-revamp-plan.md` (sibling of this file). |
| 301 | +- Reference this file with relative links; don't restate David's copy verbatim |
| 302 | + in the plan, just point back here. |
| 303 | +- Use milestones. Each milestone must end with a committable state, full |
| 304 | + `./scripts/check.sh` green, and `--only-slow` green (per the user's |
| 305 | + workflow). Suggested milestones (the planner is free to refactor): |
| 306 | + 1. Foundations: context bundle (done), wizard skeleton component, dialog |
| 307 | + registry entry, mount point swap, env-var mocks, settings registry |
| 308 | + additions, AI default flip, AI toast cleanup. End state: wizard renders an |
| 309 | + empty 90% sheet with step dots and Back button. |
| 310 | + 2. Step 1 (FDA): port and adapt `FullDiskAccessPrompt.svelte` content into |
| 311 | + the new step. Re-entry variant when FDA already granted. Linux skip. |
| 312 | + 3. Step 2 (AI): the meaty one. Provider list (left), per-provider |
| 313 | + instructions with embedded API-key flow (right), three radio choices, |
| 314 | + three FDA-state copy branches, local-model background download |
| 315 | + orchestration. |
| 316 | + 4. Step 3 (optional): four toggles with the long-form explanations. |
| 317 | + 5. Re-entry: menu item, command palette command, upgrade-nudge toast for |
| 318 | + legacy users. Plus `--only-slow` and a real-API smoke run. |
| 319 | + 6. Polish: design-system updates, docs sweep (architecture.md, |
| 320 | + onboarding/CLAUDE.md, ai/CLAUDE.md, ui/CLAUDE.md as applicable), |
| 321 | + a11y audits (tier 3 per component, tier 2 wizard spec), final |
| 322 | + `./scripts/check.sh --include-slow`. |
| 323 | +- Read these in full before drafting: |
| 324 | + - `AGENTS.md` |
| 325 | + - `docs/architecture.md` |
| 326 | + - `docs/design-principles.md` |
| 327 | + - `docs/design-system.md` |
| 328 | + - `docs/style-guide.md` |
| 329 | + - `apps/desktop/src/lib/onboarding/CLAUDE.md` |
| 330 | + - `apps/desktop/src/lib/ai/CLAUDE.md` |
| 331 | + - `apps/desktop/src/lib/ui/CLAUDE.md` |
| 332 | + - `apps/desktop/src/lib/settings/CLAUDE.md` |
| 333 | + - `apps/desktop/src-tauri/src/fda_gate.rs` |
| 334 | + - `apps/desktop/src-tauri/src/permissions.rs` |
| 335 | + - `apps/desktop/src/lib/onboarding/FullDiskAccessPrompt.svelte` |
| 336 | + - `apps/desktop/src/lib/ai/AiToastContent.svelte` |
| 337 | + - `apps/desktop/src/lib/ai/ai-state.svelte.ts` |
| 338 | + - `apps/desktop/src/lib/settings/sections/AiSection.svelte` |
| 339 | + - `apps/desktop/src/lib/settings/sections/AiCloudSection.svelte` |
| 340 | + - `apps/desktop/src/lib/settings/cloud-providers.ts` |
| 341 | + - `apps/desktop/src/lib/settings/settings-registry.ts` |
| 342 | + - `apps/desktop/src/lib/ui/ModalDialog.svelte` |
| 343 | + - `apps/desktop/src/lib/ui/dialog-registry.ts` |
| 344 | + - `apps/desktop/src/routes/(main)/+page.svelte` (FDA orchestration around |
| 345 | + lines 420–570) |
0 commit comments