Skip to content

Comments

refactor(sidebar): restyle left sidebar layout and alignment#1027

Open
leondri wants to merge 3 commits intogeneralaction:mainfrom
leondri:main
Open

refactor(sidebar): restyle left sidebar layout and alignment#1027
leondri wants to merge 3 commits intogeneralaction:mainfrom
leondri:main

Conversation

@leondri
Copy link
Contributor

@leondri leondri commented Feb 21, 2026

Screenshot 2026-02-21 at 16 56 33

Restyled sidebar using the same backend functionality:

  • Indented tasks for better visibility
  • Added Agents as secondary description
  • Added date (e.g. "1d") description

@vercel
Copy link

vercel bot commented Feb 21, 2026

@leondri is attempting to deploy a commit to the General Action Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link

greptile-apps bot commented Feb 21, 2026

Greptile Summary

This PR refactors the left sidebar to improve visual hierarchy and information density. Tasks are now indented under projects, agent names appear as secondary descriptions, and compact relative dates (e.g., "1d") are shown. The "Add Project" button moved to the Projects header, and archived tasks functionality was removed from the sidebar view.

Key changes:

  • Tasks display agent names below task name (via new useTaskAgentNames hook)
  • Tasks show compact relative dates when no changes/PR exists
  • Projects use folder icons (open/closed) instead of chevrons
  • Archive button appears on task hover instead of delete button
  • Removed archived tasks collapsible section and project delete button from sidebar
  • Added light mode background color (gray-50) to sidebar
  • Disabled layout animation transitions for smoother experience

Issue found: Remote project indicator hardcoded to always show "disconnected" state instead of using actual connection data from useRemoteProject hook.

Confidence Score: 3/5

  • This PR has one critical logic bug that breaks remote project connection indicators
  • The remote project indicator will always show disconnected status due to hardcoded values, which could confuse users working with SSH remote projects. The rest of the refactoring is well-structured UI changes.
  • Pay close attention to src/renderer/components/LeftSidebar.tsx - the remote project indicator logic needs to be restored

Important Files Changed

Filename Overview
src/renderer/components/LeftSidebar.tsx Removed archived tasks section and project deletion UI, moved "Add Project" button to header, changed project/task styling and indentation
src/renderer/components/TaskItem.tsx Restructured task display to show agent names as secondary line, added compact date formatting, changed right-side content priority (archive > changes > PR > date)
src/renderer/hooks/useTaskAgentNames.ts New hook to fetch and display agent names from task conversations, handles multi-agent tasks with "+N" notation

Last reviewed commit: a2297a7

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

