feat: [ENG-2465] add web UI Configuration page + promote ByteRover provider onboarding#545
feat: [ENG-2465] add web UI Configuration page + promote ByteRover provider onboarding#545
Conversation
|
Claude finished @wzlng's task in 4m 34s —— View job PR Review — feat: [ENG-2354] Configuration page + ByteRover provider onboarding
SummaryThis 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 Overall score: 4 / 5 Issues requiring attention🔴 Bug — 🟠 Security — 🟡 Unhandled rejection — Smaller issues
Highlights
|
…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.
|
Claude finished @wzlng's task in 2m 3s —— View job Re-review — post-cleanup commit (
|
| 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.
- 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.
|
Claude finished @wzlng's task in 1m 39s —— View job Re-review — post-synchronize commit (
|
| 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.
|
Claude finished @wzlng's task in 2m 6s —— View job Re-review — post-synchronize (merge from
|
| File | Reason |
|---|---|
summary-frontmatter.ts |
sortKeys: false to preserve curate's canonical field order |
consolidate.ts |
Same sortKeys fix + mkdtemp flakiness fix |
synthesize.ts |
Same sortKeys fix |
| Test files | Updated assertions matching the new behavior |
None of these interact with the Configuration page or provider-onboarding code. ✅
Carry-over issues (unchanged from d449491 review)
🟡 connectByteRover fire-and-forget — still open
provider-flow-dialog.tsx:422 still calls the async function without .catch():
onAuthenticated={() => {
connectByteRover(selectedProvider) // ← no .catch
}}connectByteRover fully handles its own errors (lines 131–133 have a try/catch that toasts), so no user-visible impact — but the browser will log an unhandled-rejection console warning on error. One-liner fix: Fix this →
🟡 Email shown twice when user.name is undefined — still open
identity-panel.tsx:61–71:
const displayName = user.name ?? user.email // email when no name
// ...
{displayName} <span>…<{user.email}></span>
// renders: "user@example.com <user@example.com>"Fix: only render the <email> suffix when displayName !== user.email: Fix this →
Nitpick — initials() on an email address
src/webui/utils/initials.ts splits on [-_\s]+ only. initials('user@example.com') returns "US" (first two chars of the unsplit string) rather than something like "UE". Low impact for a fallback avatar, but the split regex could include @ and .: Fix this →
Summary
| Issue | Status |
|---|---|
🟡 connectByteRover fire-and-forget |
⏳ Still open (low severity) |
| 🟡 Email shown twice when no display name | ⏳ Still open |
Nitpick: initials() on email address |
⏳ Still open |
Merge from main (3f4f6ae) |
✅ Clean, no conflicts, unrelated files only |
Overall score: 4.5 / 5 — merge is clean, no regressions. The two 🟡 items are not merge blockers, but fixing the double-email display would be a quick UX polish worth landing before the feature ships.
- Task retry: ErrorSection on the task detail now offers a "Try again"
CTA alongside "Configure provider". A new `useComposerRetryStore` hands
the failed task's content + type to whichever composer host is mounted
(TaskListView in normal mode, TourHost in tour mode), which re-opens
the composer prefilled. The task detail sheet closes on retry so the
composer takes focus rather than stacking. Pure helpers
(`composer-type-from-task`, the store) are unit-tested.
- Tour FSM rework for curate/query (steps 2/3): no more auto-mounted
composer or auto-navigation — the user clicks through the highlighted
Tasks tab → "New task" button so the IA itself is the lesson.
- New `TourBackdrop`: page-wide dim + blur on every route during the
composer steps. Click-blocking so a stray click on a blurred
Configuration tab doesn't yank the user out of the tour.
- New `TourPointer`: wraps a target with a primary-tinted glow and a
hand-drawn curved arrow + label. Arrow is pinned to the target's
center so the tip lands on the button regardless of the label's
`align`; the curve mirrors itself based on which side the label sits
on so the stick always flows from label to target.
- Coachmark on the global Tasks tab (only when off /tasks). "New task"
coachmark moves between the EmptyState and FilterBar buttons so we
never highlight both at once.
- TaskListView becomes the single composer host: reads tour state,
prefills with the example + step label, sets `tourTaskId` on submit.
`useTourWatchers` keeps only the provider→curate auto-advance.
- Failed tour task no longer surfaces "Continue": gated on
`task.status === 'completed'` instead of `isTerminalStatus`. Banner
copy branches per status. "Try again" broadened to all error codes so
non-provider failures in tour mode aren't a dead end.
- VC remote delete: `RemotesPanel` gains a destructive ghost "Delete"
button next to "Edit" with an `AlertDialog` confirm, reusing the
daemon's `vc:remote { subcommand: 'remove' }` path landed via
ENG-2466. `SetVcRemoteInput` tightened to a discriminated union so
`remove` doesn't carry an unused url field.
- PrefillBadge moved bottom-left so it shares the textarea's reserved
bottom band with the keyboard hint instead of overlapping the first
line of the prefilled example.
|
Claude finished @wzlng's task in 4m 21s —— View job Re-review — post-synchronize commit (
|
| Issue | Status |
|---|---|
🟡 connectByteRover fire-and-forget |
⏳ Still open (3rd round) |
| 🟡 Email shown twice when no display name | ⏳ Still open |
Nitpick: initials() on email address |
⏳ Still open |
🟡 closeTask missing from useEffect deps |
New in 0a99049 |
Nitpick: TourPointer curveFrom direction |
New in 0a99049 |
Overall score: 4.5 / 5 — the tour rework and retry flow are well-designed and the remote delete is clean. The three carry-over items (especially the double-email display) and the missing dep are worth addressing before the feature ships.
…s on query, badge alignment
- ComposerHeader: stop hiding the curate/query slider in tour mode. It now
renders disabled with a Tooltip ("Locked during the tour — the next step
covers the other mode."), so the user learns the affordance exists
instead of meeting it cold after the tour ends. TypeSlider gained an
optional `disabled` prop that applies `opacity-60` + `cursor-not-allowed`
+ `aria-disabled` and short-circuits hover styling.
- useTourWatchers: when the tour transitions into `query`, strip
`?task=…` from the URL. Otherwise the just-finished curate task's
detail sheet would still be open and would hide the FilterBar's
"New task" button that the next coachmark points at. The setter
returns `prev` unchanged when the param isn't present, so the effect
doesn't trigger an unneeded navigation.
- TourStepBadge: add `inline-flex items-center leading-none` so the 10px
label centers cleanly inside the 24px (h-6) pill instead of sitting on
the default text baseline.
|
Claude finished @wzlng's task in 2m 36s —— View job Re-review — post-synchronize commit (
|
| Issue | Status |
|---|---|
🟡 connectByteRover fire-and-forget |
⏳ Still open (4th round) |
| 🟡 Email shown twice when no display name | ⏳ Still open |
Nitpick: initials() on email address |
⏳ Still open |
🟡 closeTask missing from useEffect deps |
⏳ Still open |
Nitpick: TourPointer curveFrom direction |
⏳ Still open |
Badge alignment (leading-none) |
✅ Fixed in 5e462c9 |
Task detail auto-close on query step |
✅ New in 5e462c9, clean |
| Slider visible-but-locked with tooltip | ✅ New in 5e462c9, correct |
Nitpick: aria-disabled on non-interactive div |
New in 5e462c9 |
Overall score: 4.5 / 5 — the tour polish commit is clean and purposeful. No new issues of substance were introduced. The five carry-over items (two 🟡 UX, two nitpicks, one longstanding fire-and-forget) remain the only things standing between this and a clean merge.
The previous strip-`?task=` effect on entering query re-fired whenever `setSearchParams`'s reference changed (it's not stable across renders in react-router-dom), so it stripped the URL again right after the user submitted a query — closing the just-opened query detail sheet. Gate the strip on `!tourTaskId` so it only runs at the curate→query transition (when `advanceTour` has just cleared `tourTaskId`) and bails once the user has submitted a query for this step. Also add `closeTask` to the retry-seed effect's deps for completeness.
|
Claude finished @wzlng's task in 1m 46s —— View job Re-review — post-synchronize commit (
|
| Issue | Status |
|---|---|
🟡 connectByteRover fire-and-forget |
⏳ Still open (5th round) |
| 🟡 Email shown twice when no display name | ⏳ Still open |
Nitpick: initials() on email address |
⏳ Still open |
Nitpick: aria-disabled on non-interactive div |
⏳ Still open |
🟡 closeTask missing from useEffect deps |
✅ Fixed in 90e4913 |
Nitpick: TourPointer curveFrom comment |
✅ Clarified in 90e4913 |
| Tour query-detail stays open after submit | ✅ Fixed in 90e4913 |
Overall score: 4.5 / 5 — the tour detail fix is correct and well-reasoned. Two closed items. The one 🟡 that keeps appearing (connectByteRover fire-and-forget) and the double-email display are the only things worth addressing before the feature ships.

Configuration page
origin, HTTPS-only validation, replace preview, Initialize VC CTA when git is uninitialized, "Find in ByteRover" link.bg-cardalignment with ConnectorsPanel; skeleton loading rows.Provider onboarding
Error routing
Header
brv vc initinvalidates the remote query so panel auto-refreshes.Other
Tests