Skip to content

Commit 5a21bdb

Browse files
committed
Plan: onboarding revamp (FDA + AI + optional setup)
Soft-sheet multi-step wizard replacing the single FDA modal. - M1 foundations, M2 step 1 (FDA), M3 step 2 (AI), M4 step 3 (optional) - M5 re-entry + slow lane, M6 polish + docs sweep - Context bundle freezes pre-planning decisions; plan goes through 3 review rounds before exec.
1 parent 9962b00 commit 5a21bdb

2 files changed

Lines changed: 1109 additions & 0 deletions

File tree

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
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

Comments
 (0)