Skip to content

Commit e1ccb70

Browse files
committed
Checkpoint storage setting + settings view redesign
1 parent 541b54e commit e1ccb70

33 files changed

+1440
-1137
lines changed

src/core/Cline.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import getFolderSize from "get-folder-size"
1010
import * as path from "path"
1111
import { serializeError } from "serialize-error"
1212
import * as vscode from "vscode"
13+
1314
import { ApiHandler, buildApiHandler } from "../api"
1415
import { ApiStream } from "../api/transform/stream"
1516
import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider"
@@ -31,6 +32,7 @@ import { UrlContentFetcher } from "../services/browser/UrlContentFetcher"
3132
import { listFiles } from "../services/glob/list-files"
3233
import { regexSearchFiles } from "../services/ripgrep"
3334
import { parseSourceCodeForDefinitionsTopLevel } from "../services/tree-sitter"
35+
import { CheckpointStorage } from "../shared/checkpoints"
3436
import { ApiConfiguration } from "../shared/api"
3537
import { findLastIndex } from "../shared/array"
3638
import { combineApiRequests } from "../shared/combineApiRequests"
@@ -81,7 +83,7 @@ export type ClineOptions = {
8183
customInstructions?: string
8284
enableDiff?: boolean
8385
enableCheckpoints?: boolean
84-
checkpointStorage?: "task" | "workspace"
86+
checkpointStorage?: CheckpointStorage
8587
fuzzyMatchThreshold?: number
8688
task?: string
8789
images?: string[]
@@ -121,7 +123,7 @@ export class Cline {
121123

122124
// checkpoints
123125
private enableCheckpoints: boolean
124-
private checkpointStorage: "task" | "workspace"
126+
private checkpointStorage: CheckpointStorage
125127
private checkpointService?: RepoPerTaskCheckpointService | RepoPerWorkspaceCheckpointService
126128

127129
// streaming

src/core/webview/ClineProvider.ts

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as vscode from "vscode"
99
import simpleGit from "simple-git"
1010

1111
import { ApiConfiguration, ApiProvider, ModelInfo } from "../../shared/api"
12+
import { CheckpointStorage } from "../../shared/checkpoints"
1213
import { findLast } from "../../shared/array"
1314
import { CustomSupportPrompts, supportPrompt } from "../../shared/support-prompt"
1415
import { GlobalFileNames } from "../../shared/globalFileNames"
@@ -313,11 +314,13 @@ export class ClineProvider implements vscode.WebviewViewProvider {
313314

314315
public async initClineWithTask(task?: string, images?: string[]) {
315316
await this.clearTask()
317+
316318
const {
317319
apiConfiguration,
318320
customModePrompts,
319-
diffEnabled,
321+
diffEnabled: enableDiff,
320322
enableCheckpoints,
323+
checkpointStorage,
321324
fuzzyMatchThreshold,
322325
mode,
323326
customInstructions: globalInstructions,
@@ -331,8 +334,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
331334
provider: this,
332335
apiConfiguration,
333336
customInstructions: effectiveInstructions,
334-
enableDiff: diffEnabled,
337+
enableDiff,
335338
enableCheckpoints,
339+
checkpointStorage,
336340
fuzzyMatchThreshold,
337341
task,
338342
images,
@@ -346,8 +350,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
346350
const {
347351
apiConfiguration,
348352
customModePrompts,
349-
diffEnabled,
353+
diffEnabled: enableDiff,
350354
enableCheckpoints,
355+
checkpointStorage,
351356
fuzzyMatchThreshold,
352357
mode,
353358
customInstructions: globalInstructions,
@@ -357,12 +362,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
357362
const modePrompt = customModePrompts?.[mode] as PromptComponent
358363
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
359364

365+
// TODO: The `checkpointStorage` value should be derived from the
366+
// task data on disk; the current setting could be different than
367+
// the setting at the time the task was created.
368+
360369
this.cline = new Cline({
361370
provider: this,
362371
apiConfiguration,
363372
customInstructions: effectiveInstructions,
364-
enableDiff: diffEnabled,
373+
enableDiff,
365374
enableCheckpoints,
375+
checkpointStorage,
366376
fuzzyMatchThreshold,
367377
historyItem,
368378
experiments,
@@ -1022,6 +1032,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
10221032
await this.updateGlobalState("enableCheckpoints", enableCheckpoints)
10231033
await this.postStateToWebview()
10241034
break
1035+
case "checkpointStorage":
1036+
console.log(`[ClineProvider] checkpointStorage: ${message.text}`)
1037+
const checkpointStorage = message.text ?? "task"
1038+
await this.updateGlobalState("checkpointStorage", checkpointStorage)
1039+
await this.postStateToWebview()
1040+
break
10251041
case "browserViewportSize":
10261042
const browserViewportSize = message.text ?? "900x600"
10271043
await this.updateGlobalState("browserViewportSize", browserViewportSize)
@@ -1947,21 +1963,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
19471963
await fs.unlink(legacyMessagesFilePath)
19481964
}
19491965

1950-
const { enableCheckpoints } = await this.getState()
1951-
const baseDir = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
1952-
1953-
// Delete checkpoints branch.
1954-
if (enableCheckpoints && baseDir) {
1955-
const branchSummary = await simpleGit(baseDir)
1956-
.branch(["-D", `roo-code-checkpoints-${id}`])
1957-
.catch(() => undefined)
1958-
1959-
if (branchSummary) {
1960-
console.log(`[deleteTaskWithId${id}] deleted checkpoints branch`)
1961-
}
1962-
}
1963-
1964-
// Delete checkpoints directory
1966+
// Delete checkpoints directory.
1967+
// TODO: Also delete the workspace branch if it exists.
19651968
const checkpointsDir = path.join(taskDirPath, "checkpoints")
19661969

19671970
if (await fileExistsAtPath(checkpointsDir)) {
@@ -2008,6 +2011,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
20082011
soundEnabled,
20092012
diffEnabled,
20102013
enableCheckpoints,
2014+
checkpointStorage,
20112015
taskHistory,
20122016
soundVolume,
20132017
browserViewportSize,
@@ -2058,6 +2062,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
20582062
soundEnabled: soundEnabled ?? false,
20592063
diffEnabled: diffEnabled ?? true,
20602064
enableCheckpoints: enableCheckpoints ?? true,
2065+
checkpointStorage: checkpointStorage ?? "task",
20612066
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
20622067
allowedCommands,
20632068
soundVolume: soundVolume ?? 0.5,
@@ -2191,6 +2196,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
21912196
soundEnabled,
21922197
diffEnabled,
21932198
enableCheckpoints,
2199+
checkpointStorage,
21942200
soundVolume,
21952201
browserViewportSize,
21962202
fuzzyMatchThreshold,
@@ -2278,6 +2284,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
22782284
this.getGlobalState("soundEnabled") as Promise<boolean | undefined>,
22792285
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
22802286
this.getGlobalState("enableCheckpoints") as Promise<boolean | undefined>,
2287+
this.getGlobalState("checkpointStorage") as Promise<CheckpointStorage | undefined>,
22812288
this.getGlobalState("soundVolume") as Promise<number | undefined>,
22822289
this.getGlobalState("browserViewportSize") as Promise<string | undefined>,
22832290
this.getGlobalState("fuzzyMatchThreshold") as Promise<number | undefined>,
@@ -2395,6 +2402,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
23952402
soundEnabled: soundEnabled ?? false,
23962403
diffEnabled: diffEnabled ?? true,
23972404
enableCheckpoints: enableCheckpoints ?? true,
2405+
checkpointStorage: checkpointStorage ?? "task",
23982406
soundVolume,
23992407
browserViewportSize: browserViewportSize ?? "900x600",
24002408
screenshotQuality: screenshotQuality ?? 75,

src/core/webview/__tests__/ClineProvider.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ describe("ClineProvider", () => {
370370
soundEnabled: false,
371371
diffEnabled: false,
372372
enableCheckpoints: false,
373+
checkpointStorage: "task",
373374
writeDelayMs: 1000,
374375
browserViewportSize: "900x600",
375376
fuzzyMatchThreshold: 1.0,
@@ -694,6 +695,7 @@ describe("ClineProvider", () => {
694695
mode: "code",
695696
diffEnabled: true,
696697
enableCheckpoints: false,
698+
checkpointStorage: "task",
697699
fuzzyMatchThreshold: 1.0,
698700
experiments: experimentDefault,
699701
} as any)
@@ -712,6 +714,7 @@ describe("ClineProvider", () => {
712714
customInstructions: modeCustomInstructions,
713715
enableDiff: true,
714716
enableCheckpoints: false,
717+
checkpointStorage: "task",
715718
fuzzyMatchThreshold: 1.0,
716719
task: "Test task",
717720
experiments: experimentDefault,

src/shared/ExtensionMessage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { GitCommit } from "../utils/git"
77
import { Mode, CustomModePrompts, ModeConfig } from "./modes"
88
import { CustomSupportPrompts } from "./support-prompt"
99
import { ExperimentId } from "./experiments"
10+
import { CheckpointStorage } from "./checkpoints"
1011

1112
export interface LanguageModelChatSelector {
1213
vendor?: string
@@ -114,6 +115,7 @@ export interface ExtensionState {
114115
soundVolume?: number
115116
diffEnabled?: boolean
116117
enableCheckpoints: boolean
118+
checkpointStorage: CheckpointStorage
117119
browserViewportSize?: string
118120
screenshotQuality?: number
119121
fuzzyMatchThreshold?: number

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export interface WebviewMessage {
5353
| "soundVolume"
5454
| "diffEnabled"
5555
| "enableCheckpoints"
56+
| "checkpointStorage"
5657
| "browserViewportSize"
5758
| "screenshotQuality"
5859
| "openMcpSettings"

src/shared/checkpoints.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type CheckpointStorage = "task" | "workspace"
2+
3+
export const isCheckpointStorage = (value: string): value is CheckpointStorage => {
4+
return value === "task" || value === "workspace"
5+
}

src/shared/globalState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type GlobalStateKey =
5555
| "soundVolume"
5656
| "diffEnabled"
5757
| "enableCheckpoints"
58+
| "checkpointStorage"
5859
| "browserViewportSize"
5960
| "screenshotQuality"
6061
| "fuzzyMatchThreshold"

webview-ui/src/components/common/MermaidBlock.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
150150
}
151151

152152
async function svgToPng(svgEl: SVGElement): Promise<string> {
153-
console.log("svgToPng function called")
154153
// Clone the SVG to avoid modifying the original
155154
const svgClone = svgEl.cloneNode(true) as SVGElement
156155

webview-ui/src/components/history/HistoryPreview.tsx

Lines changed: 51 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -14,101 +14,64 @@ type HistoryPreviewProps = {
1414
const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
1515
const { taskHistory } = useExtensionState()
1616

17-
const handleHistorySelect = (id: string) => {
18-
vscode.postMessage({ type: "showTaskWithId", text: id })
19-
}
20-
2117
return (
22-
<div style={{ flexShrink: 0 }}>
23-
<style>
24-
{`
25-
.history-preview-item {
26-
background-color: color-mix(in srgb, var(--vscode-toolbar-hoverBackground) 65%, transparent);
27-
border-radius: 4px;
28-
position: relative;
29-
overflow: hidden;
30-
opacity: 0.8;
31-
cursor: pointer;
32-
margin-bottom: 12px;
33-
}
34-
.history-preview-item:hover {
35-
background-color: color-mix(in srgb, var(--vscode-toolbar-hoverBackground) 100%, transparent);
36-
opacity: 1;
37-
pointer-events: auto;
38-
}
39-
`}
40-
</style>
41-
<div
42-
style={{
43-
color: "var(--vscode-descriptionForeground)",
44-
margin: "10px 20px 10px 20px",
45-
display: "flex",
46-
alignItems: "center",
47-
}}>
48-
<span className="codicon codicon-comment-discussion scale-90 mr-1" />
49-
<span className="font-medium text-xs uppercase">Recent Tasks</span>
18+
<div className="flex flex-col gap-3 shrink-0 mx-5">
19+
<div className="flex items-center justify-between text-vscode-descriptionForeground">
20+
<div className="flex items-center gap-1">
21+
<span className="codicon codicon-comment-discussion scale-90 mr-1" />
22+
<span className="font-medium text-xs uppercase">Recent Tasks</span>
23+
</div>
24+
<Button variant="ghost" size="sm" onClick={() => showHistoryView()} className="uppercase">
25+
View All
26+
</Button>
5027
</div>
51-
<div className="px-5">
52-
{taskHistory
53-
.filter((item) => item.ts && item.task)
54-
.slice(0, 3)
55-
.map((item) => (
28+
{taskHistory.slice(0, 3).map((item) => (
29+
<div
30+
key={item.id}
31+
className="bg-vscode-toolbar-hoverBackground/50 hover:bg-vscode-toolbar-hoverBackground/75 rounded-xs relative overflow-hidden opacity-90 hover:opacity-100 cursor-pointer"
32+
onClick={() => vscode.postMessage({ type: "showTaskWithId", text: item.id })}>
33+
<div className="flex flex-col gap-2 p-3 pt-1">
34+
<div className="flex justify-between items-center">
35+
<span className="text-xs font-medium text-vscode-descriptionForeground uppercase">
36+
{formatDate(item.ts)}
37+
</span>
38+
<CopyButton itemTask={item.task} />
39+
</div>
5640
<div
57-
key={item.id}
58-
className="history-preview-item"
59-
onClick={() => handleHistorySelect(item.id)}>
60-
<div className="flex flex-col gap-2 p-3 pt-1">
61-
<div className="flex justify-between items-center">
62-
<span className="text-xs font-medium text-vscode-descriptionForeground uppercase">
63-
{formatDate(item.ts)}
64-
</span>
65-
<CopyButton itemTask={item.task} />
66-
</div>
67-
<div
68-
className="text-vscode-descriptionForeground overflow-hidden whitespace-pre-wrap"
69-
style={{
70-
display: "-webkit-box",
71-
WebkitLineClamp: 3,
72-
WebkitBoxOrient: "vertical",
73-
wordBreak: "break-word",
74-
overflowWrap: "anywhere",
75-
}}>
76-
{item.task}
77-
</div>
78-
<div className="text-xs text-vscode-descriptionForeground">
41+
className="text-vscode-descriptionForeground overflow-hidden whitespace-pre-wrap"
42+
style={{
43+
display: "-webkit-box",
44+
WebkitLineClamp: 3,
45+
WebkitBoxOrient: "vertical",
46+
wordBreak: "break-word",
47+
overflowWrap: "anywhere",
48+
}}>
49+
{item.task}
50+
</div>
51+
<div className="text-xs text-vscode-descriptionForeground">
52+
<span>
53+
Tokens: ↑{formatLargeNumber(item.tokensIn || 0)}
54+
{formatLargeNumber(item.tokensOut || 0)}
55+
</span>
56+
{!!item.cacheWrites && (
57+
<>
58+
{" • "}
7959
<span>
80-
Tokens: ↑{formatLargeNumber(item.tokensIn || 0)}
81-
{formatLargeNumber(item.tokensOut || 0)}
60+
Cache: +{formatLargeNumber(item.cacheWrites || 0)} {" "}
61+
{formatLargeNumber(item.cacheReads || 0)}
8262
</span>
83-
{!!item.cacheWrites && (
84-
<>
85-
{" • "}
86-
<span>
87-
Cache: +{formatLargeNumber(item.cacheWrites || 0)}{" "}
88-
{formatLargeNumber(item.cacheReads || 0)}
89-
</span>
90-
</>
91-
)}
92-
{!!item.totalCost && (
93-
<>
94-
{" • "}
95-
<span>API Cost: ${item.totalCost?.toFixed(4)}</span>
96-
</>
97-
)}
98-
</div>
99-
</div>
63+
</>
64+
)}
65+
{!!item.totalCost && (
66+
<>
67+
{" • "}
68+
<span>API Cost: ${item.totalCost?.toFixed(4)}</span>
69+
</>
70+
)}
10071
</div>
101-
))}
102-
<div className="flex justify-center">
103-
<Button
104-
variant="ghost"
105-
size="sm"
106-
onClick={() => showHistoryView()}
107-
className="font-normal text-vscode-descriptionForeground">
108-
View all history
109-
</Button>
72+
</div>
11073
</div>
111-
</div>
74+
))}
11275
</div>
11376
)
11477
}

0 commit comments

Comments
 (0)