diff --git a/src/i18n/locales/ca/marketplace.json b/src/i18n/locales/ca/marketplace.json
index 6c64374447..cf0710ac97 100644
--- a/src/i18n/locales/ca/marketplace.json
+++ b/src/i18n/locales/ca/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "No s'han trobat etiquetes.",
"selected": "Mostrant elements amb qualsevol de les etiquetes seleccionades"
},
+ "installed": {
+ "label": "Filtra per estat",
+ "all": "Tots els articles",
+ "installed": "Instal·lats",
+ "notInstalled": "No instal·lats"
+ },
"title": "Marketplace"
},
"done": "Fet",
diff --git a/src/i18n/locales/de/marketplace.json b/src/i18n/locales/de/marketplace.json
index 2981441cf0..dea82aa44c 100644
--- a/src/i18n/locales/de/marketplace.json
+++ b/src/i18n/locales/de/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Keine Tags gefunden.",
"selected": "Zeige Elemente mit einem der ausgewählten Tags"
},
+ "installed": {
+ "label": "Nach Status filtern",
+ "all": "Alle Artikel",
+ "installed": "Installierte",
+ "notInstalled": "Nicht installiert"
+ },
"title": "Marketplace"
},
"done": "Fertig",
diff --git a/src/i18n/locales/en/marketplace.json b/src/i18n/locales/en/marketplace.json
index f141aaeb05..17ae20078a 100644
--- a/src/i18n/locales/en/marketplace.json
+++ b/src/i18n/locales/en/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "No tags found.",
"selected": "Showing items with any of the selected tags"
},
+ "installed": {
+ "label": "Filter by status",
+ "all": "All Items",
+ "installed": "Installed",
+ "notInstalled": "Not Installed"
+ },
"title": "Marketplace"
},
"done": "Done",
diff --git a/src/i18n/locales/es/marketplace.json b/src/i18n/locales/es/marketplace.json
index e12e1d1dc1..ecee7c1645 100644
--- a/src/i18n/locales/es/marketplace.json
+++ b/src/i18n/locales/es/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "No se encontraron etiquetas.",
"selected": "Mostrando elementos con cualquiera de las etiquetas seleccionadas"
},
+ "installed": {
+ "label": "Filtrar por estado",
+ "all": "Todos los artículos",
+ "installed": "Instalados",
+ "notInstalled": "No instalados"
+ },
"title": "Marketplace"
},
"done": "Hecho",
diff --git a/src/i18n/locales/fr/marketplace.json b/src/i18n/locales/fr/marketplace.json
index 7a42b0033e..7a6a3dbd74 100644
--- a/src/i18n/locales/fr/marketplace.json
+++ b/src/i18n/locales/fr/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Aucune étiquette trouvée.",
"selected": "Affichage des éléments avec l'une des étiquettes sélectionnées"
},
+ "installed": {
+ "label": "Filtrer par statut",
+ "all": "Tous les articles",
+ "installed": "Installés",
+ "notInstalled": "Non installés"
+ },
"title": "Marketplace"
},
"done": "Terminé",
diff --git a/src/i18n/locales/hi/marketplace.json b/src/i18n/locales/hi/marketplace.json
index 94013c20e4..3797c50b01 100644
--- a/src/i18n/locales/hi/marketplace.json
+++ b/src/i18n/locales/hi/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "कोई टैग नहीं मिले।",
"selected": "चयनित टैग्स में से किसी भी के साथ आइटम दिखा रहे हैं"
},
+ "installed": {
+ "label": "स्थिति के अनुसार फ़िल्टर करें",
+ "all": "सभी आइटम",
+ "installed": "स्थापित",
+ "notInstalled": "स्थापित नहीं"
+ },
"title": "मार्केटप्लेस"
},
"done": "हो गया",
diff --git a/src/i18n/locales/id/marketplace.json b/src/i18n/locales/id/marketplace.json
index 77d93973a9..7f31c78cbf 100644
--- a/src/i18n/locales/id/marketplace.json
+++ b/src/i18n/locales/id/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Tidak ada tag ditemukan.",
"selected": "Menampilkan item dengan salah satu tag yang dipilih"
},
+ "installed": {
+ "label": "Filter berdasarkan status",
+ "all": "Semua Item",
+ "installed": "Terpasang",
+ "notInstalled": "Tidak Terpasang"
+ },
"title": "Marketplace"
},
"done": "Selesai",
diff --git a/src/i18n/locales/it/marketplace.json b/src/i18n/locales/it/marketplace.json
index 3cdcd2b76c..46c50acc7d 100644
--- a/src/i18n/locales/it/marketplace.json
+++ b/src/i18n/locales/it/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Nessun tag trovato.",
"selected": "Mostrando elementi con uno qualsiasi dei tag selezionati"
},
+ "installed": {
+ "label": "Filtra per stato",
+ "all": "Tutti gli articoli",
+ "installed": "Installati",
+ "notInstalled": "Non installati"
+ },
"title": "Marketplace"
},
"done": "Fatto",
diff --git a/src/i18n/locales/ja/marketplace.json b/src/i18n/locales/ja/marketplace.json
index 26cff2ade9..426a5db1fd 100644
--- a/src/i18n/locales/ja/marketplace.json
+++ b/src/i18n/locales/ja/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "タグが見つかりません。",
"selected": "選択されたタグのいずれかを持つアイテムを表示"
},
+ "installed": {
+ "label": "ステータスで絞り込む",
+ "all": "すべてのアイテム",
+ "installed": "インストール済み",
+ "notInstalled": "未インストール"
+ },
"title": "マーケットプレイス"
},
"done": "完了",
diff --git a/src/i18n/locales/ko/marketplace.json b/src/i18n/locales/ko/marketplace.json
index 52bd03edf7..349f0c5ad8 100644
--- a/src/i18n/locales/ko/marketplace.json
+++ b/src/i18n/locales/ko/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "태그를 찾을 수 없습니다.",
"selected": "선택된 태그 중 하나를 가진 항목 표시"
},
+ "installed": {
+ "label": "상태별로 필터링",
+ "all": "모든 항목",
+ "installed": "설치됨",
+ "notInstalled": "설치되지 않음"
+ },
"title": "마켓플레이스"
},
"done": "완료",
diff --git a/src/i18n/locales/nl/marketplace.json b/src/i18n/locales/nl/marketplace.json
index 5628b8f628..1274666b9c 100644
--- a/src/i18n/locales/nl/marketplace.json
+++ b/src/i18n/locales/nl/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Geen tags gevonden.",
"selected": "Items tonen met een van de geselecteerde tags"
},
+ "installed": {
+ "label": "Filteren op status",
+ "all": "Alle items",
+ "installed": "Geïnstalleerd",
+ "notInstalled": "Niet geïnstalleerd"
+ },
"title": "Marketplace"
},
"done": "Klaar",
diff --git a/src/i18n/locales/pl/marketplace.json b/src/i18n/locales/pl/marketplace.json
index 029dbd95a3..3ba3c076ad 100644
--- a/src/i18n/locales/pl/marketplace.json
+++ b/src/i18n/locales/pl/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Nie znaleziono tagów.",
"selected": "Pokazywanie elementów z dowolnym z wybranych tagów"
},
+ "installed": {
+ "label": "Filtruj według statusu",
+ "all": "Wszystkie elementy",
+ "installed": "Zainstalowane",
+ "notInstalled": "Niezainstalowane"
+ },
"title": "Marketplace"
},
"done": "Gotowe",
diff --git a/src/i18n/locales/pt-BR/marketplace.json b/src/i18n/locales/pt-BR/marketplace.json
index b0af013888..b0e763b0ff 100644
--- a/src/i18n/locales/pt-BR/marketplace.json
+++ b/src/i18n/locales/pt-BR/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Nenhuma tag encontrada.",
"selected": "Mostrando itens com qualquer uma das tags selecionadas"
},
+ "installed": {
+ "label": "Filtrar por status",
+ "all": "Todos os itens",
+ "installed": "Instalados",
+ "notInstalled": "Não instalados"
+ },
"title": "Marketplace"
},
"done": "Concluído",
diff --git a/src/i18n/locales/ru/marketplace.json b/src/i18n/locales/ru/marketplace.json
index a84b1ce3e9..1c4ba45df5 100644
--- a/src/i18n/locales/ru/marketplace.json
+++ b/src/i18n/locales/ru/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Теги не найдены.",
"selected": "Показ элементов с любым из выбранных тегов"
},
+ "installed": {
+ "label": "Фильтр по статусу",
+ "all": "Все элементы",
+ "installed": "Установленные",
+ "notInstalled": "Не установленные"
+ },
"title": "Marketplace"
},
"done": "Готово",
diff --git a/src/i18n/locales/tr/marketplace.json b/src/i18n/locales/tr/marketplace.json
index b08381d71c..628c239a12 100644
--- a/src/i18n/locales/tr/marketplace.json
+++ b/src/i18n/locales/tr/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Etiket bulunamadı.",
"selected": "Seçilen etiketlerden herhangi birine sahip öğeleri göster"
},
+ "installed": {
+ "label": "Duruma göre filtrele",
+ "all": "Tüm Öğeler",
+ "installed": "Yüklü",
+ "notInstalled": "Yüklü Değil"
+ },
"title": "Marketplace"
},
"done": "Tamam",
diff --git a/src/i18n/locales/vi/marketplace.json b/src/i18n/locales/vi/marketplace.json
index be39054309..8fbc1a7ad0 100644
--- a/src/i18n/locales/vi/marketplace.json
+++ b/src/i18n/locales/vi/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "Không tìm thấy thẻ nào.",
"selected": "Hiển thị các mục có bất kỳ thẻ nào được chọn"
},
+ "installed": {
+ "label": "Lọc theo trạng thái",
+ "all": "Tất cả các mục",
+ "installed": "Đã cài đặt",
+ "notInstalled": "Chưa cài đặt"
+ },
"title": "Marketplace"
},
"done": "Hoàn thành",
diff --git a/src/i18n/locales/zh-CN/marketplace.json b/src/i18n/locales/zh-CN/marketplace.json
index 50a2ade635..a69e9c1e9d 100644
--- a/src/i18n/locales/zh-CN/marketplace.json
+++ b/src/i18n/locales/zh-CN/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "未找到标签。",
"selected": "显示包含任一选中标签的项目"
},
+ "installed": {
+ "label": "按状态筛选",
+ "all": "所有项目",
+ "installed": "已安装",
+ "notInstalled": "未安装"
+ },
"title": "Marketplace"
},
"done": "完成",
diff --git a/src/i18n/locales/zh-TW/marketplace.json b/src/i18n/locales/zh-TW/marketplace.json
index 3eaeb0e7e0..9270314108 100644
--- a/src/i18n/locales/zh-TW/marketplace.json
+++ b/src/i18n/locales/zh-TW/marketplace.json
@@ -38,6 +38,12 @@
"noResults": "找不到標籤。",
"selected": "顯示包含任一選取標籤的項目"
},
+ "installed": {
+ "label": "按狀態篩選",
+ "all": "所有項目",
+ "installed": "已安裝",
+ "notInstalled": "未安裝"
+ },
"title": "Marketplace"
},
"done": "完成",
diff --git a/webview-ui/src/components/marketplace/MarketplaceListView.tsx b/webview-ui/src/components/marketplace/MarketplaceListView.tsx
index 8ec13be8e5..c3c497ccfe 100644
--- a/webview-ui/src/components/marketplace/MarketplaceListView.tsx
+++ b/webview-ui/src/components/marketplace/MarketplaceListView.tsx
@@ -1,6 +1,7 @@
import * as React from "react"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"
import { X, ChevronsUpDown } from "lucide-react"
@@ -27,6 +28,9 @@ export function MarketplaceListView({ stateManager, allTags, filteredTags, filte
const allItems = state.displayItems || []
const organizationMcps = state.displayOrganizationMcps || []
+ // NOTE: installed metadata is already synchronized into the state manager via handleMessage("state"/"marketplaceData")
+ // in MarketplaceViewStateManager; avoid dispatching UPDATE_FILTERS here to prevent render loops.
+
// Filter items by type if specified
const items = filterByType ? allItems.filter((item) => item.type === filterByType) : allItems
const orgMcps = filterByType === "mcp" ? organizationMcps : []
@@ -55,115 +59,132 @@ export function MarketplaceListView({ stateManager, allTags, filteredTags, filte
}
/>
- {allTags.length > 0 && (
-
-
-
-
-
- {state.filters.tags.length > 0 && (
-
- )}
-
-
-
setIsTagPopoverOpen(open)}>
-
-
-
- e.stopPropagation()}>
-
-
-
- {tagSearch && (
-
- )}
-
-
-
- {t("marketplace:filters.tags.noResults")}
-
-
- {filteredTags.map((tag: string) => (
- {
- const isSelected = state.filters.tags.includes(tag)
- manager.transition({
- type: "UPDATE_FILTERS",
- payload: {
- filters: {
- tags: isSelected
- ? state.filters.tags.filter((t) => t !== tag)
- : [...state.filters.tags, tag],
+
+
+ {allTags.length > 0 && (
+
+
setIsTagPopoverOpen(open)}>
+
+
+
+ e.stopPropagation()}>
+
+
+
+ {tagSearch && (
+
+ )}
+
+
+
+ {t("marketplace:filters.tags.noResults")}
+
+
+ {filteredTags.map((tag: string) => (
+ {
+ const isSelected = state.filters.tags.includes(tag)
+ manager.transition({
+ type: "UPDATE_FILTERS",
+ payload: {
+ filters: {
+ tags: isSelected
+ ? state.filters.tags.filter(
+ (t) => t !== tag,
+ )
+ : [...state.filters.tags, tag],
+ },
},
- },
- })
- }}
- data-selected={state.filters.tags.includes(tag)}
- className="grid grid-cols-[1rem_1fr] gap-2 cursor-pointer text-sm capitalize"
- onMouseDown={(e) => {
- e.stopPropagation()
- e.preventDefault()
- }}>
- {state.filters.tags.includes(tag) ? (
-
- ) : (
-
- )}
- {tag}
-
- ))}
-
-
-
-
-
- {state.filters.tags.length > 0 && (
-
-
- {t("marketplace:filters.tags.selected")}
-
- )}
+ })
+ }}
+ data-selected={state.filters.tags.includes(tag)}
+ className="grid grid-cols-[1rem_1fr] gap-2 cursor-pointer text-sm capitalize"
+ onMouseDown={(e) => {
+ e.stopPropagation()
+ e.preventDefault()
+ }}>
+ {state.filters.tags.includes(tag) ? (
+
+ ) : (
+
+ )}
+ {tag}
+
+ ))}
+
+
+
+
+
+
+ )}
+
+ {state.filters.tags.length > 0 && (
+
+
+
+ {t("marketplace:filters.tags.selected")}
+
+
)}
@@ -187,7 +208,7 @@ export function MarketplaceListView({ stateManager, allTags, filteredTags, filte
onClick={() =>
manager.transition({
type: "UPDATE_FILTERS",
- payload: { filters: { search: "", type: "", tags: [] } },
+ payload: { filters: { search: "", type: "", tags: [], installed: "all" } },
})
}
className="mt-4 bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground hover:bg-vscode-button-secondaryHoverBackground transition-colors">
diff --git a/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts b/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts
index 104f3e7cad..995e982164 100644
--- a/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts
+++ b/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts
@@ -14,6 +14,7 @@
import { MarketplaceItem } from "@roo-code/types"
import { vscode } from "../../utils/vscode"
import { WebviewMessage } from "../../../../src/shared/WebviewMessage"
+import type { MarketplaceInstalledMetadata } from "../../../../src/shared/ExtensionMessage"
export interface ViewState {
allItems: MarketplaceItem[]
@@ -26,7 +27,9 @@ export interface ViewState {
type: string
search: string
tags: string[]
+ installed: "all" | "installed" | "not_installed" // Filter by installation status
}
+ installedMetadata?: MarketplaceInstalledMetadata // Store installed metadata for filtering
}
type TransitionPayloads = {
@@ -65,6 +68,7 @@ export class MarketplaceViewStateManager {
type: "",
search: "",
tags: [],
+ installed: "all",
},
}
}
@@ -189,8 +193,11 @@ export class MarketplaceViewStateManager {
let newDisplayItems: MarketplaceItem[]
let newDisplayOrganizationMcps: MarketplaceItem[]
if (this.isFilterActive()) {
- newDisplayItems = this.filterItems([...items])
- newDisplayOrganizationMcps = this.filterItems([...this.state.organizationMcps])
+ newDisplayItems = this.filterItems([...items], this.state.installedMetadata)
+ newDisplayOrganizationMcps = this.filterItems(
+ [...this.state.organizationMcps],
+ this.state.installedMetadata,
+ )
} else {
// No filters active - show all items
newDisplayItems = [...items]
@@ -251,6 +258,7 @@ export class MarketplaceViewStateManager {
type: filters.type !== undefined ? filters.type : this.state.filters.type,
search: filters.search !== undefined ? filters.search : this.state.filters.search,
tags: filters.tags !== undefined ? filters.tags : this.state.filters.tags,
+ installed: filters.installed !== undefined ? filters.installed : this.state.filters.installed,
}
// Update filters first
@@ -260,8 +268,11 @@ export class MarketplaceViewStateManager {
}
// Apply filters to displayItems and displayOrganizationMcps with the updated filters
- const newDisplayItems = this.filterItems(this.state.allItems)
- const newDisplayOrganizationMcps = this.filterItems(this.state.organizationMcps)
+ const newDisplayItems = this.filterItems(this.state.allItems, this.state.installedMetadata)
+ const newDisplayOrganizationMcps = this.filterItems(
+ this.state.organizationMcps,
+ this.state.installedMetadata,
+ )
// Update state with filtered items
this.state = {
@@ -284,39 +295,54 @@ export class MarketplaceViewStateManager {
}
public isFilterActive(): boolean {
- return !!(this.state.filters.type || this.state.filters.search || this.state.filters.tags.length > 0)
+ return !!(
+ this.state.filters.type ||
+ this.state.filters.search ||
+ this.state.filters.tags.length > 0 ||
+ this.state.filters.installed !== "all"
+ )
}
- public filterItems(items: MarketplaceItem[]): MarketplaceItem[] {
- const { type, search, tags } = this.state.filters
+ public filterItems(items: MarketplaceItem[], installedMetadata?: MarketplaceInstalledMetadata): MarketplaceItem[] {
+ const { type, search, tags, installed } = this.state.filters
+ const searchLower = search?.toLowerCase()
- return items
- .map((item) => {
- // Create a copy of the item to modify
- const itemCopy = { ...item }
+ return items.filter((item) => {
+ // Check type match
+ if (type && item.type !== type) {
+ return false
+ }
- // Check specific match conditions for the main item
- const typeMatch = !type || item.type === type
- const nameMatch = search ? item.name.toLowerCase().includes(search.toLowerCase()) : false
- const descriptionMatch = search
- ? (item.description || "").toLowerCase().includes(search.toLowerCase())
- : false
- const tagMatch = tags.length > 0 ? item.tags?.some((tag) => tags.includes(tag)) : false
-
- // Determine if the main item matches all filters
- const mainItemMatches =
- typeMatch && (!search || nameMatch || descriptionMatch) && (!tags.length || tagMatch)
-
- const hasMatchingSubcomponents = false
-
- // Return the item if it matches or has matching subcomponents
- if (mainItemMatches || Boolean(hasMatchingSubcomponents)) {
- return itemCopy
+ // Check search match
+ if (searchLower) {
+ const nameMatch = item.name.toLowerCase().includes(searchLower)
+ const descriptionMatch = (item.description || "").toLowerCase().includes(searchLower)
+ if (!nameMatch && !descriptionMatch) {
+ return false
}
+ }
- return null
- })
- .filter((item): item is MarketplaceItem => item !== null)
+ // Check tag match
+ if (tags.length > 0 && !item.tags?.some((tag) => tags.includes(tag))) {
+ return false
+ }
+
+ // Check installed status if filter is active
+ if (installed !== "all" && installedMetadata) {
+ const isInstalledGlobally = !!installedMetadata?.global?.[item.id]
+ const isInstalledInProject = !!installedMetadata?.project?.[item.id]
+ const isInstalled = isInstalledGlobally || isInstalledInProject
+
+ if (installed === "installed" && !isInstalled) {
+ return false
+ }
+ if (installed === "not_installed" && isInstalled) {
+ return false
+ }
+ }
+
+ return true
+ })
}
public async handleMessage(message: any): Promise {
@@ -343,20 +369,29 @@ export class MarketplaceViewStateManager {
// Handle state updates for marketplace items
// The state.marketplaceItems come from ClineProvider, see the file src/core/webview/ClineProvider.ts
const marketplaceItems = message.state.marketplaceItems
+ const marketplaceInstalledMetadata = message.state.marketplaceInstalledMetadata
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]
+ // Update installed metadata if provided
+ if (marketplaceInstalledMetadata !== undefined) {
+ this.state.installedMetadata = marketplaceInstalledMetadata
+ }
+
// Calculate display items based on current filters
// If no filters are active, show all items
// If filters are active, apply filtering
let newDisplayItems: MarketplaceItem[]
let newDisplayOrganizationMcps: MarketplaceItem[]
if (this.isFilterActive()) {
- newDisplayItems = this.filterItems(items)
- newDisplayOrganizationMcps = this.filterItems(this.state.organizationMcps)
+ newDisplayItems = this.filterItems(items, this.state.installedMetadata)
+ newDisplayOrganizationMcps = this.filterItems(
+ this.state.organizationMcps,
+ this.state.installedMetadata,
+ )
} else {
// No filters active - show all items
newDisplayItems = items
@@ -370,6 +405,7 @@ export class MarketplaceViewStateManager {
allItems: items,
displayItems: newDisplayItems,
displayOrganizationMcps: newDisplayOrganizationMcps,
+ installedMetadata: marketplaceInstalledMetadata || this.state.installedMetadata,
}
// Notification is handled below after all state parts are processed
}
@@ -411,14 +447,25 @@ export class MarketplaceViewStateManager {
if (message.type === "marketplaceData") {
const marketplaceItems = message.marketplaceItems
const organizationMcps = message.organizationMcps || []
+ const marketplaceInstalledMetadata = message.marketplaceInstalledMetadata
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 orgMcps = [...organizationMcps]
- const newDisplayItems = this.isFilterActive() ? this.filterItems(items) : items
- const newDisplayOrganizationMcps = this.isFilterActive() ? this.filterItems(orgMcps) : orgMcps
+
+ // Update installed metadata if provided
+ if (marketplaceInstalledMetadata !== undefined) {
+ this.state.installedMetadata = marketplaceInstalledMetadata
+ }
+
+ const newDisplayItems = this.isFilterActive()
+ ? this.filterItems(items, this.state.installedMetadata)
+ : items
+ const newDisplayOrganizationMcps = this.isFilterActive()
+ ? this.filterItems(orgMcps, this.state.installedMetadata)
+ : orgMcps
// Update state in a single operation
this.state = {
@@ -428,6 +475,7 @@ export class MarketplaceViewStateManager {
organizationMcps: orgMcps,
displayItems: newDisplayItems,
displayOrganizationMcps: newDisplayOrganizationMcps,
+ installedMetadata: marketplaceInstalledMetadata || this.state.installedMetadata,
}
}
diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.spec.tsx b/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.spec.tsx
index d22c381410..02f94001d1 100644
--- a/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.spec.tsx
+++ b/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.spec.tsx
@@ -27,6 +27,7 @@ const mockState: ViewState = {
type: "",
search: "",
tags: [],
+ installed: "all",
},
}
diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.spec.ts b/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.spec.ts
index 089226ccc3..a6ffc59518 100644
--- a/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.spec.ts
+++ b/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.spec.ts
@@ -64,6 +64,7 @@ describe("MarketplaceViewStateManager", () => {
type: "",
search: "",
tags: [],
+ installed: "all",
})
})
diff --git a/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.spec.tsx b/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.spec.tsx
index 1f1ed9030b..4fdc685192 100644
--- a/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.spec.tsx
+++ b/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.spec.tsx
@@ -78,6 +78,7 @@ describe("MarketplaceItemCard", () => {
type: "",
search: "",
tags: [],
+ installed: "all" as "all" | "installed" | "not_installed",
},
setFilters: vi.fn(),
installed: {
@@ -158,7 +159,12 @@ describe("MarketplaceItemCard", () => {
renderWithProviders(