Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/core/Cline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import getFolderSize from "get-folder-size"
import * as path from "path"
import { serializeError } from "serialize-error"
import * as vscode from "vscode"

import { ApiHandler, buildApiHandler } from "../api"
import { ApiStream } from "../api/transform/stream"
import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider"
Expand All @@ -31,6 +32,7 @@ import { UrlContentFetcher } from "../services/browser/UrlContentFetcher"
import { listFiles } from "../services/glob/list-files"
import { regexSearchFiles } from "../services/ripgrep"
import { parseSourceCodeForDefinitionsTopLevel } from "../services/tree-sitter"
import { CheckpointStorage } from "../shared/checkpoints"
import { ApiConfiguration } from "../shared/api"
import { findLastIndex } from "../shared/array"
import { combineApiRequests } from "../shared/combineApiRequests"
Expand Down Expand Up @@ -81,7 +83,7 @@ export type ClineOptions = {
customInstructions?: string
enableDiff?: boolean
enableCheckpoints?: boolean
checkpointStorage?: "task" | "workspace"
checkpointStorage?: CheckpointStorage
fuzzyMatchThreshold?: number
task?: string
images?: string[]
Expand Down Expand Up @@ -121,7 +123,7 @@ export class Cline {

// checkpoints
private enableCheckpoints: boolean
private checkpointStorage: "task" | "workspace"
private checkpointStorage: CheckpointStorage
private checkpointService?: RepoPerTaskCheckpointService | RepoPerWorkspaceCheckpointService

// streaming
Expand Down
46 changes: 27 additions & 19 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as vscode from "vscode"
import simpleGit from "simple-git"

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

public async initClineWithTask(task?: string, images?: string[]) {
await this.clearTask()

const {
apiConfiguration,
customModePrompts,
diffEnabled,
diffEnabled: enableDiff,
enableCheckpoints,
checkpointStorage,
fuzzyMatchThreshold,
mode,
customInstructions: globalInstructions,
Expand All @@ -331,8 +334,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
provider: this,
apiConfiguration,
customInstructions: effectiveInstructions,
enableDiff: diffEnabled,
enableDiff,
enableCheckpoints,
checkpointStorage,
fuzzyMatchThreshold,
task,
images,
Expand All @@ -346,8 +350,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
const {
apiConfiguration,
customModePrompts,
diffEnabled,
diffEnabled: enableDiff,
enableCheckpoints,
checkpointStorage,
fuzzyMatchThreshold,
mode,
customInstructions: globalInstructions,
Expand All @@ -357,12 +362,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
const modePrompt = customModePrompts?.[mode] as PromptComponent
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")

// TODO: The `checkpointStorage` value should be derived from the
// task data on disk; the current setting could be different than
// the setting at the time the task was created.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on this as a follow-up.


this.cline = new Cline({
provider: this,
apiConfiguration,
customInstructions: effectiveInstructions,
enableDiff: diffEnabled,
enableDiff,
enableCheckpoints,
checkpointStorage,
fuzzyMatchThreshold,
historyItem,
experiments,
Expand Down Expand Up @@ -1022,6 +1032,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("enableCheckpoints", enableCheckpoints)
await this.postStateToWebview()
break
case "checkpointStorage":
console.log(`[ClineProvider] checkpointStorage: ${message.text}`)
const checkpointStorage = message.text ?? "task"
await this.updateGlobalState("checkpointStorage", checkpointStorage)
await this.postStateToWebview()
break
case "browserViewportSize":
const browserViewportSize = message.text ?? "900x600"
await this.updateGlobalState("browserViewportSize", browserViewportSize)
Expand Down Expand Up @@ -1947,21 +1963,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await fs.unlink(legacyMessagesFilePath)
}

const { enableCheckpoints } = await this.getState()
const baseDir = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)

// Delete checkpoints branch.
if (enableCheckpoints && baseDir) {
const branchSummary = await simpleGit(baseDir)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should stop tampering with the non-shadow repo altogether now that the shadow repo implementation has been out for a while.

.branch(["-D", `roo-code-checkpoints-${id}`])
.catch(() => undefined)

if (branchSummary) {
console.log(`[deleteTaskWithId${id}] deleted checkpoints branch`)
}
}

// Delete checkpoints directory
// Delete checkpoints directory.
// TODO: Also delete the workspace branch if it exists.
const checkpointsDir = path.join(taskDirPath, "checkpoints")

if (await fileExistsAtPath(checkpointsDir)) {
Expand Down Expand Up @@ -2008,6 +2011,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
soundEnabled,
diffEnabled,
enableCheckpoints,
checkpointStorage,
taskHistory,
soundVolume,
browserViewportSize,
Expand Down Expand Up @@ -2058,6 +2062,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
soundEnabled: soundEnabled ?? false,
diffEnabled: diffEnabled ?? true,
enableCheckpoints: enableCheckpoints ?? true,
checkpointStorage: checkpointStorage ?? "task",
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
allowedCommands,
soundVolume: soundVolume ?? 0.5,
Expand Down Expand Up @@ -2191,6 +2196,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
soundEnabled,
diffEnabled,
enableCheckpoints,
checkpointStorage,
soundVolume,
browserViewportSize,
fuzzyMatchThreshold,
Expand Down Expand Up @@ -2278,6 +2284,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("soundEnabled") as Promise<boolean | undefined>,
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
this.getGlobalState("enableCheckpoints") as Promise<boolean | undefined>,
this.getGlobalState("checkpointStorage") as Promise<CheckpointStorage | undefined>,
this.getGlobalState("soundVolume") as Promise<number | undefined>,
this.getGlobalState("browserViewportSize") as Promise<string | undefined>,
this.getGlobalState("fuzzyMatchThreshold") as Promise<number | undefined>,
Expand Down Expand Up @@ -2395,6 +2402,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
soundEnabled: soundEnabled ?? false,
diffEnabled: diffEnabled ?? true,
enableCheckpoints: enableCheckpoints ?? true,
checkpointStorage: checkpointStorage ?? "task",
soundVolume,
browserViewportSize: browserViewportSize ?? "900x600",
screenshotQuality: screenshotQuality ?? 75,
Expand Down
3 changes: 3 additions & 0 deletions src/core/webview/__tests__/ClineProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ describe("ClineProvider", () => {
soundEnabled: false,
diffEnabled: false,
enableCheckpoints: false,
checkpointStorage: "task",
writeDelayMs: 1000,
browserViewportSize: "900x600",
fuzzyMatchThreshold: 1.0,
Expand Down Expand Up @@ -694,6 +695,7 @@ describe("ClineProvider", () => {
mode: "code",
diffEnabled: true,
enableCheckpoints: false,
checkpointStorage: "task",
fuzzyMatchThreshold: 1.0,
experiments: experimentDefault,
} as any)
Expand All @@ -712,6 +714,7 @@ describe("ClineProvider", () => {
customInstructions: modeCustomInstructions,
enableDiff: true,
enableCheckpoints: false,
checkpointStorage: "task",
fuzzyMatchThreshold: 1.0,
task: "Test task",
experiments: experimentDefault,
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { GitCommit } from "../utils/git"
import { Mode, CustomModePrompts, ModeConfig } from "./modes"
import { CustomSupportPrompts } from "./support-prompt"
import { ExperimentId } from "./experiments"
import { CheckpointStorage } from "./checkpoints"

export interface LanguageModelChatSelector {
vendor?: string
Expand Down Expand Up @@ -114,6 +115,7 @@ export interface ExtensionState {
soundVolume?: number
diffEnabled?: boolean
enableCheckpoints: boolean
checkpointStorage: CheckpointStorage
browserViewportSize?: string
screenshotQuality?: number
fuzzyMatchThreshold?: number
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface WebviewMessage {
| "soundVolume"
| "diffEnabled"
| "enableCheckpoints"
| "checkpointStorage"
| "browserViewportSize"
| "screenshotQuality"
| "openMcpSettings"
Expand Down
5 changes: 5 additions & 0 deletions src/shared/checkpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type CheckpointStorage = "task" | "workspace"

export const isCheckpointStorage = (value: string): value is CheckpointStorage => {
return value === "task" || value === "workspace"
}
1 change: 1 addition & 0 deletions src/shared/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type GlobalStateKey =
| "soundVolume"
| "diffEnabled"
| "enableCheckpoints"
| "checkpointStorage"
| "browserViewportSize"
| "screenshotQuality"
| "fuzzyMatchThreshold"
Expand Down
1 change: 0 additions & 1 deletion webview-ui/src/components/common/MermaidBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
}

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

Expand Down
139 changes: 51 additions & 88 deletions webview-ui/src/components/history/HistoryPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,101 +14,64 @@ type HistoryPreviewProps = {
const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
const { taskHistory } = useExtensionState()

const handleHistorySelect = (id: string) => {
vscode.postMessage({ type: "showTaskWithId", text: id })
}

return (
<div style={{ flexShrink: 0 }}>
<style>
{`
.history-preview-item {
background-color: color-mix(in srgb, var(--vscode-toolbar-hoverBackground) 65%, transparent);
border-radius: 4px;
position: relative;
overflow: hidden;
opacity: 0.8;
cursor: pointer;
margin-bottom: 12px;
}
.history-preview-item:hover {
background-color: color-mix(in srgb, var(--vscode-toolbar-hoverBackground) 100%, transparent);
opacity: 1;
pointer-events: auto;
}
`}
</style>
<div
style={{
color: "var(--vscode-descriptionForeground)",
margin: "10px 20px 10px 20px",
display: "flex",
alignItems: "center",
}}>
<span className="codicon codicon-comment-discussion scale-90 mr-1" />
<span className="font-medium text-xs uppercase">Recent Tasks</span>
<div className="flex flex-col gap-3 shrink-0 mx-5">
<div className="flex items-center justify-between text-vscode-descriptionForeground">
<div className="flex items-center gap-1">
<span className="codicon codicon-comment-discussion scale-90 mr-1" />
<span className="font-medium text-xs uppercase">Recent Tasks</span>
</div>
<Button variant="ghost" size="sm" onClick={() => showHistoryView()} className="uppercase">
View All
</Button>
</div>
<div className="px-5">
{taskHistory
.filter((item) => item.ts && item.task)
.slice(0, 3)
.map((item) => (
{taskHistory.slice(0, 3).map((item) => (
<div
key={item.id}
className="bg-vscode-toolbar-hoverBackground/50 hover:bg-vscode-toolbar-hoverBackground/75 rounded-xs relative overflow-hidden opacity-90 hover:opacity-100 cursor-pointer"
onClick={() => vscode.postMessage({ type: "showTaskWithId", text: item.id })}>
<div className="flex flex-col gap-2 p-3 pt-1">
<div className="flex justify-between items-center">
<span className="text-xs font-medium text-vscode-descriptionForeground uppercase">
{formatDate(item.ts)}
</span>
<CopyButton itemTask={item.task} />
</div>
<div
key={item.id}
className="history-preview-item"
onClick={() => handleHistorySelect(item.id)}>
<div className="flex flex-col gap-2 p-3 pt-1">
<div className="flex justify-between items-center">
<span className="text-xs font-medium text-vscode-descriptionForeground uppercase">
{formatDate(item.ts)}
</span>
<CopyButton itemTask={item.task} />
</div>
<div
className="text-vscode-descriptionForeground overflow-hidden whitespace-pre-wrap"
style={{
display: "-webkit-box",
WebkitLineClamp: 3,
WebkitBoxOrient: "vertical",
wordBreak: "break-word",
overflowWrap: "anywhere",
}}>
{item.task}
</div>
<div className="text-xs text-vscode-descriptionForeground">
className="text-vscode-descriptionForeground overflow-hidden whitespace-pre-wrap"
style={{
display: "-webkit-box",
WebkitLineClamp: 3,
WebkitBoxOrient: "vertical",
wordBreak: "break-word",
overflowWrap: "anywhere",
}}>
{item.task}
</div>
<div className="text-xs text-vscode-descriptionForeground">
<span>
Tokens: ↑{formatLargeNumber(item.tokensIn || 0)} ↓
{formatLargeNumber(item.tokensOut || 0)}
</span>
{!!item.cacheWrites && (
<>
{" • "}
<span>
Tokens: ↑{formatLargeNumber(item.tokensIn || 0)}
{formatLargeNumber(item.tokensOut || 0)}
Cache: +{formatLargeNumber(item.cacheWrites || 0)} →{" "}
{formatLargeNumber(item.cacheReads || 0)}
</span>
{!!item.cacheWrites && (
<>
{" • "}
<span>
Cache: +{formatLargeNumber(item.cacheWrites || 0)} →{" "}
{formatLargeNumber(item.cacheReads || 0)}
</span>
</>
)}
{!!item.totalCost && (
<>
{" • "}
<span>API Cost: ${item.totalCost?.toFixed(4)}</span>
</>
)}
</div>
</div>
</>
)}
{!!item.totalCost && (
<>
{" • "}
<span>API Cost: ${item.totalCost?.toFixed(4)}</span>
</>
)}
</div>
))}
<div className="flex justify-center">
<Button
variant="ghost"
size="sm"
onClick={() => showHistoryView()}
className="font-normal text-vscode-descriptionForeground">
View all history
</Button>
</div>
</div>
</div>
))}
</div>
)
}
Expand Down
Loading