Make rebase abort buttons non-destructive

- Keep merge abort actions styled as destructive
- Make rebase abort match the quieter conflict review treatment
This commit is contained in:
Jinjing
2026-05-31 21:26:29 -07:00
parent 65b83327f6
commit a8fb7533aa
2 changed files with 47 additions and 2 deletions
@@ -65,6 +65,15 @@ function firstButton(markup: string): string {
return match[0]
}
function buttonContaining(markup: string, label: string): string {
const buttons = markup.match(/<button\b[\s\S]*?<\/button>/g) ?? []
const button = buttons.find((candidate) => candidate.includes(label))
if (!button) {
throw new Error(`button not found: ${label}`)
}
return button
}
function textarea(markup: string): string {
const match = markup.match(/<textarea\b[\s\S]*?<\/textarea>/)
if (!match) {
@@ -374,6 +383,22 @@ describe('ConflictSummaryCard', () => {
expect(cherryPickMarkup).not.toContain('Abort rebase')
})
it('renders rebase abort with the quiet review-conflicts button treatment', () => {
const markup = renderToStaticMarkup(
<ConflictSummaryCard
conflictOperation="rebase"
unresolvedCount={1}
isResolvingWithAI={false}
onAbortOperation={vi.fn()}
onResolveWithAI={vi.fn()}
onReview={vi.fn()}
/>
)
expect(buttonContaining(markup, 'Review conflicts')).toContain('data-variant="ghost"')
expect(buttonContaining(markup, 'Abort rebase')).toContain('data-variant="ghost"')
})
it('renders the Sparkles icon on the idle Resolve with AI button', () => {
const markup = renderToStaticMarkup(
<ConflictSummaryCard
@@ -408,4 +433,16 @@ describe('OperationBanner', () => {
expect(cherryPickMarkup).not.toContain('Abort merge')
expect(cherryPickMarkup).not.toContain('Abort rebase')
})
it('keeps rebase abort non-destructive while preserving merge abort styling', () => {
const mergeMarkup = renderToStaticMarkup(
<OperationBanner conflictOperation="merge" onAbortOperation={vi.fn()} />
)
const rebaseMarkup = renderToStaticMarkup(
<OperationBanner conflictOperation="rebase" onAbortOperation={vi.fn()} />
)
expect(buttonContaining(mergeMarkup, 'Abort merge')).toContain('data-variant="destructive"')
expect(buttonContaining(rebaseMarkup, 'Abort rebase')).toContain('data-variant="ghost"')
})
})
@@ -6341,6 +6341,14 @@ function DiffCommentsInlineList({
)
}
function conflictAbortButtonVariant(
conflictOperation: GitConflictOperation
): 'ghost' | 'destructive' {
// Why: aborting a rebase is the escape hatch for this state, so it should
// match the quiet conflict-review action instead of reading as a red danger path.
return conflictOperation === 'rebase' ? 'ghost' : 'destructive'
}
export function ConflictSummaryCard({
conflictOperation,
unresolvedCount,
@@ -6410,7 +6418,7 @@ export function ConflictSummaryCard({
{(conflictOperation === 'merge' || conflictOperation === 'rebase') && onAbortOperation ? (
<Button
type="button"
variant="destructive"
variant={conflictAbortButtonVariant(conflictOperation)}
size="sm"
className="mt-1.5 h-7 w-full text-xs"
disabled={isResolvingWithAI || isAbortingOperation}
@@ -6459,7 +6467,7 @@ export function OperationBanner({
{(conflictOperation === 'merge' || conflictOperation === 'rebase') && onAbortOperation ? (
<Button
type="button"
variant="destructive"
variant={conflictAbortButtonVariant(conflictOperation)}
size="sm"
className="mt-2 h-7 w-full text-xs"
disabled={isAbortingOperation}