7 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 354 to +360
>
<span className="block w-full truncate">
<ProjectItem
project={typedProject}
isActive={isProjectActive}
onSelect={() => onSelectProject(typedProject)}
/>
</span>
<span className="hidden w-full truncate text-xs text-muted-foreground sm:block">
{typedProject.githubInfo?.repository || typedProject.path}
</span>
</motion.button>
<div className="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-0.5">
{showProjectDelete ? (
<ProjectDeleteButton
projectName={typedProject.name}
tasks={typedProject.tasks || []}
onConfirm={() => handleDeleteProject(typedProject)}
isDeleting={isDeletingProject}
aria-label={`Delete project ${typedProject.name}`}
className={`bg-accent text-muted-foreground ${
isDeletingProject
? ''
: 'opacity-0 group-hover/project:opacity-100'
}`}
/>
) : null}
<CollapsibleTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
aria-label={`Toggle tasks for ${typedProject.name}`}
onClick={(e) => e.stopPropagation()}
className="text-muted-foreground opacity-0 group-hover/project:opacity-100 group-data-[state=open]/collapsible:opacity-100"
{typedProject.name}
</button>
{projectIsRemote && connectionId && (
<RemoteProjectIndicator
host={undefined}
connectionState={'disconnected' as ConnectionState}
Copy link

Choose a reason for hiding this comment

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

hardcoded host={undefined} and connectionState={'disconnected' as ConnectionState} instead of using actual remote project data

The useRemoteProject hook was removed but the remote project indicator still displays. These hardcoded values mean remote projects will always show as disconnected.

Suggested change
>
<span className="block w-full truncate">
<ProjectItem
project={typedProject}
isActive={isProjectActive}
onSelect={() => onSelectProject(typedProject)}
/>
</span>
<span className="hidden w-full truncate text-xs text-muted-foreground sm:block">
{typedProject.githubInfo?.repository || typedProject.path}
</span>
</motion.button>
<div className="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-0.5">
{showProjectDelete ? (
<ProjectDeleteButton
projectName={typedProject.name}
tasks={typedProject.tasks || []}
onConfirm={() => handleDeleteProject(typedProject)}
isDeleting={isDeletingProject}
aria-label={`Delete project ${typedProject.name}`}
className={`bg-accent text-muted-foreground ${
isDeletingProject
? ''
: 'opacity-0 group-hover/project:opacity-100'
}`}
/>
) : null}
<CollapsibleTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
aria-label={`Toggle tasks for ${typedProject.name}`}
onClick={(e) => e.stopPropagation()}
className="text-muted-foreground opacity-0 group-hover/project:opacity-100 group-data-[state=open]/collapsible:opacity-100"
{typedProject.name}
</button>
{projectIsRemote && connectionId && (
<RemoteProjectIndicator
host={undefined}
connectionState={'disconnected' as ConnectionState}
{projectIsRemote && connectionId && (() => {
const remote = useRemoteProject(typedProject);
return (
<RemoteProjectIndicator
host={remote.host || undefined}
connectionState={remote.connectionState as ConnectionState}
size="md"
onReconnect={remote.reconnect}
disabled={remote.isLoading}
/>
);
})()}

@arnestrickmann
Copy link
Contributor

Thanks! @leondri, will review the PR and give feedback.

Copy link
Contributor

@arnestrickmann arnestrickmann left a comment

Choose a reason for hiding this comment

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

Review Summary

Nice UI improvements — the two-line task layout (name + agent), compact dates, and the new bulk-action task list in ProjectMainView are solid. The visual hierarchy is clearly better.

However, there are blocking regressions that remove or break existing functionality. These must be fixed before merge:

Blocking Issues

  1. Remote project indicator hardcoded to "disconnected" — SSH remote projects will always show disconnected status regardless of actual connection state. The useRemoteProject hook was removed but the indicator still renders with hardcoded values. This is a functional regression.

  2. Task delete removed from sidebaronDeleteTask is accepted as a prop but underscore-prefixed and never used. TaskItem also underscore-prefixes onDelete/showDelete internally. Users can no longer delete tasks from the sidebar — only archive. The delete functionality should be preserved (e.g. in the context menu) even if archive-on-hover is the primary action.

  3. Project delete removed from sidebaronDeleteProject is accepted but never wired up. Previously there was a delete button on project hover. This feature should remain accessible.

Non-blocking but worth addressing

  1. useTaskAgentNames fires one IPC call per task — For 20+ tasks this means 20+ getConversations() IPC round-trips on render. Consider batching or caching.

  2. useTaskAgentNames shows chat count, not unique provider count — "Claude +4" when there are 5 Claude chats but only 1 unique provider is misleading.

  3. Filter state doesn't reset on project change — showFilter and searchFilter persist when switching projects.

  4. Hardcoded bg-gray-50 for sidebar light mode — Uses a fixed Tailwind gray instead of semantic theme tokens.

  5. Project name at 60% opacity + cursor-default — Makes projects look disabled/non-interactive.

connectionState={'disconnected' as ConnectionState}
size="md"
disabled
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

Blocking: RemoteProjectIndicator is hardcoded to always show disconnected with host={undefined}.

The useRemoteProject hook was removed but the indicator still renders — remote projects will always appear disconnected regardless of actual state. The onReconnect callback is also gone.

Since hooks can't be called inside a render callback, the fix is to extract this into a small child component that calls useRemoteProject internally:

const SidebarRemoteIndicator: React.FC<{ project: Project }> = ({ project }) => {
  const remote = useRemoteProject(project);
  return (
    <RemoteProjectIndicator
      host={remote.host || undefined}
      connectionState={remote.connectionState as ConnectionState}
      size="md"
      onReconnect={remote.reconnect}
      disabled={remote.isLoading}
    />
  );
};

Then use <SidebarRemoteIndicator project={typedProject} /> here.

onSidebarContextChange,
onCreateTaskForProject,
onDeleteTask,
onDeleteTask: _onDeleteTask,
Copy link
Contributor

Choose a reason for hiding this comment

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

Blocking: onDeleteTask and onDeleteProject (lines 133, 137) are accepted as props but underscore-prefixed and never used. This means:

  • Users can no longer delete tasks from the sidebar
  • Users can no longer delete projects from the sidebar

These are feature regressions. Even if the primary sidebar action becomes archive-on-hover, delete should still be accessible — e.g. through the existing TaskItem context menu, or as a secondary hover action.

export const TaskItem: React.FC<TaskItemProps> = ({
task,
onDelete,
onDelete: _onDelete,
Copy link
Contributor

Choose a reason for hiding this comment

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

Blocking: onDelete is renamed to _onDelete and never used (same for showDelete_showDelete on line 70). The delete button was completely removed from TaskItem.

The context menu still has archive + pin + rename — onDelete should be wired back in there as a context menu option, even if the hover action is now archive instead of delete.


async function load() {
try {
const res = await window.electronAPI.getConversations(taskId);
Copy link
Contributor

Choose a reason for hiding this comment

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

Performance: This hook fires getConversations(taskId) per task on mount. With 20 tasks visible, that's 20 IPC round-trips.

Consider either:

  • A batch API that fetches agent info for all tasks in one call
  • A simple in-memory cache with short TTL
  • Lifting the fetch to the parent list component and passing results down as props

additionalCount > 0 ? `${primaryName} +${additionalCount}` : primaryName;

setInfo({ primaryName, additionalCount, displayLabel, providerIds });
} catch {
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: additionalCount is totalChats - 1 (total conversations minus 1), not unique providers minus 1. If a task has 5 Claude conversations, this shows "Claude +4" — but there's really only 1 unique provider.

Should this be providerIds.length - 1 instead of totalChats - 1? Or if showing chat count is intentional, the label should clarify (e.g. "Claude (5 chats)").

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