Skip to content

feat: [ENG-2465] add web UI Configuration page + promote ByteRover provider onboarding#545

Open
wzlng wants to merge 3 commits intomainfrom
feat/ENG-2465
Open

feat: [ENG-2465] add web UI Configuration page + promote ByteRover provider onboarding#545
wzlng wants to merge 3 commits intomainfrom
feat/ENG-2465

Conversation

@wzlng
Copy link
Copy Markdown
Collaborator

@wzlng wzlng commented Apr 24, 2026

Configuration page

  • Git identity panel: get/set user.name & user.email via the daemon, one-click seed from the signed-in ByteRover account.
  • Remote panel: single origin, HTTPS-only validation, replace preview, Initialize VC CTA when git is uninitialized, "Find in ByteRover" link.
  • Shared SettingsSection + CalloutRow primitives; bg-card alignment with ConnectorsPanel; skeleton loading rows.

Provider onboarding

  • ByteRover pinned to top of the selector with an amber "Native" pill.
  • OAuth popup opens on row click (drops the redundant login-prompt confirmation); 800ms minimum visible delay before popup navigates.
  • ProviderFlowDialog mountable globally via provider-store so any surface can open it (task error CTA, etc.).

Error routing

  • toastVcError maps VC config gaps to a toast action that deep-links to /configuration#identity or #remotes.
  • Task ErrorSection shows "Configure provider" for provider-class errors; checks llmservice:error broadcast (task:error isn't always structured).
  • formatError overrides aligned with Configuration page copy.

Header

  • Responsive: Local badge hides <lg, HelpMenu/BranchDropdown/version collapse at narrower sizes, provider label truncates by breakpoint.
  • StatusDot primitive (tone + pulsing) — pulsing amber when unconfigured, green corner dot on the Plug when active.
  • brv vc init invalidates the remote query so panel auto-refreshes.

Other

  • ProjectDropdown: "Open local folder" opens OS file manager via new locations:reveal transport event (registry-guarded, no shell interp).
  • Global toast position = top-center; removed per-call overrides.
  • CSP: allow img-src https: for OAuth provider avatars.
  • BRV_GIT_REMOTE_BASE_URL surfaced via config:getEnvironment for the Remote URL input placeholder.

Tests

  • Pure util tests: detect-git-url-type, is-valid-email, validate-remote-url, is-provider-task-error, reveal-command.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Claude finished @wzlng's task in 4m 34s —— View job


PR Review — feat: [ENG-2354] Configuration page + ByteRover provider onboarding

  • Gather context and understand the PR
  • Read key changed files
  • Review for correctness, security, and code quality
  • Post inline comments
  • Post final summary

Summary

This is a solid, well-scoped PR. The Configuration page design is clean with good UX patterns (skeleton loading, deep-link anchors, inline validation, "Use account" CTA). The security thinking on the reveal endpoint is particularly good. A few issues need addressing before merge.

Overall score: 4 / 5


Issues requiring attention

🔴 Bug — retry() in LoginPromptStep silently no-ops
The useEffect that calls start() has [popup, setLoggingIn] as its dep array. retry() resets didStartRef.current and triggers a state update, but neither dep changes, so the effect never re-fires. The UI shows the 'starting' spinner indefinitely with no actual login attempt. Fix: add a retryCount state and include it in the effect's deps. See inline comment on login-prompt-step.tsx:151.

🟠 Security — validateRemoteUrl accepts http:// URLs
detectGitUrlType maps both https:// and http:// to the 'https' bucket, so validateRemoteUrl lets plaintext HTTP remotes through. Since push/pull uses an HTTPS token, this could leak credentials in clear text. The fix is to split http:// into its own 'http' bucket and reject it explicitly. See inline comment on detect-git-url-type.ts:8.

🟡 Unhandled rejection — connectByteRover in onAuthenticated
The callback in provider-flow-dialog.tsx calls the async connectByteRover without a .catch. Errors are toasted internally, but the unhandled rejection still produces a console warning. See inline comment on provider-flow-dialog.tsx:429.


Smaller issues

  • DRY: hasCode type guard is copy-pasted between get-vc-config.ts and get-vc-remote.ts. Extract to a shared util.
  • Missing test: toast-vc-error.ts is the only new lib/ utility without a unit test. The CTA routing logic is worth pinning.
  • document.querySelector(hash): Works today, but getElementById(hash.slice(1)) is simpler and immune to CSS selector syntax edge cases.
  • ConnectorsPanel max-width duplication: The panel declares the same max-width constraints that ConfigurationPage already applies to its wrapper, creating a fragile sync requirement.
  • img-src https:: Acceptable for an open-ended provider list, but deserves a comment explaining why it's this broad.

Highlights

  • The registry-guard on locations:reveal before spawning the OS file manager is exactly right — the path comes from the client and must not be trusted blindly.
  • Promise.all([login(), sleep(MIN_VISIBLE_DELAY_MS)]) is an elegant way to enforce a minimum visible delay without a second setTimeout.
  • hadLlmServiceError fallback for provider errors that lose their structured code through the task error serializer is a thoughtful workaround for a real daemon limitation.
  • isSafeHttpUrl validation before window.open in the project dropdown is a good defence against tampered .git/config values.

Comment thread src/webui/features/vc/utils/detect-git-url-type.ts Outdated
Comment thread src/webui/features/vc/api/get-vc-config.ts Outdated
Comment thread src/webui/pages/configuration-page.tsx Outdated
Comment thread src/webui/features/connectors/components/connectors-panel.tsx Outdated
Comment thread src/webui/lib/toast-vc-error.ts
Comment thread src/server/infra/webui/webui-middleware.ts
Comment thread src/server/infra/transport/handlers/locations-handler.ts
wzlng added 2 commits April 25, 2026 06:25
…ovider onboarding

Configuration page
- Git identity panel: get/set user.name & user.email via the daemon,
  one-click seed from the signed-in ByteRover account.
- Remote panel: single `origin`, HTTPS-only validation, replace preview,
  Initialize VC CTA when git is uninitialized, "Find in ByteRover" link.
- Shared SettingsSection + CalloutRow primitives; `bg-card` alignment
  with ConnectorsPanel; skeleton loading rows.

Provider onboarding
- ByteRover pinned to top of the selector with an amber "Native" pill.
- OAuth popup opens on row click (drops the redundant login-prompt
  confirmation); 800ms minimum visible delay before popup navigates.
- ProviderFlowDialog mountable globally via provider-store so any
  surface can open it (task error CTA, etc.).

Error routing
- toastVcError maps VC config gaps to a toast action that deep-links to
  /configuration#identity or #remotes.
- Task ErrorSection shows "Configure provider" for provider-class
  errors; checks llmservice:error broadcast (task:error isn't always
  structured).
- formatError overrides aligned with Configuration page copy.

Header
- Responsive: Local badge hides <lg, HelpMenu/BranchDropdown/version
  collapse at narrower sizes, provider label truncates by breakpoint.
- StatusDot primitive (tone + pulsing) — pulsing amber when
  unconfigured, green corner dot on the Plug when active.
- `brv vc init` invalidates the remote query so panel auto-refreshes.

Other
- ProjectDropdown: "Open local folder" opens OS file manager via new
  locations:reveal transport event (registry-guarded, no shell interp).
- Global toast position = top-center; removed per-call overrides.
- CSP: allow img-src https: for OAuth provider avatars.
- BRV_GIT_REMOTE_BASE_URL surfaced via config:getEnvironment for the
  Remote URL input placeholder.

Tests
- Pure util tests: detect-git-url-type, is-valid-email, validate-remote-url,
  is-provider-task-error, reveal-command.
- Deep-link toast CTAs to /configuration#identity and #remotes so VC errors
  land on the exact panel the user needs to fix, not the page top.
- Split http:// detection from https:// in detectGitUrlType and surface a
  dedicated "Plain HTTP isn't supported" validator message.
- Extract the duplicated `hasCode` type guard from get-vc-config and
  get-vc-remote into src/webui/lib/transport-error.ts.
- Drop the duplicate sm/md/lg max-width on ConnectorsPanel — the parent
  wrapper in ConfigurationPage already applies the same constraints, so
  the self-constraint was a drift risk.
- Fix LoginPromptStep's retry(): the start effect's deps didn't include
  anything that changed on retry, so the UI got stuck on "Preparing
  sign-in…" forever. Added a retryCount trigger to the deps.
- Comment the deliberately broad `img-src https:` CSP directive: OAuth
  provider avatar CDNs can't be enumerated ahead of time.
- Unit-test toastVcError's CTA wiring (CONFIG_KEY_NOT_SET, NO_REMOTE,
  unknown-code fall-through) and the new http-vs-https url validation
  paths.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Claude finished @wzlng's task in 2m 3s —— View job


