Skip to content

fix: onboarding API-key input loses focus when probe completes (#1503)#1519

Merged
1 commit merged intonesquena:masterfrom
franksong2702:fix/1503-onboarding-apikey-focus-loss
May 3, 2026
Merged

fix: onboarding API-key input loses focus when probe completes (#1503)#1519
1 commit merged intonesquena:masterfrom
franksong2702:fix/1503-onboarding-apikey-focus-loss

Conversation

@franksong2702
Copy link
Copy Markdown
Contributor

Thinking Path

  • The onboarding wizard's API-key input has oninput="_scheduleOnboardingProbe()" which fires a 400ms-debounced probe on every keystroke
  • When the probe completes, _setOnboardingProbeState() calls _renderOnboardingBody() which rebuilds the entire form DOM
  • This destroys the <input> element the user is typing into — focus and cursor position are lost
  • On localhost the probe completes in ~5-50ms so the bug window is narrow; on slow networks (VPN, corporate proxy, cold-start vLLM) the re-render routinely lands between keystrokes
  • The password field is where impact is felt most — users type long secrets character by character
  • Fix: remove _scheduleOnboardingProbe() from the api-key input's oninput handler (Option A from the issue). Add onblur="_runOnboardingProbe()" so the probe fires when the user tabs away

What Changed

static/onboarding.js — line 200, one line change:

  • Before: oninput="ONBOARDING.form.apiKey=this.value;_scheduleOnboardingProbe()"
  • After: oninput="ONBOARDING.form.apiKey=this.value" onblur="_runOnboardingProbe()"

Why It Matters

  • The API-key field is a type="password" input where users paste or type long secrets
  • Losing focus mid-typing is disorienting — "I was typing my key and the field stopped responding"
  • The probe still fires via: (a) onblur when user tabs away, (b) "Test connection" button click, (c) nextOnboardingStep() before Continue
  • The inline probe banner may not show "401 → key required" until Continue is clicked, but nextOnboardingStep gates on probe success so the flow is still correct

Verification

  • _scheduleOnboardingProbe() is only removed from the api-key input — the baseUrl input (line 185) still has it, which is acceptable UX
  • _runOnboardingProbe() on onblur ensures the probe fires before the user reaches the Continue button
  • nextOnboardingStep() (line 525) runs _runOnboardingProbe() synchronously before allowing Continue — no flow breakage
  • 25/25 tests pass

Risks / Follow-ups

  • Risk: If a user pastes a key and clicks Continue without first blurring the field, the inline probe banner won't show the result until Continue is clicked. This is acceptable — nextOnboardingStep handles it.
  • Follow-up: Option B from the issue (targeted DOM update instead of full re-render) is the proper architectural fix but is ~30-50 LOC. This PR is the 1-line mitigation.

Model Used

  • xiaomi/mimo-v2.5-pro (via Xiaomi MiMo)

…ena#1503)

The onboarding wizard's API-key input calls _scheduleOnboardingProbe()
on every keystroke (oninput). When the 400ms-debounced probe completes,
_setOnboardingProbeState() calls _renderOnboardingBody() which rebuilds
the entire form — destroying and recreating the <input> element. The
user's focus and cursor position are lost.

On fast connections (localhost) the probe completes between keystrokes
so the bug window is narrow. On slow networks (VPN, corporate proxy,
cold-start vLLM) the re-render routinely lands mid-typing.

Fix: remove _scheduleOnboardingProbe() from the api-key input's
oninput handler. The probe still fires on:
- baseUrl input change (oninput + debounce, unchanged)
- api-key field blur (onblur, added)
- 'Test connection' button click (unchanged)
- nextOnboardingStep() before Continue (unchanged)

The baseUrl input retains the oninput probe because the UX trade-off
is acceptable there (text input preserves visible content on re-render).
@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Confirmed the shape locally — static/onboarding.js:200 is the offending line:

oninput="ONBOARDING.form.apiKey=this.value;_scheduleOnboardingProbe()"

…and _scheduleOnboardingProbe() ultimately calls _renderOnboardingBody() on probe completion, which rebuilds the form DOM and destroys the active <input>. On localhost the bug window is small, but on slow networks the re-render lands between keystrokes — which matches every report we've seen on #1503.

The fix matches Option A from the issue and is the right one:

  • oninput keeps the form-state sync (ONBOARDING.form.apiKey=this.value) but stops triggering the probe
  • onblur="_runOnboardingProbe()" keeps the auto-probe behavior when the user tabs away
  • The "Test connection" button and nextOnboardingStep() still cover the explicit-probe paths

Two things I'll spot-check on review:

  1. Base-URL field at line 185 — that one also has oninput="...;_scheduleOnboardingProbe()". It's a type="text" input where re-render-mid-typing is less painful (and sometimes desirable), so keeping it as-is is defensible. Confirm the scope is intentionally api-key-only.
  2. No regression in cold-start flow — when a user pastes a long key all at once into an empty field, onblur should still fire the probe with the full value (the paste-then-blur path).

Pulling locally to verify on a probe-delayed onboarding flow. Thanks @franksong2702.

@nesquena-hermes nesquena-hermes closed this pull request by merging all changes into nesquena:master in f8ed6da May 3, 2026
sunnysktsang pushed a commit to sunnysktsang/hermes-webui that referenced this pull request May 3, 2026
sunnysktsang pushed a commit to sunnysktsang/hermes-webui that referenced this pull request May 3, 2026
…sorbed

CHANGELOG, ROADMAP, TESTING bumped (3936 \u2192 3946).

8 constituent PRs:
- nesquena#1523 (@franksong2702) branch indicator codepoint fix
- nesquena#1519 (@franksong2702) onboarding API-key focus loss fix
- nesquena#1518 (@franksong2702) voice-mode toggle-off recognizer stop
- nesquena#1516 (@franksong2702) YAML newline CSS rules
- nesquena#1517 (@franksong2702) __CACHE_VERSION__ \u2192 __WEBUI_VERSION__ rename
- nesquena#1532 (@ai-ag2026) state.db WebUI session recovery
- nesquena#1525 (@ai-ag2026) stale stream state proactive cleanup
- nesquena#1526 (@ai-ag2026) max_tokens forwarding + OpenRouter quota classifier

Opus MUST-FIX absorbed: sw.js conflict-marker cleanup + regression guard.
Opus SHOULD-FIX deferred to follow-up nesquena#1533 (race in _clear_stale_stream_state).

2 closed as duplicates: nesquena#1528 (identical to nesquena#1517), nesquena#1529 (superseded by nesquena#1516).
1 maintainer-review label: nesquena#1531 (Asunfly stowaway change in force-push).
5 stay on hold: nesquena#1418 nesquena#1464 nesquena#1404 nesquena#1353 nesquena#1311.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants