feat(ui): add desktop git sidebar + terminal dock and improve in-app PR workflow (#362)

* feat: add unified dropdown with services content in header

* feat: add right Git sidebar with resizable panel

* feat: implement responsive panel auto-toggle and terminal rehydration

- Auto-close the right sidebar when width is below a threshold and auto-open it when space permits
- Auto-close the bottom terminal when height is below a threshold and auto-open it when enough space
- Apply a dedicated rehydrated streaming configuration for terminal sessions to optimize reconnect behavior

* feat: enhance PR view with status caching and annotations

* feat(ui): enable chat dispatch in PullRequestSection

* feat(TerminalView): adjust layout

* feat: refine chat input layout and text selection menu

* fix(ui): show empty state in GitView when no changes

* feat(git): update PR actions styling and create PR button
This commit is contained in:
Bohdan Triapitsyn
2026-02-09 03:13:34 +02:00
committed by GitHub
parent 3f29b2c6a2
commit 5b0a97d170
30 changed files with 3216 additions and 1443 deletions
@@ -24,6 +24,10 @@ interface SyncActionsProps {
onPull: (remote: GitRemote) => void;
onPush: (remote: GitRemote) => void;
disabled: boolean;
iconOnly?: boolean;
tooltipDelayMs?: number;
aheadCount?: number;
behindCount?: number;
}
export const SyncActions: React.FC<SyncActionsProps> = ({
@@ -33,6 +37,10 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
onPull,
onPush,
disabled,
iconOnly = false,
tooltipDelayMs = 1000,
aheadCount = 0,
behindCount = 0,
}) => {
const hasNoRemotes = remotes.length === 0;
const isDisabled = disabled || syncAction !== null || hasNoRemotes;
@@ -65,23 +73,34 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
loadingIcon: React.ReactNode,
label: string,
onClick: () => void,
tooltipText: string
tooltipText: string,
counter?: number
) => {
const button = (
<Button
variant="ghost"
size="sm"
className="h-8 px-2"
className={iconOnly ? 'relative h-8 w-8 px-0' : 'h-8 px-2'}
onClick={onClick}
disabled={isDisabled}
>
{syncAction === action ? loadingIcon : icon}
<span className="hidden sm:inline">{label}</span>
{!iconOnly && <span className="git-header-label">{label}</span>}
{!iconOnly && typeof counter === 'number' && counter > 0 ? (
<span className="rounded-sm bg-interactive-selection/40 px-1 text-[10px] leading-4 text-foreground tabular-nums">
{counter}
</span>
) : null}
{iconOnly && typeof counter === 'number' && counter > 0 ? (
<span className="absolute -right-1 -top-1 min-w-[1rem] rounded-full bg-interactive-selection px-1 text-[10px] leading-4 text-interactive-selection-foreground tabular-nums">
{counter}
</span>
) : null}
</Button>
);
return (
<Tooltip delayDuration={1000}>
<Tooltip delayDuration={tooltipDelayMs}>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent sideOffset={8}>{tooltipText}</TooltipContent>
</Tooltip>
@@ -94,21 +113,32 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
loadingIcon: React.ReactNode,
label: string,
onSelect: (remote: GitRemote) => void,
tooltipText: string
tooltipText: string,
counter?: number
) => {
return (
<DropdownMenu>
<Tooltip delayDuration={1000}>
<Tooltip delayDuration={tooltipDelayMs}>
<TooltipTrigger asChild>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-8 px-2"
className={iconOnly ? 'relative h-8 w-8 px-0' : 'h-8 px-2'}
disabled={isDisabled}
>
{syncAction === action ? loadingIcon : icon}
<span className="hidden sm:inline">{label}</span>
{!iconOnly && <span className="git-header-label">{label}</span>}
{!iconOnly && typeof counter === 'number' && counter > 0 ? (
<span className="rounded-sm bg-interactive-selection/40 px-1 text-[10px] leading-4 text-foreground tabular-nums">
{counter}
</span>
) : null}
{iconOnly && typeof counter === 'number' && counter > 0 ? (
<span className="absolute -right-1 -top-1 min-w-[1rem] rounded-full bg-interactive-selection px-1 text-[10px] leading-4 text-interactive-selection-foreground tabular-nums">
{counter}
</span>
) : null}
</Button>
</DropdownMenuTrigger>
</TooltipTrigger>
@@ -159,7 +189,8 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
<RiLoader4Line className="size-4 animate-spin" />,
'Pull',
onPull,
'Pull changes'
behindCount > 0 ? `Pull changes (${behindCount} behind)` : 'Pull changes',
behindCount
)
: renderButton(
'pull',
@@ -167,7 +198,8 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
<RiLoader4Line className="size-4 animate-spin" />,
'Pull',
handlePull,
'Pull changes'
behindCount > 0 ? `Pull changes (${behindCount} behind)` : 'Pull changes',
behindCount
)}
{hasMultipleRemotes
@@ -177,7 +209,8 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
<RiLoader4Line className="size-4 animate-spin" />,
'Push',
onPush,
'Push changes'
aheadCount > 0 ? `Push changes (${aheadCount} ahead)` : 'Push changes',
aheadCount
)
: renderButton(
'push',
@@ -185,7 +218,8 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
<RiLoader4Line className="size-4 animate-spin" />,
'Push',
handlePush,
'Push changes'
aheadCount > 0 ? `Push changes (${aheadCount} ahead)` : 'Push changes',
aheadCount
)}
</div>
);