mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 08:03:13 +00:00
* Fix view full details button to use correct worktree and sticky header - Pin a sticky bar with check name and "View full logs" button inside expanded check details so the affordance stays visible while scrolling through annotations and job output - Accept an explicit `worktreeId` prop so folder-workspace PR checks use the child worktree ID rather than the (non-existent) active worktree - Show "View full logs" when log tail is available, "View full details" otherwise Co-authored-by: Orca <help@stably.ai> * fix: address review findings * fix: address CI failures --------- Co-authored-by: Orca <help@stably.ai>
172 lines
5.5 KiB
TypeScript
172 lines
5.5 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { ChevronDown, RefreshCw, SlidersHorizontal, Sparkle } from 'lucide-react'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger
|
|
} from '@/components/ui/dropdown-menu'
|
|
import { cn } from '@/lib/utils'
|
|
import type {
|
|
SourceControlActionRecipe,
|
|
SourceControlLaunchActionId
|
|
} from '../../../../shared/source-control-ai-actions'
|
|
import type { TuiAgent } from '../../../../shared/types'
|
|
import type { LaunchSource } from '../../../../shared/telemetry-events'
|
|
import type { SourceControlAiWriteTarget } from '../../../../shared/source-control-ai-recipe-save'
|
|
import { translate } from '@/i18n/i18n'
|
|
import { SourceControlAgentActionDialog } from './SourceControlAgentActionDialog'
|
|
|
|
export type SourceControlFixSplitButtonProps = {
|
|
label: string
|
|
actionId: SourceControlLaunchActionId
|
|
dialogTitle: string
|
|
dialogDescription: string
|
|
launchSource: LaunchSource
|
|
contextUnavailableLabel: string
|
|
primaryTitle: string
|
|
primaryAriaLabel: string
|
|
chevronTitle: string
|
|
chevronAriaLabel: string
|
|
worktreeId: string | null
|
|
groupId: string | null
|
|
connectionId?: string | null
|
|
repoId?: string | null
|
|
launchPlatform?: NodeJS.Platform
|
|
prompt: string | null
|
|
isLaunching: boolean
|
|
disabledReason?: string
|
|
variant: React.ComponentProps<typeof Button>['variant']
|
|
size: React.ComponentProps<typeof Button>['size']
|
|
iconClassName: string
|
|
primaryClassName?: string
|
|
chevronClassName?: string
|
|
savedAgentId?: TuiAgent | null
|
|
savedCommandInputTemplate?: string | null
|
|
savedAgentArgs?: string | null
|
|
onSaveAgentDefault?: (
|
|
target: SourceControlAiWriteTarget,
|
|
actionId: SourceControlLaunchActionId,
|
|
recipe: SourceControlActionRecipe
|
|
) => void | Promise<void>
|
|
onOpenSettings?: () => void
|
|
onFixWithDefaultAgent: (promptOverride?: string) => Promise<boolean> | boolean
|
|
onPromptDelivered?: () => void
|
|
}
|
|
|
|
export function SourceControlFixSplitButton({
|
|
label,
|
|
actionId,
|
|
dialogTitle,
|
|
dialogDescription,
|
|
launchSource,
|
|
contextUnavailableLabel,
|
|
primaryTitle,
|
|
primaryAriaLabel,
|
|
chevronTitle,
|
|
chevronAriaLabel,
|
|
worktreeId,
|
|
groupId,
|
|
connectionId,
|
|
repoId,
|
|
launchPlatform,
|
|
prompt,
|
|
isLaunching,
|
|
disabledReason,
|
|
variant,
|
|
size,
|
|
iconClassName,
|
|
primaryClassName,
|
|
chevronClassName,
|
|
savedAgentId,
|
|
savedCommandInputTemplate,
|
|
savedAgentArgs,
|
|
onSaveAgentDefault,
|
|
onOpenSettings,
|
|
onFixWithDefaultAgent,
|
|
onPromptDelivered
|
|
}: SourceControlFixSplitButtonProps): React.JSX.Element {
|
|
const [composerOpen, setComposerOpen] = useState(false)
|
|
const canLaunch = Boolean(worktreeId && groupId && prompt && !disabledReason)
|
|
const dividerClass = variant === 'default' ? 'border-primary-foreground/20' : 'border-border'
|
|
|
|
return (
|
|
<>
|
|
<DropdownMenu>
|
|
<div className="flex shrink-0 items-stretch">
|
|
<Button
|
|
type="button"
|
|
variant={variant}
|
|
size={size}
|
|
className={cn('rounded-r-none', primaryClassName)}
|
|
disabled={isLaunching || !canLaunch}
|
|
title={disabledReason ?? primaryTitle}
|
|
aria-label={primaryAriaLabel}
|
|
onClick={() => void onFixWithDefaultAgent()}
|
|
>
|
|
{isLaunching ? (
|
|
<RefreshCw className={cn(iconClassName, 'animate-spin')} />
|
|
) : (
|
|
<Sparkle className={iconClassName} />
|
|
)}
|
|
{label}
|
|
</Button>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
type="button"
|
|
variant={variant}
|
|
size={size}
|
|
className={cn('rounded-l-none border-l', dividerClass, chevronClassName)}
|
|
disabled={isLaunching || !canLaunch}
|
|
title={chevronTitle}
|
|
aria-label={chevronAriaLabel}
|
|
>
|
|
<ChevronDown className={iconClassName} />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
</div>
|
|
<DropdownMenuContent align="end" className="min-w-[210px] p-1">
|
|
{worktreeId && groupId && prompt && !disabledReason ? (
|
|
<DropdownMenuItem
|
|
onSelect={() => setComposerOpen(true)}
|
|
className="gap-2 rounded-[7px] px-2 py-1.5 text-[12px] leading-5 font-medium"
|
|
>
|
|
<SlidersHorizontal className="size-4 text-muted-foreground" />
|
|
{translate(
|
|
'auto.components.right.sidebar.SourceControl.f0a2dc9e46',
|
|
'Customize launch...'
|
|
)}
|
|
</DropdownMenuItem>
|
|
) : (
|
|
<DropdownMenuItem disabled>{contextUnavailableLabel}</DropdownMenuItem>
|
|
)}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
{worktreeId && groupId && prompt ? (
|
|
<SourceControlAgentActionDialog
|
|
open={composerOpen}
|
|
onOpenChange={setComposerOpen}
|
|
actionId={actionId}
|
|
title={dialogTitle}
|
|
description={dialogDescription}
|
|
baseCommandInput={prompt}
|
|
worktreeId={worktreeId}
|
|
groupId={groupId}
|
|
connectionId={connectionId}
|
|
repoId={repoId}
|
|
promptDelivery="submit-after-ready"
|
|
launchPlatform={launchPlatform}
|
|
launchSource={launchSource}
|
|
savedAgentId={savedAgentId}
|
|
savedCommandInputTemplate={savedCommandInputTemplate}
|
|
savedAgentArgs={savedAgentArgs}
|
|
onSaveAgentDefault={onSaveAgentDefault}
|
|
onOpenSettings={onOpenSettings}
|
|
onLaunched={onPromptDelivered}
|
|
/>
|
|
) : null}
|
|
</>
|
|
)
|
|
}
|