Skip to content
Closed
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
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const globalSettingsSchema = z.object({
lastShownAnnouncementId: z.string().optional(),
customInstructions: z.string().optional(),
taskHistory: z.array(historyItemSchema).optional(),
showAllWorkspacesTasks: z.boolean().optional(),

condensingApiConfigId: z.string().optional(),
customCondensingPrompt: z.string().optional(),
Expand Down
3 changes: 3 additions & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ export class ClineProvider
profileThresholds,
alwaysAllowFollowupQuestions,
followupAutoApproveTimeoutMs,
showAllWorkspacesTasks,
} = await this.getState()

const telemetryKey = process.env.POSTHOG_API_KEY
Expand Down Expand Up @@ -1522,6 +1523,7 @@ export class ClineProvider
hasOpenedModeSelector: this.getGlobalState("hasOpenedModeSelector") ?? false,
alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions ?? false,
followupAutoApproveTimeoutMs: followupAutoApproveTimeoutMs ?? 60000,
showAllWorkspacesTasks: showAllWorkspacesTasks ?? false,
}
}

Expand Down Expand Up @@ -1675,6 +1677,7 @@ export class ClineProvider
codebaseIndexEmbedderModelId: "",
},
profileThresholds: stateValues.profileThresholds ?? {},
showAllWorkspacesTasks: stateValues.showAllWorkspacesTasks ?? false,
}
}

