Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
32 changes: 28 additions & 4 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,33 @@ export class ClineProvider
}
}

/**
* Fetches marketplace data on demand to avoid blocking main state updates
*/
async fetchMarketplaceData() {
try {
const [marketplaceItems, marketplaceInstalledMetadata] = await Promise.all([
this.marketplaceManager.getCurrentItems().catch(() => []),
this.marketplaceManager.getInstallationMetadata().catch(() => ({ project: {}, global: {} })),
])

// Send marketplace data separately
this.postMessageToWebview({
type: "marketplaceData",
marketplaceItems: marketplaceItems || [],
marketplaceInstalledMetadata: marketplaceInstalledMetadata || { project: {}, global: {} },
})
} catch (error) {
console.error("Failed to fetch marketplace data:", error)
// Send empty data on error to prevent UI from hanging
this.postMessageToWebview({
type: "marketplaceData",
marketplaceItems: [],
marketplaceInstalledMetadata: { project: {}, global: {} },
})
}
}

/**
* Checks if there is a file-based system prompt override for the given mode
*/
Expand Down Expand Up @@ -1342,13 +1369,10 @@ export class ClineProvider
const allowedCommands = vscode.workspace.getConfiguration(Package.name).get<string[]>("allowedCommands") || []
const cwd = this.cwd

// Fetch marketplace data
// Initialize marketplace data as empty - will be fetched on demand
let marketplaceItems: any[] = []
let marketplaceInstalledMetadata: any = { project: {}, global: {} }

marketplaceItems = (await this.marketplaceManager.getCurrentItems()) || []
marketplaceInstalledMetadata = await this.marketplaceManager.getInstallationMetadata()

// Check if there's a system prompt override for the current mode
const currentMode = mode ?? defaultModeSlug
const hasSystemPromptOverride = await this.hasFileBasedSystemPromptOverride(currentMode)
Expand Down
6 changes: 6 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,12 @@ export const webviewMessageHandler = async (
break
}

case "fetchMarketplaceData": {
// Fetch marketplace data on demand
await provider.fetchMarketplaceData()
break
}

case "installMarketplaceItem": {
if (marketplaceManager && message.mpItem && message.mpInstallOptions) {
try {
Expand Down
3 changes: 3 additions & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export interface ExtensionMessage {
| "indexCleared"
| "codebaseIndexConfig"
| "marketplaceInstallResult"
| "marketplaceData"
text?: string
payload?: any // Add a generic payload for now, can refine later
action?:
Expand Down Expand Up @@ -136,6 +137,8 @@ export interface ExtensionMessage {
userInfo?: CloudUserInfo
organizationAllowList?: OrganizationAllowList
tab?: string
marketplaceItems?: MarketplaceItem[]
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
}

export type ExtensionState = Pick<
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export interface WebviewMessage {
| "cancelMarketplaceInstall"
| "removeInstalledMarketplaceItem"
| "marketplaceInstallResult"
| "fetchMarketplaceData"
| "switchTab"
text?: string
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
Expand Down
9 changes: 2 additions & 7 deletions webview-ui/src/components/marketplace/MarketplaceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,9 @@ export function MarketplaceView({ stateManager, onDone }: MarketplaceViewProps)
// a filter message with empty filters, which will cause the extension to
// send back the full state including all marketplace items.
if (!hasReceivedInitialState && state.allItems.length === 0) {
// Send empty filter to trigger state update
// Fetch marketplace data on demand
vscode.postMessage({
type: "filterMarketplaceItems",
filters: {
type: "",
search: "",
tags: [],
},
type: "fetchMarketplaceData",
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,5 +341,28 @@ export class MarketplaceViewStateManager {
void this.transition({ type: "FETCH_ITEMS" })
}
}

// Handle marketplace data updates (fetched on demand)
if (message.type === "marketplaceData") {
const marketplaceItems = message.marketplaceItems

if (marketplaceItems !== undefined) {
// Always use the marketplace items from the extension when they're provided
// This ensures fresh data is always displayed
const items = [...marketplaceItems]
const newDisplayItems = this.isFilterActive() ? this.filterItems(items) : items

// Update state in a single operation
this.state = {
...this.state,
isFetching: false,
allItems: items,
displayItems: newDisplayItems,
}
}

// Notify state change
this.notifyStateChange()
}
}
}
25 changes: 25 additions & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setCondensingApiConfigId: (value: string) => void
customCondensingPrompt?: string
setCustomCondensingPrompt: (value: string) => void
marketplaceItems?: any[]
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
setApiConfiguration: (config: ProviderSettings) => void
setCustomInstructions: (value?: string) => void
setAlwaysAllowReadOnly: (value: boolean) => void
Expand Down Expand Up @@ -217,6 +219,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
const [mcpServers, setMcpServers] = useState<McpServer[]>([])
const [currentCheckpoint, setCurrentCheckpoint] = useState<string>()
const [extensionRouterModels, setExtensionRouterModels] = useState<RouterModels | undefined>(undefined)
const [marketplaceItems, setMarketplaceItems] = useState<any[]>([])
const [marketplaceInstalledMetadata, setMarketplaceInstalledMetadata] = useState<{
project: Record<string, any>
global: Record<string, any>
}>({ project: {}, global: {} })

const setListApiConfigMeta = useCallback(
(value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
Expand All @@ -242,6 +249,13 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setState((prevState) => mergeExtensionState(prevState, newState))
setShowWelcome(!checkExistKey(newState.apiConfiguration))
setDidHydrateState(true)
// Handle marketplace data if present in state message
if (newState.marketplaceItems !== undefined) {
setMarketplaceItems(newState.marketplaceItems)
}
if (newState.marketplaceInstalledMetadata !== undefined) {
setMarketplaceInstalledMetadata(newState.marketplaceInstalledMetadata)
}
break
}
case "theme": {
Expand Down Expand Up @@ -288,6 +302,15 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setExtensionRouterModels(message.routerModels)
break
}
case "marketplaceData": {
if (message.marketplaceItems !== undefined) {
setMarketplaceItems(message.marketplaceItems)
}
if (message.marketplaceInstalledMetadata !== undefined) {
setMarketplaceInstalledMetadata(message.marketplaceInstalledMetadata)
}
break
}
}
},
[setListApiConfigMeta],
Expand Down Expand Up @@ -320,6 +343,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
screenshotQuality: state.screenshotQuality,
routerModels: extensionRouterModels,
cloudIsAuthenticated: state.cloudIsAuthenticated ?? false,
marketplaceItems,
marketplaceInstalledMetadata,
setExperimentEnabled: (id, enabled) =>
setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })),
setApiConfiguration,
Expand Down