Re-review — post-cleanup commit (1136381)

  • Gather context and understand the PR
  • Read files changed in the cleanup commit
  • Check each previously-flagged issue
  • Post updated review summary

The cleanup commit addressed all seven issues from the original review. Here's the per-issue accounting:


🔴 Bug — retry() silently no-ops → Fixed

login-prompt-step.tsx now has const [retryCount, setRetryCount] = useState(0) and the useEffect dep array is [popup, setLoggingIn, retryCount]. retry() increments retryCount, which forces the effect to re-run. The comment on line 101–103 accurately explains the intent. ✅


🟠 Security — validateRemoteUrl accepts http://Fixed

detect-git-url-type.ts now returns 'http' for http:// URLs (line 9), not 'https'. validate-remote-url.ts explicitly rejects 'http' with "Plain HTTP isn't supported — use an HTTPS URL." (line 15). Both test files (detect-git-url-type.test.ts and validate-remote-url.test.ts) have cases covering the http:// rejection path. ✅


🟡 Unhandled rejection — connectByteRover in onAuthenticatedNot changed

provider-flow-dialog.tsx was not touched in the cleanup commit; line ~422 still reads:

onAuthenticated={() => {
  connectByteRover(selectedProvider)
}}

The practical risk is low — connectByteRover catches all errors internally and toasts them — but the fire-and-forget call will still trigger a browser console warning when the callback fires. A one-line fix remains applicable: Fix this →