Expand Down
1 change: 1 addition & 0 deletions src/core/webview/__tests__/ClineProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ describe("ClineProvider", () => {
version: "1.0.0",
clineMessages: [],
taskHistory: [],
showAllWorkspacesTasks: false,
shouldShowAnnouncement: false,
apiConfiguration: {
apiProvider: "openrouter",
Expand Down
5 changes: 5 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2206,5 +2206,10 @@ export const webviewMessageHandler = async (
}
break
}

case "showAllWorkspacesTasks":
await updateGlobalState("showAllWorkspacesTasks", message.bool ?? false)
await provider.postStateToWebview()
break
}
}
2 changes: 2 additions & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface ExtensionMessage {
| "shareTaskSuccess"
| "codeIndexSettingsSaved"
| "codeIndexSecretStatus"
| "showAllWorkspacesTasks"
text?: string
payload?: any // Add a generic payload for now, can refine later
action?:
Expand Down Expand Up @@ -231,6 +232,7 @@ export type ExtensionState = Pick<
version: string
clineMessages: ClineMessage[]
currentTaskItem?: HistoryItem
showAllWorkspacesTasks: boolean
apiConfiguration?: ProviderSettings
uriScheme?: string
shouldShowAnnouncement: boolean
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export interface WebviewMessage {
| "checkRulesDirectoryResult"
| "saveCodeIndexSettingsAtomic"
| "requestCodeIndexSecretStatus"
| "showAllWorkspacesTasks"
text?: string
editedMessageContent?: string
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
Expand Down
21 changes: 15 additions & 6 deletions webview-ui/src/components/history/HistoryView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { memo, useState } from "react"

import { vscode } from "@/utils/vscode"
import { DeleteTaskDialog } from "./DeleteTaskDialog"
import { BatchDeleteTaskDialog } from "./BatchDeleteTaskDialog"
import { Virtuoso } from "react-virtuoso"
Expand Down Expand Up @@ -35,8 +37,8 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
sortOption,
setSortOption,
setLastNonRelevantSort,
showAllWorkspaces,
setShowAllWorkspaces,
showAllWorkspacesTasks,
setShowAllWorkspacesTasks,
} = useTaskSearch()
const { t } = useAppTranslation()

Expand Down Expand Up @@ -129,12 +131,19 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
</VSCodeTextField>
<div className="flex gap-2">
<Select
value={showAllWorkspaces ? "all" : "current"}
onValueChange={(value) => setShowAllWorkspaces(value === "all")}>
value={showAllWorkspacesTasks ? "all" : "current"}
onValueChange={(value) => {
const showAll = value === "all"
setShowAllWorkspacesTasks(showAll)
vscode.postMessage({
type: "showAllWorkspacesTasks",
bool: showAll,
})
}}>
<SelectTrigger className="flex-1">
<SelectValue>
{t("history:workspace.prefix")}{" "}
{t(`history:workspace.${showAllWorkspaces ? "all" : "current"}`)}
{t(`history:workspace.${showAllWorkspacesTasks ? "all" : "current"}`)}
</SelectValue>
</SelectTrigger>
<SelectContent>
Expand Down Expand Up @@ -238,7 +247,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
key={item.id}
item={item}
variant="full"
showWorkspace={showAllWorkspaces}
showWorkspace={showAllWorkspacesTasks}
isSelectionMode={isSelectionMode}
isSelected={selectedTaskIds.includes(item.id)}
onToggleSelection={toggleTaskSelection}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,20 @@ const mockTaskHistory: HistoryItem[] = [
]

describe("useTaskSearch", () => {
let showAllWorkspacesTasks = false
const setShowAllWorkspacesTasks = vi.fn().mockImplementation((value) => {
showAllWorkspacesTasks = value
})

beforeEach(() => {
vi.clearAllMocks()
mockUseExtensionState.mockReturnValue({
showAllWorkspacesTasks = false
mockUseExtensionState.mockImplementation(() => ({
taskHistory: mockTaskHistory,
cwd: "/workspace/project1",
} as any)
showAllWorkspacesTasks,
setShowAllWorkspacesTasks,
}))
})

it("returns all tasks by default", () => {
Expand All @@ -75,23 +83,25 @@ describe("useTaskSearch", () => {
expect(result.current.tasks.every((task) => task.workspace === "/workspace/project1")).toBe(true)
})

it("shows all workspaces when showAllWorkspaces is true", () => {
const { result } = renderHook(() => useTaskSearch())
it("shows all workspaces when showAllWorkspacesTasks is true", () => {
const { result, rerender } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
})
rerender()

expect(result.current.tasks).toHaveLength(3)
expect(result.current.showAllWorkspaces).toBe(true)
expect(result.current.showAllWorkspacesTasks).toBe(true)
})

it("sorts by newest by default", () => {
const { result } = renderHook(() => useTaskSearch())
const { result, rerender } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
})
rerender()

expect(result.current.sortOption).toBe("newest")
expect(result.current.tasks[0].id).toBe("task-2") // Feb 17
Expand All @@ -100,38 +110,41 @@ describe("useTaskSearch", () => {
})

it("sorts by oldest", () => {
const { result } = renderHook(() => useTaskSearch())
const { result, rerender } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
result.current.setSortOption("oldest")
})
rerender()

expect(result.current.tasks[0].id).toBe("task-3") // Feb 15
expect(result.current.tasks[1].id).toBe("task-1") // Feb 16
expect(result.current.tasks[2].id).toBe("task-2") // Feb 17
})

it("sorts by most expensive", () => {
const { result } = renderHook(() => useTaskSearch())
const { result, rerender } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
result.current.setSortOption("mostExpensive")
})
rerender()

expect(result.current.tasks[0].id).toBe("task-3") // $0.05
expect(result.current.tasks[1].id).toBe("task-2") // $0.02
expect(result.current.tasks[2].id).toBe("task-1") // $0.01
})

it("sorts by most tokens", () => {
const { result } = renderHook(() => useTaskSearch())
const { result, rerender } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
result.current.setSortOption("mostTokens")
})
rerender()

// task-2: 200 + 100 + 25 + 10 = 335 tokens
// task-3: 150 + 75 = 225 tokens
Expand All @@ -145,7 +158,7 @@ describe("useTaskSearch", () => {
const { result } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
result.current.setSearchQuery("React")
})

Expand Down Expand Up @@ -244,16 +257,19 @@ describe("useTaskSearch", () => {
},
] as HistoryItem[]

mockUseExtensionState.mockReturnValue({
mockUseExtensionState.mockImplementation(() => ({
taskHistory: incompleteTaskHistory,
cwd: "/workspace/project1",
} as any)
showAllWorkspacesTasks,
setShowAllWorkspacesTasks,
}))

const { result } = renderHook(() => useTaskSearch())
const { result, rerender } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
})
rerender()

