Skip to content

fix(desktop): resolve UI audit findings across native shell, bridge & web UI#89

Merged
cnjack merged 1 commit into
mainfrom
fix/desktop-ui-audit
Jun 21, 2026
Merged

fix(desktop): resolve UI audit findings across native shell, bridge & web UI#89
cnjack merged 1 commit into
mainfrom
fix/desktop-ui-audit

Conversation

@cnjack

@cnjack cnjack commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Systematic remediation of a multi-agent UI audit of the desktop app (Tauri native shell + bridge composables + the reused web UI). Fixes all P1/P2 findings and the bulk of P3s, organized around the underlying patterns (silent failures, stale-on-switch state, desktop-shell-specific gaps, dead code) rather than one-off symptoms.

Related Issues / Tickets

  • N/A (internal audit follow-up)

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring / code cleanup
  • Performance improvement
  • Test update

Changes Made

  1. Approvals survive reload / session-switch / WS-reconnect — backend now retains the request payload (webPendingApproval{ch,data}) and exposes GET /api/approval/pending (PendingApprovalRequests); the store pulls and re-attaches pending approval cards via reconcileApprovals() alongside the existing ask_user reconcile. A pending approval no longer disappears and leaves the agent hung.
  2. Surface failures instead of swallowing them — WorkspacePicker switch errors (inline), a backend-unreachable boot overlay with Retry, approval-resolve errors + in-flight double-submit guard, file-open / directory-load errors.
  3. Switch hygiene — a single loadWorkspaceState() shared by boot/setup/project-switch (the three previously fetched different subsets); RightPanel Files/Changes remount on workspace change; ChatInput clears draft/images across sessions; one unified post-switch handler so the inline picker and the projects modal behave identically.
  4. Desktop-shell specifics — drag strips for splash / setup / SSH wizard under the macOS overlay title bar; tool output (terminal/file/diff/grep) is selectable & copyable inside the shell; the native folder picker distinguishes cancel vs. unavailable and falls back to the in-app browser.
  5. Composer — hide the workspace switcher once a conversation has started (the task's workspace is fixed); keep the branch picker; drop the whole row when it would be empty.
  6. Consistency / a11y — TopBar status dot & label priority unified; terminal tabs no longer nest <button> and the close control is keyboard-reachable; sidebar archived-task toggle restored; inline task rename (window.prompt is unreliable in the webview); MCP login message scoped to its server; SetupView provider-switch clears stale key + validation; tray left-click restores a minimized window; Esc no longer kills the run while a modal is open.
  7. Redundancy / leaks — delete unused MCPPanel.vue; remove dead exports / dead rootPath watch / dead setup base-URL code; clean up the channel-poll timer leak in Settings.

Testing

  • npx vue-tsc --build — clean
  • npx vite build — clean (pre-existing chunk-size warning only)
  • go build ./..., go vet ./internal/handler ./internal/web, go test ./internal/handler ./internal/web — all pass
  • cargo check (desktop/src-tauri) — clean
  • npx oxlint src/ — 0 warnings / 0 errors

Screenshots / Recordings

N/A — behavioral/state fixes; verified via build + tests (full-stack runtime needs the Go sidecar).

Checklist

  • I have read the contributing guidelines.
  • My code follows the project's style guidelines.
  • I have performed a self-review of my code.
  • I have added/updated tests as needed.
  • I have updated relevant documentation.
  • All new and existing tests pass.

Additional Notes

Consciously deferred (with rationale, not regressions):

  • MCP edit-form OAuth backfill — needs the backend to expose client_id/scopes in the list response (and client_secret must never be round-tripped), a backend redesign with security implications rather than a safe quick edit.
  • useBranch window listeners — an intentional app-lifetime singleton (bounded, non-growing), so a ref-counted teardown adds risk for negligible benefit.
  • A handful of purely cosmetic/subjective items (English-only setup copy with no i18n system, mobile 2px margin, favorites filter) left to avoid regressions.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added full-window connection error overlay with retry button
    • Approval requests now persist across page reloads and reconnects
    • Native folder picker support on desktop platforms
    • Inline task renaming in sidebar
  • Bug Fixes

    • Fixed minimized window toggle behavior
    • Tool status no longer stuck in running state after agent completion
    • Improved Escape key handling for agent control context
  • Improvements

    • Enhanced file and directory error handling with retry options
    • Improved accessibility with ARIA labels and semantic HTML
    • Better workspace switching reliability
    • Improved macOS window dragging support

… web UI

Systematic pass over the desktop UI (Tauri native shell + bridge + the reused
web UI) addressing a multi-agent audit. Fixes all P1/P2 findings plus the bulk
of P3s, grouped by the underlying patterns rather than one-off symptoms.

Highlights:
- Approvals survive reload/switch/reconnect: backend retains the request payload
  (webPendingApproval) and exposes GET /api/approval/pending; the store pulls and
  re-attaches pending approval cards via reconcileApprovals() alongside the
  existing ask_user reconcile — a pending approval no longer vanishes and hangs
  the agent.
- Surface failures instead of swallowing them: WorkspacePicker switch errors,
  a backend-unreachable boot overlay with Retry, approval-resolve errors +
  double-submit guard, file-open and directory-load errors.
- Switch hygiene: single loadWorkspaceState() shared by boot/setup/project-switch
  (no more drifting fetch sets); RightPanel Files/Changes remount on workspace
  change; ChatInput clears draft/images across sessions; unified post-switch
  handler so the inline picker and projects modal behave identically.
- Desktop-shell specifics: drag strips for splash / setup / SSH wizard under the
  macOS overlay title bar; tool output is selectable/copyable in the shell;
  native folder picker distinguishes cancel vs unavailable and falls back.
- Composer: hide the workspace switcher once a conversation has started (the
  task's workspace is fixed); keep the branch picker.
- Consistency/a11y: TopBar status dot/label priority unified; terminal tabs no
  longer nest <button> and the close control is keyboard-reachable; sidebar
  archived-task toggle restored; inline task rename (window.prompt is unreliable
  in the webview); timer-leak cleanup in Settings.
- Redundancy: delete unused MCPPanel.vue; drop dead exports / dead rootPath
  watch / dead setup base-URL code; tray left-click now restores a minimized
  window.

Verified: vue-tsc, vite build, go build/vet/test (handler+web), cargo check,
oxlint all clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR adds pending-approval persistence and reconnect reconciliation (backend webPendingApproval struct, GET /api/approval/pending endpoint, frontend reconcileApprovals store action), introduces a boot-time connection-error overlay with retry in App.vue, hardens workspace switching with inline error display and SSH path blocking, and delivers a batch of accessibility, macOS drag-strip, and UI polish improvements across ~25 components and composables.

Changes

Approval Reconciliation Workflow

Layer / File(s) Summary
Backend pending-approval storage and API endpoint
internal/handler/web.go, internal/web/server.go
Introduces webPendingApproval struct to co-store channel and WebApprovalRequestData; updates RequestApproval/ResolveApproval; adds PendingApprovalRequests() method and GET /api/approval/pending HTTP handler.
Frontend API method, type extension, and store reconciliation
web/src/composables/api.ts, web/src/types/api.ts, web/src/stores/chat.ts
Adds api.approvalPending(), extends PendingApproval with resolving?, introduces reconcileApprovals action (deduplication + re-attachment on load/restore), hardens resolveApproval with re-entry guard and error recovery, and fixes recursive agentDone for subagent children.
ApprovalBanner busy/disabled state
web/src/components/ApprovalBanner.vue
Disables all three decision buttons and applies .approval-busy visual dimming while approval.resolving is true.

Boot Flow, Connection Error, and Workspace Switching

Layer / File(s) Summary
App boot sequence and connection-error overlay
web/src/App.vue
Adds booting/connectionError state, loadWorkspaceState(), and an async boot() that branches into error overlay, setup wizard, or normal startup; onMounted awaits boot(); renders a connection-error card with Retry button.
Workspace picker error handling and project SSH guard
web/src/components/WorkspacePicker.vue, web/src/components/ProjectSwitcher.vue, web/src/stores/project.ts, web/src/composables/useDesktop.ts
WorkspacePicker tracks switchErr, keeps picker open on failure, wraps native picker in try/catch; ProjectSwitcher adds openFolderAction with native/fallback; project.ts blocks ssh:// paths; pickFolder now throws instead of returning null.
CommandPalette remote-task routing and keyboard scroll
web/src/components/CommandPalette.vue
Rewrites openTask with re-entrancy guard and remote-path branching via parseRemoteLabel + injected openRemoteConnect; keyboard navigation auto-scrolls highlighted item.

Desktop Polish and UI Component Improvements

Layer / File(s) Summary
macOS drag strips, splash page, and window minimize fix
desktop/splash/index.html, desktop/src-tauri/src/main.rs, web/src/components/RemoteConnectWizard.vue, web/src/components/SetupView.vue, web/src/style.css, .gitignore
Adds .drag-strip elements for macOS title-bar dragging; fixes toggle_main to restore minimized windows; adds Tauri macOS padding-top for setup overlay; ARIA-enhances splash status region.
SetupView retryable loaders and nav state clearing
web/src/components/SetupView.vue
Refactors provider/model loading into loadProviders/loadModels with retry buttons; selectProvider clears API-key fields and awaits model load; watch([apiKey, baseURL]) clears stale validation feedback.
SettingsDialog per-server MCP login messages and channel polling cleanup
web/src/components/SettingsDialog.vue, web/src/components/MCPPanel.vue
Scopes OAuth status per server via mcpLoginMessageFor; refactors channel polling with stopChannelPoll() and 3-minute safety cap; deletes MCPPanel.vue.
Sidebar inline task rename and archive toggle
web/src/components/Sidebar.vue
Replaces window.prompt rename with inline input state machine using nextTick focus/select; adds archive-tasks toggle button; introduces .task-rename CSS.
ChatInput session/image resets and FileTreePanel error states
web/src/components/ChatInput.vue, web/src/components/FileTreePanel.vue, web/src/components/RightPanel.vue
ChatInput clears draft/menus on session change and drops images when unsupported; FileTreePanel adds dirError/fileError error banners; RightPanel adds :key="store.pwd" to force panel remount on workspace change.
Miscellaneous component and composable fixes
web/src/components/AskUserCard.vue, web/src/components/GoalBanner.vue, web/src/components/DiffViewer.vue, web/src/components/TerminalPanel.vue, web/src/components/TopBar.vue, web/src/components/ToolCallCard.vue, web/src/composables/notifications.ts
Idempotent AskUserCard select + pending dot + digit-hint cap; GoalBanner readable status labels; DiffViewer selection persistence on mode change; TerminalPanel valid button nesting and keyboard focus; TopBar consistent statusLabel/ARIA; ToolCallCard data-selectable attribute; notifications.ts drops supported/permission.

Sequence Diagram(s)

sequenceDiagram
  participant onMounted as App onMounted
  participant boot
  participant fetchHealth
  participant loadWorkspaceState
  participant reconcileApprovals as chat.reconcileApprovals
  participant approvalPending as GET /api/approval/pending
  participant ApprovalBanner

  onMounted->>boot: await boot()
  boot->>fetchHealth: probe /api/health
  alt connection error
    boot-->>onMounted: show connectionError overlay
  else needs_setup
    boot-->>onMounted: show SetupView
  else ok
    boot->>loadWorkspaceState: load config/models/channels
    boot->>reconcileApprovals: on session restore/load
    reconcileApprovals->>approvalPending: GET /api/approval/pending
    approvalPending-->>reconcileApprovals: []ApprovalRequestData
    reconcileApprovals-->>ApprovalBanner: inject missing approval cards
  end

  Note over ApprovalBanner: approval.resolving=true disables buttons
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • cnjack/jcode#76: Directly modifies internal/handler/web.go's ResolveApproval approval semantics, which this PR also restructures by changing the pending-approval storage and resolution path.

Poem

🐰 Hop, hop through the reconnect maze,
Approvals remembered through connection haze,
The window won't hide when it's just minimized,
Drag strips for macOS — how civilized!
Inline rename, error cards, retry with glee —
A tidy codebase, as it should be!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: addressing UI audit findings across the native shell, bridge, and web UI components with fixes and improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/desktop-ui-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cnjack cnjack merged commit bf9d745 into main Jun 21, 2026
2 of 3 checks passed
@cnjack cnjack deleted the fix/desktop-ui-audit branch June 21, 2026 04:28
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.

1 participant