style: change overall look#8
Conversation
| onCreateScript={onCreateScript} | ||
| onOpenSshPassphrase={onOpenSshPassphrase} | ||
| onToggleTheme={onToggleTheme} | ||
| onOpenSettings={onOpenSettings} |
There was a problem hiding this comment.
🟡 Medium sidebar/index.tsx:41
Tapping Settings from the mobile sidebar leaves the sidebar open, so SettingsPage renders behind it with its content squeezed or off-screen until the user manually closes the sidebar. onOpenSettings only switches the workspace view; nothing clears isMobileSidebarOpen, and SidebarShell stays visible on small screens whenever that flag is true. Consider closing the mobile sidebar when settings opens, or clearing it inside onOpenSettings in PrRunApp.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/lib/components/templates/sidebar/index.tsx around line 41:
Tapping **Settings** from the mobile sidebar leaves the sidebar open, so `SettingsPage` renders behind it with its content squeezed or off-screen until the user manually closes the sidebar. `onOpenSettings` only switches the workspace view; nothing clears `isMobileSidebarOpen`, and `SidebarShell` stays visible on small screens whenever that flag is true. Consider closing the mobile sidebar when settings opens, or clearing it inside `onOpenSettings` in `PrRunApp`.
| getBackendUrl().then(setBackendUrl); | ||
| }, []); | ||
|
|
||
| async function refreshAll() { |
There was a problem hiding this comment.
🟡 Medium settings-page/general-settings.tsx:27
A failed project refresh from the "Refresh all projects" button completes silently with no toast or visible error feedback. refreshAll wraps onRefreshProject in tryPromise, expecting it to reject on failure, but the supplied updateProject catches its own errors and stores them in actionError instead of rejecting — so tryPromise always sees success and the break/toast.error branch is never reached. Since MainPanel (which surfaces actionError) is not rendered while the settings page is open, the error is hidden. Either have onRefreshProject reject on failure so tryPromise can catch it, or surface the error from within refreshAll directly.
Also found in 1 other location(s)
src/lib/components/templates/settings-page/projects-settings.tsx:48
ProjectsSettingscallsonRefreshProject(project)directly from the buttononClickwithout any local error handling. In this screen,onRefreshProjectisstate.updateProject, which catches backend failures and only stores them inactionError. That error is rendered byMainPanel, but the settings view replacesMainPanelentirely, so a refresh failure from this button produces no toast or inline error and appears to do nothing.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/lib/components/templates/settings-page/general-settings.tsx around line 27:
A failed project refresh from the "Refresh all projects" button completes silently with no toast or visible error feedback. `refreshAll` wraps `onRefreshProject` in `tryPromise`, expecting it to reject on failure, but the supplied `updateProject` catches its own errors and stores them in `actionError` instead of rejecting — so `tryPromise` always sees success and the `break`/`toast.error` branch is never reached. Since `MainPanel` (which surfaces `actionError`) is not rendered while the settings page is open, the error is hidden. Either have `onRefreshProject` reject on failure so `tryPromise` can catch it, or surface the error from within `refreshAll` directly.
Also found in 1 other location(s):
- src/lib/components/templates/settings-page/projects-settings.tsx:48 -- `ProjectsSettings` calls `onRefreshProject(project)` directly from the button `onClick` without any local error handling. In this screen, `onRefreshProject` is `state.updateProject`, which catches backend failures and only stores them in `actionError`. That error is rendered by `MainPanel`, but the settings view replaces `MainPanel` entirely, so a refresh failure from this button produces no toast or inline error and appears to do nothing.
| }; | ||
| } | ||
|
|
||
| function historicalPullRequestBranch( |
There was a problem hiding this comment.
🟡 Medium git/pull-request-branches.ts:146
historicalPullRequestBranch sets source: "pull-request" for closed and merged PRs, so useAppStatusSummary counts them in openPullRequestCount along with genuinely open PRs. This inflates the open PR total and badge, reporting closed/merged PRs as open. The source field alone cannot distinguish open from historical PRs; consider keying the open-PR count off pullRequest.state === "OPEN" instead.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/backend/handlers/git/pull-request-branches.ts around line 146:
`historicalPullRequestBranch` sets `source: "pull-request"` for closed and merged PRs, so `useAppStatusSummary` counts them in `openPullRequestCount` along with genuinely open PRs. This inflates the open PR total and badge, reporting closed/merged PRs as open. The `source` field alone cannot distinguish open from historical PRs; consider keying the open-PR count off `pullRequest.state === "OPEN"` instead.
| return; | ||
| } | ||
|
|
||
| onChangeDraft({ |
There was a problem hiding this comment.
🟡 Medium changes/focused-diff.tsx:154
The "Add line comment" gutter button stores line.side directly into the DiffCommentDraft without defaulting, but hovered lines can have side: undefined. When the draft is later submitted, toGitHubSide(undefined) maps to LEFT, so a comment started from an addition-side line with no explicit side is submitted against the wrong diff side. Apply the same ?? "additions" fallback used in selectRange for endSide and startSide.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/lib/components/templates/main-panel/changes/focused-diff.tsx around line 154:
The "Add line comment" gutter button stores `line.side` directly into the `DiffCommentDraft` without defaulting, but hovered lines can have `side: undefined`. When the draft is later submitted, `toGitHubSide(undefined)` maps to `LEFT`, so a comment started from an addition-side line with no explicit `side` is submitted against the wrong diff side. Apply the same `?? "additions"` fallback used in `selectRange` for `endSide` and `startSide`.
There was a problem hiding this comment.
🟡 Medium
pr-run/src/backend/handlers/git/diff.ts
Line 99 in d045580
getBranchDiffFiles silently drops pure renames and mode-only changes from the returned file list. Git omits these from --numstat, so mergeDiffFileMetadata only iterates over --numstat entries and never includes files that appear only in --name-status, causing the diff view to miss those changed files entirely. Consider seeding the merged result with --name-status entries that have no matching --numstat line, so renames and mode-only changes are represented.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/backend/handlers/git/diff.ts around line 99:
`getBranchDiffFiles` silently drops pure renames and mode-only changes from the returned file list. Git omits these from `--numstat`, so `mergeDiffFileMetadata` only iterates over `--numstat` entries and never includes files that appear only in `--name-status`, causing the diff view to miss those changed files entirely. Consider seeding the merged result with `--name-status` entries that have no matching `--numstat` line, so renames and mode-only changes are represented.
| if (event.key === "Tab") { | ||
| event.preventDefault(); | ||
|
|
||
| if (!areWorkspaceShortcutsEnabled) { | ||
| return; | ||
| } | ||
|
|
||
| const nextTabId = cycleWorkspaceTabs( |
There was a problem hiding this comment.
🟡 Medium workspace-titlebar/index.tsx:51
Ctrl/Cmd+Tab is always swallowed via event.preventDefault() even when areWorkspaceShortcutsEnabled is false, so on screens where workspace shortcuts are disabled the browser's default tab navigation is blocked and no replacement action runs. Move event.preventDefault() after the areWorkspaceShortcutsEnabled check so default navigation is preserved when shortcuts are off.
| if (event.key === "Tab") { | |
| event.preventDefault(); | |
| if (!areWorkspaceShortcutsEnabled) { | |
| return; | |
| } | |
| const nextTabId = cycleWorkspaceTabs( | |
| if (event.key === "Tab") { | |
| if (!areWorkspaceShortcutsEnabled) { | |
| return; | |
| } | |
| event.preventDefault(); | |
| const nextTabId = cycleWorkspaceTabs( |
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/lib/components/templates/workspace-titlebar/index.tsx around lines 51-58:
`Ctrl/Cmd+Tab` is always swallowed via `event.preventDefault()` even when `areWorkspaceShortcutsEnabled` is `false`, so on screens where workspace shortcuts are disabled the browser's default tab navigation is blocked and no replacement action runs. Move `event.preventDefault()` after the `areWorkspaceShortcutsEnabled` check so default navigation is preserved when shortcuts are off.


Use base ui, removes heroui
Note
Redesign the workspace UI with a new titlebar, sidebar, activity feed, diff viewer, and overview page
@heroui/reactwith Base UI (@base-ui/react) and a newsrc/lib/components/ui/layer (Button, Dialog, Input, Toast, ScrollArea, Tooltip, Select, etc.), removing allonPress/isDisabledprop patterns in favour of standardonClick/disabled.WorkspaceTitlebarwith a draggable tab strip (WorktreeTabs), keyboard shortcuts (Cmd+Tab to cycle, Cmd+W to close), and a sidebar toggle; tab state persists tolocalStorageviauseWorkspaceTabsStore.assignProjectAvatars), and PR-aware branch items that show author avatars, status pills (Draft/Open/Closed/Merged), and a worktree-removal confirmation dialog.useWorktreeActivityQuery.GET /overviewbackend endpoint.listGitHubPullRequestsnow returns both open and closed PRs with enriched fields.useUiPreferencesStoreto persist sidebar width, terminal panel height, terminal list width, and theme; theme supports a'system'mode that follows the OS and syncs to the native Electron title bar via a newwindow:setTitleBarThemeIPC handler.onSelectBranch,onUpdateProject, group toggle callbacks) and tab identifiers ('general'→'activity','diff'→'changes') are breaking changes for any consumers of these components.Macroscope summarized d045580.