Skip to content

feat(desktop): first-run onboarding overlay for default provider API key#2752

Merged
esengine merged 3 commits into
esengine:main-v2from
Bernardxu123:feat/desktop-onboarding
Jun 2, 2026
Merged

feat(desktop): first-run onboarding overlay for default provider API key#2752
esengine merged 3 commits into
esengine:main-v2from
Bernardxu123:feat/desktop-onboarding

Conversation

@Bernardxu123

Copy link
Copy Markdown
Collaborator

Summary

Adds a first-run gate in the desktop app: when the default provider's
API key (DEEPSEEK_API_KEY, matching Default() in internal/config) is
unset in the process, the user sees a full-window overlay asking them
to paste a sk-... key. The submit button validates the key against
the vendor's balance endpoint, persists it to ./.env, and rebuilds
the controller. The overlay stays up until the kernel emits
agent:ready and the main UI takes over.

Closes the "what does the user do when they download the desktop app
and launch it for the first time" gap — currently the chat composer
just shows a topbar error and the user has to dig into Settings to
find the API key field.

Changes (3 commits)

  1. chore(desktop): allow esbuild postinstall in pnpm workspace
    pnpm 11+ refuses to run build scripts unless explicitly approved.
    wails dev would exit 1 on a fresh clone with
    ERR_PNPM_IGNORED_BUILDS. Project-scope esbuild: true so it
    travels with the repo.

  2. feat(desktop): add NeedsOnboarding/ConnectKey bindings
    Two new methods on *App:

    • NeedsOnboarding() bool — true when DEEPSEEK_API_KEY is empty
      in the process. Cheap, no I/O.
    • ConnectKey(apiKey string) error — validates via
      billing.FetchWithClient (zero-token, 8s timeout), persists via
      upsertDotEnv (writes to ./.env and os.Setenvs the key so
      the next rebuild picks it up), then triggers a controller
      rebuild. Returns "key is required" / "status 401" /
      "network: ..." for the UI to map.
  3. feat(desktop): add first-run OnboardingOverlay

    • desktop/frontend/src/components/OnboardingOverlay.tsx
      full-window modal with logo, paste field, submit button (with
      spinner), "How do I get a key?" link (opens
      platform.deepseek.com/api_keys in the system browser), and a
      small "Skip for now" footer link.
    • desktop/frontend/src/App.tsx — one-shot NeedsOnboarding()
      probe on mount; gates the entire app on the result. Probe is
      useEffect([]) so Settings panel changes don't re-trigger.
    • desktop/frontend/src/lib/bridge.ts — adds both methods to
      AppBindings; browser dev mock returns true when no provider
      has its key set, so the overlay flow is exercisable in
      pnpm dev outside the Wails shell.
    • desktop/frontend/src/styles.css.onboarding* classes
      (dark --bg-elev card, --accent CTA, mono input, spinner
      animation). Terminal-hacker aesthetic, matches the rest of
      the theme.
    • desktop/frontend/src/locales/{en,zh}.ts — 13 i18n keys each
      (title, tagline, input label/placeholder, submit, validating,
      get-key link, privacy, three error states, unknown, skip).

UX flow

  1. Fresh install / cleared key → app launches → overlay covers the
    window. Composer is hidden; no chat possible without a key.
  2. User pastes a key, hits Enter (or clicks "Connect & start").
    Button shows a spinner; input locks.
  3. Go side validates → 401 → "That key didn't work — check it's
    active and has billing set up." Network fail → "Couldn't reach
    DeepSeek — check your network and try again." Other → raw message.
  4. Success → key written to ./.env (or desktop/.env in wails
    dev), process env updated, controller rebuilt. Frontend unmounts
    the overlay; agent:ready → main UI.
  5. "Skip for now" → overlay dismisses, user can poke around the
    empty state. Trying to chat surfaces the topbar startupError
    banner; Settings panel is the path back.

i18n

Auto-detects from navigator.language (WebView2 reflects the system
locale on Windows). User override in Settings → Language. All 13
new keys present in both en.ts and zh.ts; translate() falls
back to English on missing keys, so partial translations degrade
gracefully.

Test plan

  • go build ./... — clean
  • tsc --noEmit — clean
  • vite build — clean
  • go test ./... in desktop/ — all pass
  • Local wails dev: rename .env.env.bak, clear
    DEEPSEEK_API_KEY from User env, restart wails dev → overlay
    appears. Paste valid key → overlay dismisses, desktop/.env
    has the new key, restart wails dev → no overlay.
  • Click "Skip for now" → main UI loads, topbar shows
    startupError, Settings panel accepts a key and the chat
    resumes after the kernel rebuilds.
  • i18n: switch Settings → Language to 中文, restart wails dev,
    overlay text flips to Chinese (title: 连接 Reasonix, submit:
    连接并开始, skip: 稍后设置, etc.).

