Skip to content

style: change overall look#8

Merged
felipe-software merged 14 commits into
mainfrom
style/ui-overhaul
Jul 10, 2026
Merged

style: change overall look#8
felipe-software merged 14 commits into
mainfrom
style/ui-overhaul

Conversation

@felipe-software

@felipe-software felipe-software commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Use base ui, removes heroui

Note

Redesign the workspace UI with a new titlebar, sidebar, activity feed, diff viewer, and overview page

  • Replaces HeroUI/@heroui/react with Base UI (@base-ui/react) and a new src/lib/components/ui/ layer (Button, Dialog, Input, Toast, ScrollArea, Tooltip, Select, etc.), removing all onPress/isDisabled prop patterns in favour of standard onClick/disabled.
  • Adds a WorkspaceTitlebar with a draggable tab strip (WorktreeTabs), keyboard shortcuts (Cmd+Tab to cycle, Cmd+W to close), and a sidebar toggle; tab state persists to localStorage via useWorkspaceTabsStore.
  • Replaces the old sidebar with always-expanded group sections, an Overview button, a Settings footer, project avatars (assignProjectAvatars), and PR-aware branch items that show author avatars, status pills (Draft/Open/Closed/Merged), and a worktree-removal confirmation dialog.
  • Adds a Changes tab (continuous and focused diff modes, file picker, inline review comments) and an Activity tab (commits with stats, PR comments/reviews rendered as GitHub-flavored markdown) in the main panel; commit history data is replaced by useWorktreeActivityQuery.
  • Adds an Overview page showing aggregate additions/deletions, open PRs, and worktrees across projects, backed by a new GET /overview backend endpoint.
  • Adds a Settings page with sections for General, Appearance (system/dark/light theme), Projects, Scripts, SSH, and Diagnostics.
  • Extends the backend with new routes for worktree activity, PR comments/reviews (add, submit, discard), package scripts catalog, and terminal command preparation; listGitHubPullRequests now returns both open and closed PRs with enriched fields.
  • Introduces useUiPreferencesStore to 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 new window:setTitleBarTheme IPC handler.
  • Risk: the sidebar prop contract (onSelectBranch, onUpdateProject, group toggle callbacks) and tab identifiers ('general'→'activity', 'diff'→'changes') are breaking changes for any consumers of these components.

Macroscope summarized d045580.

Comment thread src/lib/components/templates/global-terminal-panel.tsx Outdated
Comment thread src/lib/components/templates/pr-run-app/index.tsx Outdated
Comment thread src/lib/components/templates/sidebar/sidebar-branch-item.tsx
Comment thread src/lib/components/ui/sidebar.tsx
Comment thread src/lib/components/templates/settings-page/scripts-settings.tsx Outdated
onCreateScript={onCreateScript}
onOpenSshPassphrase={onOpenSshPassphrase}
onToggleTheme={onToggleTheme}
onOpenSettings={onOpenSettings}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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`.

Comment thread src/lib/hooks/store/use-ui-preferences-store.ts
getBackendUrl().then(setBackendUrl);
}, []);

async function refreshAll() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

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.

🚀 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.

Comment thread src/lib/components/ui/button.tsx Outdated
Comment thread src/lib/components/templates/overview/index.tsx
Comment thread src/backend/handlers/git/overview.ts Outdated
@felipe-software

Copy link
Copy Markdown
Owner Author
image

Comment thread src/lib/components/templates/pr-run-app/index.tsx
Comment thread src/lib/components/templates/workspace-titlebar/index.tsx Outdated
@felipe-software

Copy link
Copy Markdown
Owner Author
image

Comment thread src/backend/handlers/scripts/package-scripts.ts Outdated
Comment thread src/backend/handlers/git/diff.ts Outdated
Comment thread src/backend/handlers/scripts/package-scripts.ts
Comment thread src/lib/components/templates/main-panel/activity/review-composer.tsx Outdated
Comment thread src/backend/handlers/git/activity.ts
Comment thread src/backend/handlers/git/github-review.ts
Comment thread src/backend/handlers/scripts/package-scripts.ts Outdated
Comment thread src/backend/handlers/git/github-activity.ts Outdated
Comment thread src/lib/components/templates/main-panel/activity/index.tsx Outdated
Comment thread src/backend/handlers/git/github-review.ts Outdated
};
}

function historicalPullRequestBranch(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Comment thread src/lib/components/templates/main-panel/branch-page-header.tsx
return;
}

onChangeDraft({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium

function mergeDiffFileMetadata(

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.

Comment on lines +51 to +58
if (event.key === "Tab") {
event.preventDefault();

if (!areWorkspaceShortcutsEnabled) {
return;
}

const nextTabId = cycleWorkspaceTabs(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Suggested change
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.

@felipe-software
felipe-software merged commit 60ef262 into main Jul 10, 2026
2 checks passed
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