Nitpicks → all addressed

Issue Resolution
hasCode type guard duplicated Extracted to src/webui/lib/transport-error.ts; both get-vc-config.ts and get-vc-remote.ts now import from it ✅
Missing test for toast-vc-error.ts test/unit/webui/lib/toast-vc-error.test.ts added; covers CONFIG_KEY_NOT_SET CTA, NO_REMOTE CTA, and unknown-code plain toast ✅
document.querySelector(hash) selector risk Resolved by removing hash-based scroll entirely from configuration-page.tsx — the #identity/#remotes/#connectors anchor sections and the useEffect were dropped ✅
ConnectorsPanel max-width duplication Removed sm:max-w-lg md:max-w-xl lg:max-w-2xl from the panel; it now fills its parent with w-full
img-src https: needs a comment Comment added in webui-middleware.ts lines 38–42 explaining the open-ended provider CDN rationale ✅

One trade-off to note

Dropping the hash anchors simplifies the code but loses the scroll-to-section behaviour the PR description advertised. Toast CTAs (Set identity, Set remote) now navigate to /configuration without scrolling to the relevant panel. With three short panels on a single page this is probably fine, but worth a deliberate decision rather than a side-effect of the CSS selector cleanup.


Updated overall score: 4.5 / 5 — the one remaining issue (connectByteRover fire-and-forget) is low-severity. All structural and security concerns are resolved.

@bao-byterover bao-byterover changed the title feat: [ENG-2354] add web UI Configuration page + promote ByteRover provider onboarding feat: [ENG-2465] add web UI Configuration page + promote ByteRover provider onboarding Apr 25, 2026
Comment thread src/server/infra/transport/handlers/locations-handler.ts Outdated
- Replace the "Not set" callout (foreground title + dashed alert chrome) with
  a calmer account-led row: avatar + suggested identity, soft "from your
  ByteRover account — optional, used for commit attribution" sub-line, and
  ghost Edit / primary Apply actions. Fixes user feedback that the previous
  copy read as required-field-error.
- Drop the unused CalloutRow import from this file (kept for other panels).
- locations-handler: pass `windowsHide: true` to the reveal-folder spawn so
  the brief CMD flash is suppressed on Windows; minor format-only line-wrap
  on the REVEAL onRequest registration.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 25, 2026

Claude finished @wzlng's task in 1m 39s —— View job


Re-review — post-synchronize commit (d449491)

  • Gather context and understand new commits
  • Check windowsHide fix (blocking issue from bao-byterover)
  • Check connectByteRover fire-and-forget (remaining issue from prior review)
  • Review identity not-set row redesign
  • Post updated review summary

The new commit addresses the blocking windowsHide issue and ships the identity-row redesign. Here's the full accounting.


🔴 Blocking — windowsHide: true on spawnFixed

locations-handler.ts:126 now passes {detached: true, stdio: 'ignore', windowsHide: true} to spawn. The CMD flash on Windows is suppressed. ✅


🟡 Carry-over — connectByteRover fire-and-forget → Still not fixed

provider-flow-dialog.tsx:422 still reads:

onAuthenticated={() => {
  connectByteRover(selectedProvider)
}}

This remains a one-liner fix: Fix this →


Identity not-set row redesign — reviewed

The overall direction is correct — leading with the account avatar is a much calmer UX than the previous "Not set" alert box.

🟡 Minor — email shown twice when user.name is undefined

identity-panel.tsx:61:

const displayName = user.name ?? user.email

When the ByteRover account has no display name, displayName is the email address, and line 71 renders:

user@example.com <user@example.com>

Consider hiding the <email> suffix when displayName already is the email:

{displayName}
{user.name && (
  <span className="text-muted-foreground font-normal">&lt;{user.email}&gt;</span>
)}

Nitpick — initials() on an email address

initials('user@example.com') returns "US" (the first two characters) because @ and . are not split tokens. This is fine for the fallback avatar, but worth noting. Fix this →

Praise

The early-return split between !user (no account) and user (account found) is clean — two clearly distinct UX states with no tangled conditionals. The shrink-0 on the button group prevents it from collapsing on narrow panels. ✅


Summary

Issue Status
🔴 windowsHide on spawn ✅ Fixed in d449491
🟡 connectByteRover fire-and-forget ⏳ Still open (low severity)
🟡 Email shown twice when no display name New in d449491
Nitpick: initials() on email address New in d449491

Updated overall score: 4.5 / 5 — the blocking issue is resolved. Two low-severity items remain; neither is a merge blocker.

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