// Should only include tasks with both ts and task content
expect(result.current.tasks).toHaveLength(3)
Expand All @@ -264,7 +280,7 @@ describe("useTaskSearch", () => {
const { result } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
result.current.setSearchQuery("nonexistent")
})

Expand All @@ -275,7 +291,7 @@ describe("useTaskSearch", () => {
const { result } = renderHook(() => useTaskSearch())

act(() => {
result.current.setShowAllWorkspaces(true)
result.current.setShowAllWorkspacesTasks(true)
result.current.setSearchQuery("test")
result.current.setSortOption("mostRelevant")
})
Expand Down
11 changes: 5 additions & 6 deletions webview-ui/src/components/history/useTaskSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import { useExtensionState } from "@/context/ExtensionStateContext"
type SortOption = "newest" | "oldest" | "mostExpensive" | "mostTokens" | "mostRelevant"

export const useTaskSearch = () => {
const { taskHistory, cwd } = useExtensionState()
const { taskHistory, cwd, showAllWorkspacesTasks, setShowAllWorkspacesTasks } = useExtensionState()
const [searchQuery, setSearchQuery] = useState("")
const [sortOption, setSortOption] = useState<SortOption>("newest")
const [lastNonRelevantSort, setLastNonRelevantSort] = useState<SortOption | null>("newest")
const [showAllWorkspaces, setShowAllWorkspaces] = useState(false)

useEffect(() => {
if (searchQuery && sortOption !== "mostRelevant" && !lastNonRelevantSort) {
Expand All @@ -25,11 +24,11 @@ export const useTaskSearch = () => {

const presentableTasks = useMemo(() => {
let tasks = taskHistory.filter((item) => item.ts && item.task)
if (!showAllWorkspaces) {
if (!showAllWorkspacesTasks) {
tasks = tasks.filter((item) => item.workspace === cwd)
}
return tasks
}, [taskHistory, showAllWorkspaces, cwd])
}, [taskHistory, showAllWorkspacesTasks, cwd])

const fzf = useMemo(() => {
return new Fzf(presentableTasks, {
Expand Down Expand Up @@ -86,7 +85,7 @@ export const useTaskSearch = () => {
setSortOption,
lastNonRelevantSort,
setLastNonRelevantSort,
showAllWorkspaces,
setShowAllWorkspaces,
showAllWorkspacesTasks,
setShowAllWorkspacesTasks,
}
}
33 changes: 33 additions & 0 deletions webview-ui/src/components/settings/GeneralSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { useAppTranslation } from "@src/i18n/TranslationContext"

import { ExtensionStateContextType } from "@/context/ExtensionStateContext"
import { SetCachedStateField } from "./types"
import { Section } from "./Section"

type GeneralProps = {
showAllWorkspacesTasks: boolean
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
}

export const GeneralSettings = ({ showAllWorkspacesTasks, setCachedStateField }: GeneralProps) => {
const { t } = useAppTranslation()

return (
<Section>
<div>
<VSCodeCheckbox
checked={showAllWorkspacesTasks}
onChange={(e) => {
const checked = (e.target as HTMLInputElement).checked
setCachedStateField("showAllWorkspacesTasks", checked)
}}>
<span className="font-medium">{t("settings:general.showAllWorkspacesTasks.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
{t("settings:general.showAllWorkspacesTasks.description")}
</div>
</div>
</Section>
)
}
Loading