fix(desktop): hide empty rejected plan previews

ExitPlanMode can be interrupted while a real plan approval preview is still rendered elsewhere. In that path the tool result had no plan input and displayed an extra empty plan card, which made plan mode look duplicated. Render the plan preview in rejected tool cards only when real plan, file path, or permission data exists.

Tested: cd desktop && bun run test PlanModePermissionDialog.test.tsx

Tested: bun run check:desktop

Tested: Computer Use dev Electron smoke via desktop/scripts/electron-dev.ts; original #869 repro no longer showed the empty '暂无计划内容' card after HMR.

Not-tested: release build post-install retest; GitHub issue remains open until release.

Confidence: high

Scope-risk: narrow
This commit is contained in:
程序员阿江(Relakkes) 2026-06-23 17:11:09 +08:00
parent 3ba8b44a23
commit 9c02e7f4eb
2 changed files with 34 additions and 8 deletions

View File

@ -189,6 +189,24 @@ describe('plan mode permission UI', () => {
expect(container.textContent).not.toContain('Tool Output')
})
it('does not render an empty plan preview for interrupted ExitPlanMode results', () => {
const { container } = render(
<ToolCallBlock
toolName="ExitPlanMode"
input={{}}
result={{
isError: true,
content: 'Tool permission request failed: AbortError',
}}
/>,
)
expect(container.textContent).toContain('Plan rejected')
expect(container.textContent).toContain('Tool permission request failed: AbortError')
expect(container.textContent).not.toContain("Claude's plan")
expect(container.textContent).not.toContain('No plan content available.')
})
it('renders EnterPlanMode as a compact status instead of raw model instructions', () => {
const { container } = render(
<ToolCallBlock

View File

@ -249,6 +249,12 @@ function PlanToolCallBlock({
}) {
const t = useTranslation()
const preview = extractPlanPreview(input, result?.content)
const hasPlanPreview = Boolean(
preview.plan.trim() ||
preview.filePath ||
preview.allowedPrompts.length > 0,
)
const showPlanPreview = hasPlanPreview || !result?.isError
const title = result?.isError
? t('permission.planRejected')
: result
@ -287,14 +293,16 @@ function PlanToolCallBlock({
{expanded ? (
<div className="space-y-2.5 border-t border-[var(--color-border)]/60 px-3 py-3">
<PlanPreviewCard
title={t('permission.planPreviewTitle')}
plan={preview.plan}
filePath={preview.filePath}
allowedPrompts={preview.allowedPrompts}
requestedPermissionsTitle={t('permission.planRequestedPermissions')}
emptyLabel={t('permission.planEmpty')}
/>
{showPlanPreview ? (
<PlanPreviewCard
title={t('permission.planPreviewTitle')}
plan={preview.plan}
filePath={preview.filePath}
allowedPrompts={preview.allowedPrompts}
requestedPermissionsTitle={t('permission.planRequestedPermissions')}
emptyLabel={t('permission.planEmpty')}
/>
) : null}
{result?.isError && hasRawResult ? (
renderResultOutput(result, extractTextContent(result.content) ?? '', t)
) : null}