Notes

  • No new dependencies. Reuses billing.FetchWithClient,
    upsertDotEnv, a.rebuild(). Adds one log line in
    ConnectKey for save failures (will be added in a follow-up if
    reviewers want).
  • Doesn't touch main.go / startup() / buildController().
    Onboarding is purely a frontend gate; if the user skips, the
    existing topbar error path takes over.
  • Bridge mock returns true from NeedsOnboarding() when no
    provider has its key set, so pure browser dev (pnpm dev)
    exercises the overlay without rebuilding the Go side.
  • The desktop/frontend/pnpm-workspace.yaml is unrelated to the
    feature itself but blocks wails dev on a fresh clone; committing
    it here saves the next contributor the 10-minute debug session.

pnpm 11+ refuses to run build scripts (esbuild's postinstall fetches its
native binary) unless explicitly approved. Without this file, every fresh
clone hits ERR_PNPM_IGNORED_BUILDS and `wails dev` exits 1 on the first
run. See https://pnpm.io/settings#onlybuiltdependencies.

Setting `esbuild: true` here keeps the approval project-scoped so it
travels with the repo.
Exposes two new bound methods on `App` so the React side can ask whether
the default provider's API key is missing and, if so, write one in:

- `NeedsOnboarding() bool` — true when DEEPSEEK_API_KEY is unset in the
  process. Cheap, no I/O; the frontend calls it once on mount.
- `ConnectKey(apiKey string) error` — validates the pasted key against
  the vendor's balance endpoint (no tokens spent, ~hundreds of ms),
  persists it to `./.env` via the existing `upsertDotEnv` helper
  (which also `os.Setenv`s it so the next rebuild picks it up), then
  triggers a controller rebuild. The kernel emits `agent:ready` once
  boot.Build completes, and the frontend unmounts the overlay.

A one-line `mustCwd` helper logs the process cwd for diagnostics when
env probes look wrong; it lives next to NeedsOnboarding for now and can
move if a second consumer appears.
When `app.NeedsOnboarding()` returns true (default provider's API key
is unset in the process), App.tsx mounts a full-window overlay that
asks the user to paste a `sk-...` key. The submit button calls
`app.ConnectKey(key)`, which validates against the vendor's balance
endpoint and persists to `./.env` (handled in the previous Go commit).

UX choices:
- One-shot probe on mount only. Settings panel changes that add a key
  don't re-trigger the overlay (the user has clearly already onboarded).
- "Skip for now" footer link drops the user into the main UI; the
  topbar `startupError` banner surfaces the missing key when they try
  to chat. The Settings panel is the canonical path to set it later.
- Validation errors map to i18n keys: 401/403 → "invalid", dial/timeout
  → "network", everything else → the raw message. The Go side returns
  human-readable strings already.
- i18n keys live in en.ts and zh.ts alongside the rest of the dict
  (13 keys each). Missing keys fall back to English per the existing
  translate() behavior.
- Styling matches the terminal-hacker aesthetic: dark `--bg-elev`
  card, `--accent` CTA, mono font for the input, spinner on submit.

The browser dev mock in `bridge.ts` returns true from NeedsOnboarding
when no provider has its key set, so the overlay is exercisable in
`pnpm dev` outside the Wails shell (handy for layout tweaks without
rebuilding the Go side).
@github-actions github-actions Bot added the v2 Go rewrite (1.x) — main-v2 branch, active development label Jun 2, 2026
@esengine esengine merged commit 5cdfbe9 into esengine:main-v2 Jun 2, 2026
6 checks passed
@Bernardxu123 Bernardxu123 deleted the feat/desktop-onboarding branch June 2, 2026 14:53
CVEngineer66 pushed a commit to CVEngineer66/DeepSeek-Reasonix that referenced this pull request Jun 7, 2026
The .onboarding__skip:disabled block was missing its closing '}', causing
esbuild (used by Vite dev mode) to abort CSS parsing at line 7049. All
rules after line 7049 — including .workbench-dock layout positioning —
were silently discarded, breaking the right-side dock layout.

Introduced as a merge conflict artefact between esengine#2752 (onboarding) and
esengine#3129 (InlineDiff component).
CVEngineer66 pushed a commit to CVEngineer66/DeepSeek-Reasonix that referenced this pull request Jun 7, 2026
A second merged conflict artefact from esengine#2752 (onboarding) and esengine#3129
(InlineDiff) left an unclosed block at line 7082. Same root cause as
PR esengine#3418 but at a different offset. The check-css-syntax.mjs script
(PR esengine#3420) now catches this, but upstream itself is affected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants