Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit e686dee

Browse files
committed
feat: add "View Diff" button to task actions for viewing all changes since task started
1 parent cb83656 commit e686dee

3 files changed

Lines changed: 107 additions & 3 deletions

File tree

webview-ui/src/components/chat/TaskActions.tsx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react"
1+
import { useState, useMemo, useCallback } from "react"
22
import { useTranslation } from "react-i18next"
33

44
import type { HistoryItem } from "@roo-code/types"
@@ -9,7 +9,15 @@ import { useExtensionState } from "@/context/ExtensionStateContext"
99

1010
import { DeleteTaskDialog } from "../history/DeleteTaskDialog"
1111
import { ShareButton } from "./ShareButton"
12-
import { CopyIcon, CheckIcon, DownloadIcon, Trash2Icon, FileJsonIcon, MessageSquareCodeIcon } from "lucide-react"
12+
import {
13+
CopyIcon,
14+
CheckIcon,
15+
DownloadIcon,
16+
Trash2Icon,
17+
FileJsonIcon,
18+
MessageSquareCodeIcon,
19+
DiffIcon,
20+
} from "lucide-react"
1321
import { LucideIconButton } from "./LucideIconButton"
1422

1523
interface TaskActionsProps {
@@ -21,7 +29,26 @@ export const TaskActions = ({ item, buttonsDisabled }: TaskActionsProps) => {
2129
const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null)
2230
const { t } = useTranslation()
2331
const { copyWithFeedback, showCopyFeedback } = useCopyToClipboard()
24-
const { debug } = useExtensionState()
32+
const { debug, enableCheckpoints, clineMessages } = useExtensionState()
33+
34+
const firstCheckpointHash = useMemo(() => {
35+
const msg = (clineMessages ?? []).find((m) => m.say === "checkpoint_saved")
36+
return msg?.text
37+
}, [clineMessages])
38+
39+
const lastCheckpointHash = useMemo(() => {
40+
const checkpointMessages = (clineMessages ?? []).filter((m) => m.say === "checkpoint_saved")
41+
return checkpointMessages.length > 0 ? checkpointMessages[checkpointMessages.length - 1].text : undefined
42+
}, [clineMessages])
43+
44+
const onViewFullDiff = useCallback(() => {
45+
if (lastCheckpointHash) {
46+
vscode.postMessage({
47+
type: "checkpointDiff",
48+
payload: { commitHash: lastCheckpointHash, mode: "full" },
49+
})
50+
}
51+
}, [lastCheckpointHash])
2552

2653
return (
2754
<div className="flex flex-row items-center -ml-0.5 mt-1 gap-1">
@@ -31,6 +58,10 @@ export const TaskActions = ({ item, buttonsDisabled }: TaskActionsProps) => {
3158
onClick={() => vscode.postMessage({ type: "exportCurrentTask" })}
3259
/>
3360

61+
{enableCheckpoints && firstCheckpointHash && (
62+
<LucideIconButton icon={DiffIcon} title={t("chat:task.viewDiff")} onClick={onViewFullDiff} />
63+
)}
64+
3465
{item?.task && (
3566
<LucideIconButton
3667
icon={showCopyFeedback ? CheckIcon : CopyIcon}

webview-ui/src/components/chat/__tests__/TaskActions.spec.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ vi.mock("react-i18next", () => ({
5151
"chat:task.sharingDisabledByOrganization": "Sharing disabled by organization",
5252
"chat:task.openApiHistory": "Open API History",
5353
"chat:task.openUiHistory": "Open UI History",
54+
"chat:task.viewDiff": "View all changes since task started",
5455
"cloud:cloudBenefitsTitle": "Connect to Roo Code Cloud",
5556
"cloud:cloudBenefitHistory": "Access your task history from anywhere",
5657
"cloud:cloudBenefitSharing": "Share tasks with your team",
@@ -525,4 +526,75 @@ describe("TaskActions", () => {
525526
})
526527
})
527528
})
529+
530+
describe("View Diff Button", () => {
531+
it("renders view diff button when checkpoints are enabled and checkpoint exists", () => {
532+
mockUseExtensionState.mockReturnValue({
533+
sharingEnabled: true,
534+
cloudIsAuthenticated: true,
535+
cloudUserInfo: { organizationName: "Test Organization" },
536+
enableCheckpoints: true,
537+
clineMessages: [{ say: "checkpoint_saved", text: "abc123", ts: 1000 }],
538+
} as any)
539+
540+
render(<TaskActions item={mockItem} buttonsDisabled={false} />)
541+
542+
const viewDiffButton = screen.getByLabelText("View all changes since task started")
543+
expect(viewDiffButton).toBeInTheDocument()
544+
})
545+
546+
it("does not render view diff button when checkpoints are disabled", () => {
547+
mockUseExtensionState.mockReturnValue({
548+
sharingEnabled: true,
549+
cloudIsAuthenticated: true,
550+
cloudUserInfo: { organizationName: "Test Organization" },
551+
enableCheckpoints: false,
552+
clineMessages: [{ say: "checkpoint_saved", text: "abc123", ts: 1000 }],
553+
} as any)
554+
555+
render(<TaskActions item={mockItem} buttonsDisabled={false} />)
556+
557+
const viewDiffButton = screen.queryByLabelText("View all changes since task started")
558+
expect(viewDiffButton).toBeNull()
559+
})
560+
561+
it("does not render view diff button when no checkpoints exist", () => {
562+
mockUseExtensionState.mockReturnValue({
563+
sharingEnabled: true,
564+
cloudIsAuthenticated: true,
565+
cloudUserInfo: { organizationName: "Test Organization" },
566+
enableCheckpoints: true,
567+
clineMessages: [],
568+
} as any)
569+
570+
render(<TaskActions item={mockItem} buttonsDisabled={false} />)
571+
572+
const viewDiffButton = screen.queryByLabelText("View all changes since task started")
573+
expect(viewDiffButton).toBeNull()
574+
})
575+
576+
it("sends checkpointDiff message with full mode when view diff button is clicked", () => {
577+
mockUseExtensionState.mockReturnValue({
578+
sharingEnabled: true,
579+
cloudIsAuthenticated: true,
580+
cloudUserInfo: { organizationName: "Test Organization" },
581+
enableCheckpoints: true,
582+
clineMessages: [
583+
{ say: "checkpoint_saved", text: "first-hash", ts: 1000 },
584+
{ say: "text", text: "some message", ts: 2000 },
585+
{ say: "checkpoint_saved", text: "last-hash", ts: 3000 },
586+
],
587+
} as any)
588+
589+
render(<TaskActions item={mockItem} buttonsDisabled={false} />)
590+
591+
const viewDiffButton = screen.getByLabelText("View all changes since task started")
592+
fireEvent.click(viewDiffButton)
593+
594+
expect(mockPostMessage).toHaveBeenCalledWith({
595+
type: "checkpointDiff",
596+
payload: { commitHash: "last-hash", mode: "full" },
597+
})
598+
})
599+
})
528600
})

webview-ui/src/i18n/locales/en/chat.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"shareSuccessPublic": "Public link copied to clipboard",
2828
"openApiHistory": "Open API History",
2929
"openUiHistory": "Open UI History",
30+
"viewDiff": "View all changes since task started",
3031
"backToParentTask": "Parent task"
3132
},
3233
"unpin": "Unpin",

0 commit comments

Comments
 (0)