From 0c92a892aececcdfe7515e8cc2de1ae383d479ba Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 19:27:51 +0100 Subject: [PATCH 01/25] feat(marketplace): add MCP servers and modes tabs Implements the full marketplace with all three tabs functional: - MCP Servers: browse, search, filter by status/tags, install with scope selection, method picker, and parameter form - Modes: browse, search, filter by status/tags, install/remove - Skills: unchanged from previous PR Rewrites marketplace components to use kilo-ui Dialog/Card components, useDialog() for modal management, and vscode.onMessage for result subscriptions. Adds MarketplaceListView shared component for MCP and Modes tabs. Full installer with MCP config writing (local/remote), mode YAML parsing, and template parameter substitution. Adds 18 new i18n keys per locale (16 languages) for MCP/mode specific strings (badges, filters, install method, parameters, type labels). --- packages/kilo-i18n/src/ar.ts | 18 ++ packages/kilo-i18n/src/br.ts | 18 ++ packages/kilo-i18n/src/bs.ts | 18 ++ packages/kilo-i18n/src/da.ts | 18 ++ packages/kilo-i18n/src/de.ts | 18 ++ packages/kilo-i18n/src/en.ts | 18 ++ packages/kilo-i18n/src/es.ts | 18 ++ packages/kilo-i18n/src/fr.ts | 18 ++ packages/kilo-i18n/src/ja.ts | 18 ++ packages/kilo-i18n/src/ko.ts | 18 ++ packages/kilo-i18n/src/no.ts | 18 ++ packages/kilo-i18n/src/pl.ts | 18 ++ packages/kilo-i18n/src/ru.ts | 18 ++ packages/kilo-i18n/src/th.ts | 18 ++ packages/kilo-i18n/src/zh.ts | 18 ++ packages/kilo-i18n/src/zht.ts | 18 ++ packages/kilo-vscode/src/KiloProvider.ts | 33 ++- .../src/services/marketplace/installer.ts | 154 ++++++++++- .../components/marketplace/InstallModal.tsx | 181 +++++++++---- .../src/components/marketplace/ItemCard.tsx | 43 ++-- .../marketplace/MarketplaceListView.tsx | 136 ++++++++++ .../marketplace/MarketplaceView.tsx | 240 ++++++++++-------- .../components/marketplace/RemoveDialog.tsx | 35 +-- .../marketplace/SkillsMarketplace.tsx | 8 +- .../src/components/marketplace/index.ts | 2 +- .../components/marketplace/marketplace.css | 222 +++++++++------- .../src/components/marketplace/utils.ts | 12 +- .../webview-ui/src/types/marketplace.ts | 20 +- 28 files changed, 1067 insertions(+), 307 deletions(-) create mode 100644 packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx diff --git a/packages/kilo-i18n/src/ar.ts b/packages/kilo-i18n/src/ar.ts index 4abab3098f2..6bd89a624c6 100644 --- a/packages/kilo-i18n/src/ar.ts +++ b/packages/kilo-i18n/src/ar.ts @@ -43,4 +43,22 @@ export const dict = { "هل أنت متأكد أنك تريد إزالة هذا الـ {{type}}؟ سيؤدي هذا إلى إزالته من تكوين {{scope}} الخاص بك.", "marketplace.remove.cancel": "إلغاء", "marketplace.remove.confirm.button": "إزالة", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "بحث...", + "marketplace.filter.all": "جميع العناصر", + "marketplace.filter.notInstalled": "غير مثبت", + "marketplace.empty": "لم يتم العثور على عناصر", + "marketplace.badge.mcpServer": "خادم MCP", + "marketplace.badge.mode": "وضع", + "marketplace.card.by": "بواسطة {{author}}", + "marketplace.install.method": "طريقة التثبيت", + "marketplace.install.parameters": "المعلمات", + "marketplace.install.optional": "(اختياري)", + "marketplace.install.required": "{{name}} مطلوب", + "marketplace.scope.project": "مشروع", + "marketplace.scope.global": "عام", + "marketplace.remove.type.mcp": "خادم MCP", + "marketplace.remove.type.skill": "مهارة", + "marketplace.remove.type.mode": "وضع", + "marketplace.remove.failed": "فشلت إزالة {{name}}", } diff --git a/packages/kilo-i18n/src/br.ts b/packages/kilo-i18n/src/br.ts index cfa23e214d6..6acda4e7942 100644 --- a/packages/kilo-i18n/src/br.ts +++ b/packages/kilo-i18n/src/br.ts @@ -43,4 +43,22 @@ export const dict = { "Tem certeza que deseja remover este {{type}}? Isso o removerá da sua configuração {{scope}}.", "marketplace.remove.cancel": "Cancelar", "marketplace.remove.confirm.button": "Remover", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Pesquisar...", + "marketplace.filter.all": "Todos os Itens", + "marketplace.filter.notInstalled": "Não Instalado", + "marketplace.empty": "Nenhum item encontrado", + "marketplace.badge.mcpServer": "Servidor MCP", + "marketplace.badge.mode": "Modo", + "marketplace.card.by": "por {{author}}", + "marketplace.install.method": "Método de Instalação", + "marketplace.install.parameters": "Parâmetros", + "marketplace.install.optional": "(opcional)", + "marketplace.install.required": "{{name}} é obrigatório", + "marketplace.scope.project": "projeto", + "marketplace.scope.global": "global", + "marketplace.remove.type.mcp": "servidor MCP", + "marketplace.remove.type.skill": "habilidade", + "marketplace.remove.type.mode": "modo", + "marketplace.remove.failed": "Falha ao remover {{name}}", } diff --git a/packages/kilo-i18n/src/bs.ts b/packages/kilo-i18n/src/bs.ts index aecc7423546..a2a8be0ed1f 100644 --- a/packages/kilo-i18n/src/bs.ts +++ b/packages/kilo-i18n/src/bs.ts @@ -48,4 +48,22 @@ export const dict = { "Jeste li sigurni da želite ukloniti ovaj {{type}}? Ovo će ga ukloniti iz vaše {{scope}} konfiguracije.", "marketplace.remove.cancel": "Otkaži", "marketplace.remove.confirm.button": "Ukloni", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Pretraži...", + "marketplace.filter.all": "Sve stavke", + "marketplace.filter.notInstalled": "Nije instalirano", + "marketplace.empty": "Nema pronađenih stavki", + "marketplace.badge.mcpServer": "MCP Server", + "marketplace.badge.mode": "Režim", + "marketplace.card.by": "od {{author}}", + "marketplace.install.method": "Metoda instalacije", + "marketplace.install.parameters": "Parametri", + "marketplace.install.optional": "(opciono)", + "marketplace.install.required": "{{name}} je obavezno", + "marketplace.scope.project": "projekat", + "marketplace.scope.global": "globalno", + "marketplace.remove.type.mcp": "MCP server", + "marketplace.remove.type.skill": "vještina", + "marketplace.remove.type.mode": "režim", + "marketplace.remove.failed": "Uklanjanje {{name}} nije uspjelo", } diff --git a/packages/kilo-i18n/src/da.ts b/packages/kilo-i18n/src/da.ts index fb5541bdaeb..8c5ff923725 100644 --- a/packages/kilo-i18n/src/da.ts +++ b/packages/kilo-i18n/src/da.ts @@ -43,4 +43,22 @@ export const dict = { "Er du sikker på, at du vil fjerne denne {{type}}? Dette vil fjerne den fra din {{scope}} konfiguration.", "marketplace.remove.cancel": "Annuller", "marketplace.remove.confirm.button": "Fjern", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Søg...", + "marketplace.filter.all": "Alle elementer", + "marketplace.filter.notInstalled": "Ikke installeret", + "marketplace.empty": "Ingen elementer fundet", + "marketplace.badge.mcpServer": "MCP-server", + "marketplace.badge.mode": "Tilstand", + "marketplace.card.by": "af {{author}}", + "marketplace.install.method": "Installationsmetode", + "marketplace.install.parameters": "Parametre", + "marketplace.install.optional": "(valgfrit)", + "marketplace.install.required": "{{name}} er påkrævet", + "marketplace.scope.project": "projekt", + "marketplace.scope.global": "global", + "marketplace.remove.type.mcp": "MCP-server", + "marketplace.remove.type.skill": "færdighed", + "marketplace.remove.type.mode": "tilstand", + "marketplace.remove.failed": "Kunne ikke fjerne {{name}}", } diff --git a/packages/kilo-i18n/src/de.ts b/packages/kilo-i18n/src/de.ts index bd34499ade6..0ee1c8bd0c7 100644 --- a/packages/kilo-i18n/src/de.ts +++ b/packages/kilo-i18n/src/de.ts @@ -43,4 +43,22 @@ export const dict = { "Sind Sie sicher, dass Sie diese {{type}} entfernen möchten? Dadurch wird sie aus Ihrer {{scope}}-Konfiguration entfernt.", "marketplace.remove.cancel": "Abbrechen", "marketplace.remove.confirm.button": "Entfernen", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Suchen...", + "marketplace.filter.all": "Alle Elemente", + "marketplace.filter.notInstalled": "Nicht installiert", + "marketplace.empty": "Keine Elemente gefunden", + "marketplace.badge.mcpServer": "MCP-Server", + "marketplace.badge.mode": "Modus", + "marketplace.card.by": "von {{author}}", + "marketplace.install.method": "Installationsmethode", + "marketplace.install.parameters": "Parameter", + "marketplace.install.optional": "(optional)", + "marketplace.install.required": "{{name}} ist erforderlich", + "marketplace.scope.project": "Projekt", + "marketplace.scope.global": "Global", + "marketplace.remove.type.mcp": "MCP-Server", + "marketplace.remove.type.skill": "Skill", + "marketplace.remove.type.mode": "Modus", + "marketplace.remove.failed": "Fehler beim Entfernen von {{name}}", } diff --git a/packages/kilo-i18n/src/en.ts b/packages/kilo-i18n/src/en.ts index 5cccd33c5c2..d8ae2dee47d 100644 --- a/packages/kilo-i18n/src/en.ts +++ b/packages/kilo-i18n/src/en.ts @@ -45,4 +45,22 @@ export const dict = { "Are you sure you want to remove this {{type}}? This will remove it from your {{scope}} configuration.", "marketplace.remove.cancel": "Cancel", "marketplace.remove.confirm.button": "Remove", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Search...", + "marketplace.filter.all": "All Items", + "marketplace.filter.notInstalled": "Not Installed", + "marketplace.empty": "No items found", + "marketplace.badge.mcpServer": "MCP Server", + "marketplace.badge.mode": "Mode", + "marketplace.card.by": "by {{author}}", + "marketplace.install.method": "Installation Method", + "marketplace.install.parameters": "Parameters", + "marketplace.install.optional": "(optional)", + "marketplace.install.required": "{{name}} is required", + "marketplace.scope.project": "project", + "marketplace.scope.global": "global", + "marketplace.remove.type.mcp": "MCP server", + "marketplace.remove.type.skill": "skill", + "marketplace.remove.type.mode": "mode", + "marketplace.remove.failed": "Failed to remove {{name}}", } diff --git a/packages/kilo-i18n/src/es.ts b/packages/kilo-i18n/src/es.ts index cdedb7864b8..570e93f2672 100644 --- a/packages/kilo-i18n/src/es.ts +++ b/packages/kilo-i18n/src/es.ts @@ -43,4 +43,22 @@ export const dict = { "¿Estás seguro de que deseas eliminar este {{type}}? Esto lo eliminará de tu configuración {{scope}}.", "marketplace.remove.cancel": "Cancelar", "marketplace.remove.confirm.button": "Eliminar", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Buscar...", + "marketplace.filter.all": "Todos los elementos", + "marketplace.filter.notInstalled": "No instalado", + "marketplace.empty": "No se encontraron elementos", + "marketplace.badge.mcpServer": "Servidor MCP", + "marketplace.badge.mode": "Modo", + "marketplace.card.by": "por {{author}}", + "marketplace.install.method": "Método de instalación", + "marketplace.install.parameters": "Parámetros", + "marketplace.install.optional": "(opcional)", + "marketplace.install.required": "{{name}} es obligatorio", + "marketplace.scope.project": "proyecto", + "marketplace.scope.global": "global", + "marketplace.remove.type.mcp": "servidor MCP", + "marketplace.remove.type.skill": "habilidad", + "marketplace.remove.type.mode": "modo", + "marketplace.remove.failed": "Error al eliminar {{name}}", } diff --git a/packages/kilo-i18n/src/fr.ts b/packages/kilo-i18n/src/fr.ts index e741db9adc7..545f324f9e8 100644 --- a/packages/kilo-i18n/src/fr.ts +++ b/packages/kilo-i18n/src/fr.ts @@ -43,4 +43,22 @@ export const dict = { "Êtes-vous sûr de vouloir supprimer ce {{type}} ? Cela le supprimera de votre configuration {{scope}}.", "marketplace.remove.cancel": "Annuler", "marketplace.remove.confirm.button": "Supprimer", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Rechercher...", + "marketplace.filter.all": "Tous les éléments", + "marketplace.filter.notInstalled": "Non installé", + "marketplace.empty": "Aucun élément trouvé", + "marketplace.badge.mcpServer": "Serveur MCP", + "marketplace.badge.mode": "Mode", + "marketplace.card.by": "par {{author}}", + "marketplace.install.method": "Méthode d'installation", + "marketplace.install.parameters": "Paramètres", + "marketplace.install.optional": "(facultatif)", + "marketplace.install.required": "{{name}} est requis", + "marketplace.scope.project": "projet", + "marketplace.scope.global": "global", + "marketplace.remove.type.mcp": "serveur MCP", + "marketplace.remove.type.skill": "compétence", + "marketplace.remove.type.mode": "mode", + "marketplace.remove.failed": "Échec de la suppression de {{name}}", } diff --git a/packages/kilo-i18n/src/ja.ts b/packages/kilo-i18n/src/ja.ts index 460697114ab..c368f713a5e 100644 --- a/packages/kilo-i18n/src/ja.ts +++ b/packages/kilo-i18n/src/ja.ts @@ -41,4 +41,22 @@ export const dict = { "marketplace.remove.confirm": "この{{type}}を削除してもよろしいですか? これにより、{{scope}}設定から削除されます。", "marketplace.remove.cancel": "キャンセル", "marketplace.remove.confirm.button": "削除", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "検索...", + "marketplace.filter.all": "すべてのアイテム", + "marketplace.filter.notInstalled": "未インストール", + "marketplace.empty": "アイテムが見つかりません", + "marketplace.badge.mcpServer": "MCPサーバー", + "marketplace.badge.mode": "モード", + "marketplace.card.by": "作成者: {{author}}", + "marketplace.install.method": "インストール方法", + "marketplace.install.parameters": "パラメーター", + "marketplace.install.optional": "(任意)", + "marketplace.install.required": "{{name}} は必須です", + "marketplace.scope.project": "プロジェクト", + "marketplace.scope.global": "グローバル", + "marketplace.remove.type.mcp": "MCPサーバー", + "marketplace.remove.type.skill": "スキル", + "marketplace.remove.type.mode": "モード", + "marketplace.remove.failed": "{{name}} の削除に失敗しました", } diff --git a/packages/kilo-i18n/src/ko.ts b/packages/kilo-i18n/src/ko.ts index 1d2d11e4443..857dd478ea6 100644 --- a/packages/kilo-i18n/src/ko.ts +++ b/packages/kilo-i18n/src/ko.ts @@ -41,4 +41,22 @@ export const dict = { "marketplace.remove.confirm": "이 {{type}}을(를) 제거하시겠습니까? {{scope}} 구성에서 제거됩니다.", "marketplace.remove.cancel": "취소", "marketplace.remove.confirm.button": "제거", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "검색...", + "marketplace.filter.all": "모든 항목", + "marketplace.filter.notInstalled": "설치되지 않음", + "marketplace.empty": "항목을 찾을 수 없음", + "marketplace.badge.mcpServer": "MCP 서버", + "marketplace.badge.mode": "모드", + "marketplace.card.by": "제작: {{author}}", + "marketplace.install.method": "설치 방법", + "marketplace.install.parameters": "매개변수", + "marketplace.install.optional": "(선택 사항)", + "marketplace.install.required": "{{name}}이(가) 필요합니다", + "marketplace.scope.project": "프로젝트", + "marketplace.scope.global": "글로벌", + "marketplace.remove.type.mcp": "MCP 서버", + "marketplace.remove.type.skill": "스킬", + "marketplace.remove.type.mode": "모드", + "marketplace.remove.failed": "{{name}} 제거 실패", } diff --git a/packages/kilo-i18n/src/no.ts b/packages/kilo-i18n/src/no.ts index 70e5f3a3e0c..6878d64b0c3 100644 --- a/packages/kilo-i18n/src/no.ts +++ b/packages/kilo-i18n/src/no.ts @@ -43,4 +43,22 @@ export const dict = { "Er du sikker på at du vil fjerne denne {{type}}? Dette vil fjerne den fra din {{scope}}-konfigurasjon.", "marketplace.remove.cancel": "Avbryt", "marketplace.remove.confirm.button": "Fjern", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Søk...", + "marketplace.filter.all": "Alle elementer", + "marketplace.filter.notInstalled": "Ikke installert", + "marketplace.empty": "Ingen elementer funnet", + "marketplace.badge.mcpServer": "MCP-server", + "marketplace.badge.mode": "Modus", + "marketplace.card.by": "av {{author}}", + "marketplace.install.method": "Installasjonsmetode", + "marketplace.install.parameters": "Parametere", + "marketplace.install.optional": "(valgfritt)", + "marketplace.install.required": "{{name}} er påkrevd", + "marketplace.scope.project": "prosjekt", + "marketplace.scope.global": "global", + "marketplace.remove.type.mcp": "MCP-server", + "marketplace.remove.type.skill": "ferdighet", + "marketplace.remove.type.mode": "modus", + "marketplace.remove.failed": "Kunne ikke fjerne {{name}}", } diff --git a/packages/kilo-i18n/src/pl.ts b/packages/kilo-i18n/src/pl.ts index b4b7f0685a1..1cc4c5f420d 100644 --- a/packages/kilo-i18n/src/pl.ts +++ b/packages/kilo-i18n/src/pl.ts @@ -43,4 +43,22 @@ export const dict = { "Czy na pewno chcesz usunąć ten {{type}}? Spowoduje to usunięcie z konfiguracji: {{scope}}.", "marketplace.remove.cancel": "Anuluj", "marketplace.remove.confirm.button": "Usuń", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Szukaj...", + "marketplace.filter.all": "Wszystkie elementy", + "marketplace.filter.notInstalled": "Nie zainstalowano", + "marketplace.empty": "Nie znaleziono elementów", + "marketplace.badge.mcpServer": "Serwer MCP", + "marketplace.badge.mode": "Tryb", + "marketplace.card.by": "autor: {{author}}", + "marketplace.install.method": "Metoda instalacji", + "marketplace.install.parameters": "Parametry", + "marketplace.install.optional": "(opcjonalne)", + "marketplace.install.required": "{{name}} jest wymagane", + "marketplace.scope.project": "projekt", + "marketplace.scope.global": "globalny", + "marketplace.remove.type.mcp": "serwer MCP", + "marketplace.remove.type.skill": "umiejętność", + "marketplace.remove.type.mode": "tryb", + "marketplace.remove.failed": "Nie udało się usunąć {{name}}", } diff --git a/packages/kilo-i18n/src/ru.ts b/packages/kilo-i18n/src/ru.ts index 581b6925967..cf8a06e54a0 100644 --- a/packages/kilo-i18n/src/ru.ts +++ b/packages/kilo-i18n/src/ru.ts @@ -43,4 +43,22 @@ export const dict = { "Вы уверены, что хотите удалить этот {{type}}? Это удалит его из вашей конфигурации ({{scope}}).", "marketplace.remove.cancel": "Отмена", "marketplace.remove.confirm.button": "Удалить", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "Поиск...", + "marketplace.filter.all": "Все элементы", + "marketplace.filter.notInstalled": "Не установлено", + "marketplace.empty": "Элементы не найдены", + "marketplace.badge.mcpServer": "MCP-сервер", + "marketplace.badge.mode": "Режим", + "marketplace.card.by": "от {{author}}", + "marketplace.install.method": "Способ установки", + "marketplace.install.parameters": "Параметры", + "marketplace.install.optional": "(необязательно)", + "marketplace.install.required": "Требуется {{name}}", + "marketplace.scope.project": "проект", + "marketplace.scope.global": "глобально", + "marketplace.remove.type.mcp": "MCP-сервер", + "marketplace.remove.type.skill": "навык", + "marketplace.remove.type.mode": "режим", + "marketplace.remove.failed": "Не удалось удалить {{name}}", } diff --git a/packages/kilo-i18n/src/th.ts b/packages/kilo-i18n/src/th.ts index 5fe5514aab0..196fbbecca9 100644 --- a/packages/kilo-i18n/src/th.ts +++ b/packages/kilo-i18n/src/th.ts @@ -43,4 +43,22 @@ export const dict = { "คุณแน่ใจหรือไม่ว่าต้องการลบ {{type}} นี้? สิ่งนี้จะลบออกจากการกำหนดค่า {{scope}} ของคุณ", "marketplace.remove.cancel": "ยกเลิก", "marketplace.remove.confirm.button": "ลบ", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "ค้นหา...", + "marketplace.filter.all": "รายการทั้งหมด", + "marketplace.filter.notInstalled": "ยังไม่ได้ติดตั้ง", + "marketplace.empty": "ไม่พบรายการ", + "marketplace.badge.mcpServer": "เซิร์ฟเวอร์ MCP", + "marketplace.badge.mode": "โหมด", + "marketplace.card.by": "โดย {{author}}", + "marketplace.install.method": "วิธีการติดตั้ง", + "marketplace.install.parameters": "พารามิเตอร์", + "marketplace.install.optional": "(ไม่บังคับ)", + "marketplace.install.required": "จำเป็นต้องใช้ {{name}}", + "marketplace.scope.project": "โปรเจ็กต์", + "marketplace.scope.global": "โกลบอล", + "marketplace.remove.type.mcp": "เซิร์ฟเวอร์ MCP", + "marketplace.remove.type.skill": "ทักษะ", + "marketplace.remove.type.mode": "โหมด", + "marketplace.remove.failed": "ไม่สามารถลบ {{name}} ได้", } diff --git a/packages/kilo-i18n/src/zh.ts b/packages/kilo-i18n/src/zh.ts index a5f9d8870d3..3ddf2b8c988 100644 --- a/packages/kilo-i18n/src/zh.ts +++ b/packages/kilo-i18n/src/zh.ts @@ -40,4 +40,22 @@ export const dict = { "marketplace.remove.confirm": "确定要移除此 {{type}} 吗?这会将其从你的 {{scope}} 配置中移除。", "marketplace.remove.cancel": "取消", "marketplace.remove.confirm.button": "移除", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "搜索...", + "marketplace.filter.all": "所有项目", + "marketplace.filter.notInstalled": "未安装", + "marketplace.empty": "未找到项目", + "marketplace.badge.mcpServer": "MCP 服务器", + "marketplace.badge.mode": "模式", + "marketplace.card.by": "作者:{{author}}", + "marketplace.install.method": "安装方法", + "marketplace.install.parameters": "参数", + "marketplace.install.optional": "(可选)", + "marketplace.install.required": "{{name}} 是必填项", + "marketplace.scope.project": "项目", + "marketplace.scope.global": "全局", + "marketplace.remove.type.mcp": "MCP 服务器", + "marketplace.remove.type.skill": "技能", + "marketplace.remove.type.mode": "模式", + "marketplace.remove.failed": "移除 {{name}} 失败", } diff --git a/packages/kilo-i18n/src/zht.ts b/packages/kilo-i18n/src/zht.ts index 219ed9d7f95..ff74dab6e2f 100644 --- a/packages/kilo-i18n/src/zht.ts +++ b/packages/kilo-i18n/src/zht.ts @@ -40,4 +40,22 @@ export const dict = { "marketplace.remove.confirm": "確定要移除此 {{type}} 嗎?這會將其從你的 {{scope}} 設定中移除。", "marketplace.remove.cancel": "取消", "marketplace.remove.confirm.button": "移除", + "marketplace.tab.mcp": "MCP", + "marketplace.search": "搜尋...", + "marketplace.filter.all": "所有項目", + "marketplace.filter.notInstalled": "未安裝", + "marketplace.empty": "找不到項目", + "marketplace.badge.mcpServer": "MCP 伺服器", + "marketplace.badge.mode": "模式", + "marketplace.card.by": "作者:{{author}}", + "marketplace.install.method": "安裝方法", + "marketplace.install.parameters": "參數", + "marketplace.install.optional": "(選填)", + "marketplace.install.required": "{{name}} 是必填項", + "marketplace.scope.project": "專案", + "marketplace.scope.global": "全域", + "marketplace.remove.type.mcp": "MCP 伺服器", + "marketplace.remove.type.skill": "技能", + "marketplace.remove.type.mode": "模式", + "marketplace.remove.failed": "移除 {{name}} 失敗", } diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 09e98a3cda2..f8273f964a2 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -669,8 +669,11 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper case "removeInstalledMarketplaceItem": { const workspace = this.getProjectDirectory(this.currentSession?.id) const scope = message.mpInstallOptions?.target ?? "project" - const result = await this.getMarketplace().remove(message.mpItem, scope, workspace) - if (result.success) await this.disposeCliInstance(scope) + const result = + message.mpItem.type === "mode" + ? await this.removeModeViaCli(message.mpItem) + : await this.getMarketplace().remove(message.mpItem, scope, workspace) + if (result.success && message.mpItem.type !== "mode") await this.disposeCliInstance(scope) this.postMessage({ type: "marketplaceRemoveResult", success: result.success, @@ -1256,6 +1259,32 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper await this.fetchAndSendAgents() } + /** + * Remove a marketplace mode via the CLI removeAgent endpoint. + * Returns a RemoveResult so it plugs into the marketplace removal flow. + */ + private async removeModeViaCli(item: { + id: string + content: string + }): Promise<{ success: boolean; slug: string; error?: string }> { + if (!this.client) return { success: false, slug: item.id, error: "CLI not connected" } + const yaml = await import("yaml") + const mode = yaml.parse(item.content) as Record | undefined + const slug = (mode?.slug as string) ?? item.id + try { + const dir = this.getWorkspaceDirectory() + const result = await this.client.kilocode.removeAgent({ name: slug, directory: dir }) + if (result.error) { + console.error("[Kilo New] removeAgent (marketplace) returned error:", result.error) + return { success: false, slug: item.id, error: String(result.error) } + } + return { success: true, slug: item.id } + } catch (error) { + console.error("[Kilo New] Failed to remove mode (marketplace):", error) + return { success: false, slug: item.id, error: String(error) } + } + } + /** * Dispose the CLI backend instance so it re-reads config from disk. * Call after any marketplace install/remove that writes config files directly. diff --git a/packages/kilo-vscode/src/services/marketplace/installer.ts b/packages/kilo-vscode/src/services/marketplace/installer.ts index 7cf55351ef5..0b19073be49 100644 --- a/packages/kilo-vscode/src/services/marketplace/installer.ts +++ b/packages/kilo-vscode/src/services/marketplace/installer.ts @@ -6,6 +6,9 @@ import { promisify } from "util" import type { MarketplaceItem, SkillMarketplaceItem, + McpMarketplaceItem, + ModeMarketplaceItem, + McpInstallationMethod, InstallMarketplaceItemOptions, InstallResult, RemoveResult, @@ -24,9 +27,96 @@ export class MarketplaceInstaller { ): Promise { const scope = options.target ?? "project" if (item.type === "skill") return this.installSkill(item, scope, workspace) - return { success: false, slug: item.id, error: `${item.type} installation not yet implemented` } + if (item.type === "mcp") return this.installMcp(item, options, scope, workspace) + return this.installMode(item, scope, workspace) } + // ── MCP ───────────────────────────────────────────────────────────── + + async installMcp( + item: McpMarketplaceItem, + options: InstallMarketplaceItemOptions, + scope: "project" | "global", + workspace?: string, + ): Promise { + if (scope === "project" && !workspace) { + return { success: false, slug: item.id, error: "No workspace directory for project-scope install" } + } + + const config = await this.readConfig(scope, workspace) + if (!config.mcp) config.mcp = {} + + if (config.mcp[item.id]) { + return { success: false, slug: item.id, error: "MCP server already installed. Remove it first." } + } + + const content = this.resolveMcpContent(item, options) + if (!content) { + return { success: false, slug: item.id, error: "No installation content for MCP server" } + } + + const entry = this.buildMcpEntry(content, options.parameters) + config.mcp[item.id] = entry + + await this.writeConfig(scope, workspace, config) + return { success: true, slug: item.id } + } + + private resolveMcpContent(item: McpMarketplaceItem, options: InstallMarketplaceItemOptions): string | undefined { + if (typeof item.content === "string") return item.content + if (!Array.isArray(item.content) || item.content.length === 0) return undefined + const name = options.parameters?.__method as string | undefined + if (name) { + const found = item.content.find((m: McpInstallationMethod) => m.name === name) + if (found) return found.content + } + return item.content[0].content + } + + private buildMcpEntry(content: string, params?: Record): Record { + const filtered = Object.fromEntries(Object.entries(params ?? {}).filter(([k]) => k !== "__method")) + const replaced = Object.keys(filtered).length > 0 ? substituteParams(content, filtered) : content + + // Parse the content as JSON — the marketplace API provides JSON config blocks + try { + return JSON.parse(replaced) + } catch { + // Fallback: treat as a command string + return { command: replaced } + } + } + + // ── Mode ──────────────────────────────────────────────────────────── + + async installMode( + item: ModeMarketplaceItem, + scope: "project" | "global", + workspace?: string, + ): Promise { + if (scope === "project" && !workspace) { + return { success: false, slug: item.id, error: "No workspace directory for project-scope install" } + } + + const config = await this.readConfig(scope, workspace) + if (!config.agent) config.agent = {} + + if (config.agent[item.id]) { + return { success: false, slug: item.id, error: "Mode already installed. Remove it first." } + } + + // Mode content is a JSON blob for the agent config + try { + config.agent[item.id] = JSON.parse(item.content) + } catch { + config.agent[item.id] = { system: item.content } + } + + await this.writeConfig(scope, workspace, config) + return { success: true, slug: item.id } + } + + // ── Skill ─────────────────────────────────────────────────────────── + async installSkill( item: SkillMarketplaceItem, scope: "project" | "global", @@ -106,9 +196,34 @@ export class MarketplaceInstaller { } } + // ── Remove ────────────────────────────────────────────────────────── + async remove(item: MarketplaceItem, scope: "project" | "global", workspace?: string): Promise { if (item.type === "skill") return this.removeSkill(item, scope, workspace) - return { success: false, slug: item.id, error: `${item.type} removal not yet implemented` } + if (item.type === "mcp") return this.removeMcp(item, scope, workspace) + return this.removeMode(item, scope, workspace) + } + + async removeMcp(item: McpMarketplaceItem, scope: "project" | "global", workspace?: string): Promise { + const config = await this.readConfig(scope, workspace) + if (!config.mcp?.[item.id]) { + return { success: true, slug: item.id } + } + delete config.mcp[item.id] + if (Object.keys(config.mcp).length === 0) delete config.mcp + await this.writeConfig(scope, workspace, config) + return { success: true, slug: item.id } + } + + async removeMode(item: ModeMarketplaceItem, scope: "project" | "global", workspace?: string): Promise { + const config = await this.readConfig(scope, workspace) + if (!config.agent?.[item.id]) { + return { success: true, slug: item.id } + } + delete config.agent[item.id] + if (Object.keys(config.agent).length === 0) delete config.agent + await this.writeConfig(scope, workspace, config) + return { success: true, slug: item.id } } async removeSkill( @@ -136,6 +251,32 @@ export class MarketplaceInstaller { return { success: false, slug: item.id, error: String(err) } } } + + // ── Config helpers ────────────────────────────────────────────────── + + private async readConfig( + scope: "project" | "global", + workspace?: string, + ): Promise>> { + const filepath = this.paths.configPath(scope, workspace) + try { + const content = await fs.readFile(filepath, "utf-8") + return JSON.parse(content) + } catch (err) { + if ((err as NodeJS.ErrnoException).code === "ENOENT") return {} + throw err + } + } + + private async writeConfig( + scope: "project" | "global", + workspace: string | undefined, + config: Record, + ): Promise { + const filepath = this.paths.configPath(scope, workspace) + await fs.mkdir(path.dirname(filepath), { recursive: true }) + await fs.writeFile(filepath, JSON.stringify(config, null, 2) + "\n", "utf-8") + } } // ── Helpers ───────────────────────────────────────────────────────── @@ -145,6 +286,15 @@ function isSafeId(id: string): boolean { return /^[\w\-@.]+$/.test(id) } +function substituteParams(template: string, params: Record): string { + let result = template + for (const [key, value] of Object.entries(params)) { + result = result.replaceAll(`{{${key}}}`, String(value ?? "")) + result = result.replaceAll(`\${${key}}`, String(value ?? "")) + } + return result +} + async function findEscapedPaths(dir: string): Promise { const resolved = path.resolve(dir) const escaped: string[] = [] diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/InstallModal.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/InstallModal.tsx index e070513b5de..3655739c762 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/InstallModal.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/InstallModal.tsx @@ -1,71 +1,109 @@ -import { Component, createSignal, onMount, onCleanup, Show, For } from "solid-js" +import { createSignal, createEffect, onCleanup, Show, For } from "solid-js" +import { Dialog } from "@kilocode/kilo-ui/dialog" import { Button } from "@kilocode/kilo-ui/button" import { RadioGroup } from "@kilocode/kilo-ui/radio-group" +import { Select } from "@kilocode/kilo-ui/select" +import { TextField } from "@kilocode/kilo-ui/text-field" +import { Spinner } from "@kilocode/kilo-ui/spinner" import { useVSCode } from "../../context/vscode" import { useServer } from "../../context/server" import { useLanguage } from "../../context/language" -import type { MarketplaceItem } from "../../types/marketplace" +import type { MarketplaceItem, McpMarketplaceItem, McpInstallationMethod, McpParameter } from "../../types/marketplace" interface ScopeOption { value: "project" | "global" label: string - disabled?: boolean } interface Props { item: MarketplaceItem onClose: () => void - onInstallResult?: (success: boolean) => void + onInstallResult: (success: boolean, scope: "project" | "global") => void } -const InstallModal: Component = (props) => { +export const InstallModal = (props: Props) => { const vscode = useVSCode() const server = useServer() const { t } = useLanguage() const workspace = () => server.workspaceDirectory() - const scopeOptions = (): ScopeOption[] => [ - { value: "project", label: t("marketplace.install.scope.project"), disabled: !workspace() }, - { value: "global", label: t("marketplace.install.scope.global") }, + const options = (): ScopeOption[] => [ + { value: "project", label: t("marketplace.scope.project") }, + { value: "global", label: t("marketplace.scope.global") }, ] - const [scope, setScope] = createSignal(workspace() ? scopeOptions()[0] : scopeOptions()[1]) + const initial = workspace() ? options()[0] : options()[1] + const [scope, setScope] = createSignal(initial) const [installing, setInstalling] = createSignal(false) const [result, setResult] = createSignal<{ success: boolean; error?: string } | null>(null) + const [params, setParams] = createSignal>({}) - const prerequisites = () => props.item.prerequisites ?? [] + // MCP installation methods + const methods = (): McpInstallationMethod[] => { + if (props.item.type !== "mcp") return [] + const mcp = props.item as McpMarketplaceItem + if (!Array.isArray(mcp.content)) return [] + return mcp.content + } - onMount(() => { - const handler = (event: MessageEvent) => { - const msg = event.data - if (msg?.type === "marketplaceInstallResult" && msg.slug === props.item.id) { + const [method, setMethod] = createSignal(methods()[0]) + + const prerequisites = (): string[] => { + if (method()?.prerequisites?.length) return method()!.prerequisites! + return props.item.prerequisites ?? [] + } + + const parameters = (): McpParameter[] => { + if (method()?.parameters?.length) return method()!.parameters! + if (props.item.type === "mcp") return (props.item as McpMarketplaceItem).parameters ?? [] + return [] + } + + const valid = () => { + for (const param of parameters()) { + if (!param.optional && !params()[param.key]?.trim()) return false + } + return true + } + + const setParam = (key: string, value: string) => { + setParams((prev) => ({ ...prev, [key]: value })) + } + + createEffect(() => { + const unsub = vscode.onMessage((msg) => { + if (msg.type === "marketplaceInstallResult" && msg.slug === props.item.id) { setInstalling(false) setResult({ success: msg.success, error: msg.error }) - props.onInstallResult?.(msg.success) + props.onInstallResult(msg.success, scope().value) } - } - window.addEventListener("message", handler) - onCleanup(() => window.removeEventListener("message", handler)) + }) + onCleanup(unsub) }) const handleInstall = () => { setInstalling(true) + const paramValues: Record = { ...params() } + if (method()) { + paramValues.__method = method()!.name + } vscode.postMessage({ type: "installMarketplaceItem", mpItem: props.item, - mpInstallOptions: { target: scope().value }, + mpInstallOptions: { + target: scope().value, + parameters: Object.keys(paramValues).length > 0 ? paramValues : undefined, + }, }) } return ( -
!installing() && props.onClose()}> -
e.stopPropagation()}> -

{t("marketplace.install.title", { name: props.item.name })}

- - -
- + + +
+
+ {t("marketplace.install.scope")} x.value} label={(x: ScopeOption) => x.label} @@ -73,46 +111,81 @@ const InstallModal: Component = (props) => { />
+ 1}> +
+ {t("marketplace.install.method")} + o.value} + label={(o: StatusOption) => o.label} + onSelect={(v: StatusOption | undefined) => v && setStatus(v)} + /> +
+ 0}> +
+ + {(tag) => ( + + )} + +
+
+ + +
+ } + > + 0} fallback={

{props.emptyMessage}

}> +
+ + {(item) => ( + {(tag) => {tag}}} + /> + )} + +
+
+
+
+ ) +} diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx index e821a6e0beb..7e546aad7b9 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx @@ -1,168 +1,184 @@ -import { Component, createSignal, createMemo, onMount, onCleanup, Show } from "solid-js" +import { createSignal, createMemo, createEffect, onCleanup, Show } from "solid-js" import { Tabs } from "@kilocode/kilo-ui/tabs" +import { Card } from "@kilocode/kilo-ui/card" +import { Button } from "@kilocode/kilo-ui/button" import { useVSCode } from "../../context/vscode" import { useServer } from "../../context/server" import { useLanguage } from "../../context/language" -import type { MarketplaceItem, SkillMarketplaceItem, MarketplaceInstalledMetadata } from "../../types/marketplace" -import SkillsMarketplace from "./SkillsMarketplace" -import InstallModal from "./InstallModal" -import RemoveDialog from "./RemoveDialog" +import { useDialog } from "@kilocode/kilo-ui/context/dialog" +import type { + MarketplaceItem, + McpMarketplaceItem, + ModeMarketplaceItem, + SkillMarketplaceItem, + MarketplaceInstalledMetadata, +} from "../../types/marketplace" +import { SkillsMarketplace } from "./SkillsMarketplace" +import { MarketplaceListView } from "./MarketplaceListView" +import { InstallModal } from "./InstallModal" +import { RemoveDialog } from "./RemoveDialog" import "./marketplace.css" const EMPTY_METADATA: MarketplaceInstalledMetadata = { project: {}, global: {} } -const MarketplaceView: Component = () => { +export const MarketplaceView = () => { const vscode = useVSCode() const server = useServer() const { t } = useLanguage() + const dialog = useDialog() const [items, setItems] = createSignal([]) const [metadata, setMetadata] = createSignal(EMPTY_METADATA) const [fetching, setFetching] = createSignal(true) const [errors, setErrors] = createSignal([]) - const [tab, setTab] = createSignal("skill") - - // Install/Remove state - const [installItem, setInstallItem] = createSignal(null) - const [removeItem, setRemoveItem] = createSignal<{ item: MarketplaceItem; scope: "project" | "global" } | null>(null) - const [pendingRemove, setPendingRemove] = createSignal(null) - const [removeError, setRemoveError] = createSignal(null) + const [tab, setTab] = createSignal("mcp") const skills = createMemo(() => items().filter((i): i is SkillMarketplaceItem => i.type === "skill")) + const mcps = createMemo(() => items().filter((i): i is McpMarketplaceItem => i.type === "mcp")) + const modes = createMemo(() => items().filter((i): i is ModeMarketplaceItem => i.type === "mode")) const fetchData = () => { setFetching(true) vscode.postMessage({ type: "fetchMarketplaceData" }) } - onMount(() => { - fetchData() - - const handler = (event: MessageEvent) => { - const msg = event.data - if (msg?.type === "marketplaceData") { + // Listen for messages + createEffect(() => { + const unsub = vscode.onMessage((msg) => { + if (msg.type === "marketplaceData") { setItems(msg.marketplaceItems ?? []) setMetadata(msg.marketplaceInstalledMetadata ?? EMPTY_METADATA) setErrors(msg.errors ?? []) setFetching(false) } - if (msg?.type === "marketplaceRemoveResult") { - if (msg.slug === pendingRemove()) { - setPendingRemove(null) - if (!msg.success) { - setRemoveError(msg.error ?? "Failed to remove item") - } else { - fetchData() - } - } + if (msg.type === "marketplaceRemoveResult") { + if (msg.success) fetchData() } - // Re-fetch when agents or skills change (e.g. after install via CLI) - if (msg?.type === "agentsLoaded" || msg?.type === "skillsLoaded") { + if (msg.type === "agentsLoaded" || msg.type === "skillsLoaded") { fetchData() } - // Re-fetch when workspace directory changes - if (msg?.type === "workspaceDirectoryChanged") { + if (msg.type === "workspaceDirectoryChanged") { fetchData() } - } - window.addEventListener("message", handler) - onCleanup(() => window.removeEventListener("message", handler)) + }) + onCleanup(unsub) }) + // Re-fetch when workspace changes + createEffect(() => { + server.workspaceDirectory() + fetchData() + }) + + const telemetry = (event: string, properties?: Record) => { + vscode.postMessage({ type: "telemetry", event, properties: properties ?? {} }) + } + const handleInstall = (item: MarketplaceItem) => { - setInstallItem(item) + dialog.show(() => ( + dialog.close()} + onInstallResult={(success, scope) => { + if (success) { + telemetry("marketplace_item_installed", { + id: item.id, + type: item.type, + scope, + }) + dialog.close() + fetchData() + } + }} + /> + )) } const handleRemove = (item: MarketplaceItem, scope: "project" | "global") => { - setRemoveItem({ item, scope }) + dialog.show(() => ( + dialog.close()} + onConfirm={() => { + vscode.postMessage({ + type: "removeInstalledMarketplaceItem", + mpItem: item, + mpInstallOptions: { target: scope }, + }) + telemetry("marketplace_item_removed", { + id: item.id, + type: item.type, + scope, + }) + dialog.close() + }} + /> + )) } - const confirmRemove = () => { - const r = removeItem() - if (!r) return - setPendingRemove(r.item.id) - setRemoveError(null) - vscode.postMessage({ - type: "removeInstalledMarketplaceItem", - mpItem: r.item, - mpInstallOptions: { target: r.scope }, - }) - setRemoveItem(null) + const dismissError = (idx: number) => { + setErrors((prev) => prev.filter((_, i) => i !== idx)) } return (
- + 0}> + {errors().map((err, idx) => ( + + {err} + + + ))} + + + - {t("marketplace.tab.skills")} - {t("marketplace.tab.mcpServers")} + {t("marketplace.tab.mcp")} {t("marketplace.tab.modes")} + {t("marketplace.tab.skills")} - - - +
+ + + - -
-

{t("marketplace.placeholder")}

-
-
+ + + - -
-

{t("marketplace.placeholder")}

-
-
+ + + +
- - 0}> -
- {errors().map((e) => ( -

{e}

- ))} -
-
- - -
-

{removeError()}

-
-
- - - {(item) => ( - setInstallItem(null)} - onInstallResult={(success) => { - if (success) { - setInstallItem(null) - fetchData() - } - }} - /> - )} - - - - {(r) => ( - setRemoveItem(null)} - onConfirm={confirmRemove} - /> - )} -
) } - -export default MarketplaceView diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/RemoveDialog.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/RemoveDialog.tsx index 4e544fac0cd..b092861b90e 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/RemoveDialog.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/RemoveDialog.tsx @@ -1,4 +1,4 @@ -import { Component } from "solid-js" +import { Dialog } from "@kilocode/kilo-ui/dialog" import { Button } from "@kilocode/kilo-ui/button" import type { MarketplaceItem } from "../../types/marketplace" import { useLanguage } from "../../context/language" @@ -10,25 +10,26 @@ interface Props { onConfirm: () => void } -const RemoveDialog: Component = (props) => { +export const RemoveDialog = (props: Props) => { const { t } = useLanguage() + const typeName = () => { + if (props.item.type === "mcp") return t("marketplace.type.mcp") + if (props.item.type === "mode") return t("marketplace.type.mode") + return t("marketplace.type.skill") + } + return ( -
-
e.stopPropagation()}> -

{t("marketplace.remove.title", { name: props.item.name })}

-

{t("marketplace.remove.confirm", { type: props.item.type, scope: props.scope })}

-
- - -
+ +

{t("marketplace.remove.confirm", { type: typeName(), scope: t(`marketplace.scope.${props.scope}`) })}

+
+ +
-
+ ) } - -export default RemoveDialog diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx index e55c4d3419c..d71a250266e 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx @@ -1,11 +1,11 @@ -import { Component, createSignal, createMemo, For, Show } from "solid-js" +import { createSignal, createMemo, For, Show } from "solid-js" import { TextField } from "@kilocode/kilo-ui/text-field" import { RadioGroup } from "@kilocode/kilo-ui/radio-group" import { Tag } from "@kilocode/kilo-ui/tag" import { Spinner } from "@kilocode/kilo-ui/spinner" import type { SkillMarketplaceItem, MarketplaceInstalledMetadata, MarketplaceItem } from "../../types/marketplace" import { useLanguage } from "../../context/language" -import ItemCard from "./ItemCard" +import { ItemCard } from "./ItemCard" interface Props { items: SkillMarketplaceItem[] @@ -17,7 +17,7 @@ interface Props { const ALL = "__all__" -const SkillsMarketplace: Component = (props) => { +export const SkillsMarketplace = (props: Props) => { const { t } = useLanguage() const [search, setSearch] = createSignal("") const [category, setCategory] = createSignal(ALL) @@ -82,5 +82,3 @@ const SkillsMarketplace: Component = (props) => {
) } - -export default SkillsMarketplace diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/index.ts b/packages/kilo-vscode/webview-ui/src/components/marketplace/index.ts index 979b85151d1..e322a99a16f 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/index.ts +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/index.ts @@ -1 +1 @@ -export { default as MarketplaceView } from "./MarketplaceView" +export { MarketplaceView } from "./MarketplaceView" diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css b/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css index 49c95541ce4..5d958d7fdd2 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css @@ -6,6 +6,19 @@ height: 100%; padding: 12px; overflow: auto; + gap: 8px; +} + +.marketplace-tabs-root { + display: flex; + flex-direction: column; + flex: 1; + min-height: 0; +} + +.marketplace-content { + flex: 1; + overflow: auto; } /* List layout */ @@ -23,10 +36,52 @@ align-items: center; } +.marketplace-search-field { + flex: 1; +} + .marketplace-category-filters { overflow-x: auto; } +/* Tag filters */ + +.marketplace-active-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.marketplace-tag-filter { + all: unset; + cursor: pointer; + opacity: 0.5; + transition: opacity 0.15s ease; +} + +.marketplace-tag-filter:hover { + opacity: 0.8; +} + +.marketplace-tag-filter.active { + opacity: 1; +} + +/* Loading & empty */ + +.marketplace-loading { + display: flex; + justify-content: center; + align-items: center; + min-height: 200px; +} + +.marketplace-empty { + text-align: center; + color: var(--text-weak); + padding: 24px; +} + /* Grid */ .marketplace-grid { @@ -48,9 +103,6 @@ flex-direction: column; gap: 8px; padding: 12px; - border: 1px solid var(--border-base); - border-radius: 6px; - background: var(--background-base); } .marketplace-card-header { @@ -71,14 +123,6 @@ color: var(--text-base); } -.marketplace-card-link { - text-decoration: none; -} - -.marketplace-card-link:hover { - text-decoration: underline; -} - .marketplace-card-author { font-size: 12px; color: var(--text-weak); @@ -115,117 +159,109 @@ flex-shrink: 0; } -.marketplace-installed-tag { +/* Installed badge */ + +.marketplace-badge-installed { color: var(--text-on-success-base) !important; border-color: var(--border-success-base) !important; } +/* Remove button */ + .marketplace-remove-btn { color: var(--text-on-critical-base) !important; border-color: var(--border-critical-base) !important; } -/* Placeholder */ +/* Remove dialog actions */ -.marketplace-placeholder { - display: flex; - justify-content: center; - align-items: center; - min-height: 200px; - color: var(--text-weak); - font-size: 16px; -} - -/* Empty state */ - -.marketplace-empty { - text-align: center; - color: var(--text-weak); - padding: 24px; -} - -/* Modal / Dialog */ - -.marketplace-modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.marketplace-modal { - background: var(--background-base); - border: 1px solid var(--border-base); - border-radius: 8px; - padding: 20px; - max-width: 480px; - width: 90%; - max-height: 80vh; - overflow-y: auto; -} - -.marketplace-modal h3 { - margin: 0 0 16px; - font-size: 16px; - color: var(--text-base); -} - -.marketplace-modal p { - margin: 0 0 12px; - color: var(--text-weak); - font-size: 13px; -} - -.marketplace-modal-section { - margin-bottom: 16px; -} - -.marketplace-modal-section > label { - display: block; - font-weight: 600; - font-size: 13px; - margin-bottom: 6px; - color: var(--text-base); -} - -.marketplace-modal-actions { +.marketplace-remove-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; } -.marketplace-modal-result { - text-align: center; - padding: 12px 0; +/* Danger button (remove confirm) */ + +.danger-btn { + color: var(--text-on-critical-base) !important; + border-color: var(--border-critical-base) !important; } -.marketplace-prerequisites { +/* Install modal */ + +.install-modal-body { + display: flex; + flex-direction: column; + gap: 16px; +} + +.install-modal-section { + display: flex; + flex-direction: column; + gap: 6px; +} + +.install-modal-label { + font-weight: 600; + font-size: 13px; + color: var(--text-base); +} + +.install-modal-prerequisites { margin: 0; padding-left: 20px; font-size: 13px; color: var(--text-weak); } -/* Error/Success messages */ - -.marketplace-errors { - margin-top: 12px; +.install-modal-param { + margin-bottom: 8px; } -.marketplace-error { - color: var(--text-on-critical-base); - font-size: 13px; +.install-modal-footer { + display: flex; + justify-content: flex-end; + gap: 8px; } -.marketplace-success { +.install-modal-result { + text-align: center; + padding: 12px 0; +} + +.install-modal-success { color: var(--text-on-success-base); font-size: 14px; font-weight: 600; + margin: 0 0 16px; +} + +.install-modal-error-msg { + color: var(--text-on-critical-base); + font-size: 13px; + margin: 0 0 16px; +} + +/* Error banner */ + +.marketplace-error-banner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px 12px; + font-size: 13px; +} + +/* Link style */ + +.link { + color: var(--text-interactive-base); + cursor: pointer; +} + +.link:hover { + text-decoration: underline; } diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/utils.ts b/packages/kilo-vscode/webview-ui/src/components/marketplace/utils.ts index 264b722a54c..9504e734f37 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/utils.ts +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/utils.ts @@ -1,11 +1,19 @@ import type { MarketplaceInstalledMetadata } from "../../types/marketplace" +export function isInstalled( + id: string, + type: string, + metadata: MarketplaceInstalledMetadata, +): "project" | "global" | false { + return installedScopes(id, type, metadata)[0] ?? false +} + export function installedScopes( id: string, type: string, metadata: MarketplaceInstalledMetadata, -): Array<"project" | "global"> { - const scopes: Array<"project" | "global"> = [] +): ("project" | "global")[] { + const scopes: ("project" | "global")[] = [] if (metadata.project[id]?.type === type) scopes.push("project") if (metadata.global[id]?.type === type) scopes.push("global") return scopes diff --git a/packages/kilo-vscode/webview-ui/src/types/marketplace.ts b/packages/kilo-vscode/webview-ui/src/types/marketplace.ts index d41a7351266..943d2bec10a 100644 --- a/packages/kilo-vscode/webview-ui/src/types/marketplace.ts +++ b/packages/kilo-vscode/webview-ui/src/types/marketplace.ts @@ -1,3 +1,17 @@ +export interface McpParameter { + name: string + key: string + placeholder?: string + optional?: boolean +} + +export interface McpInstallationMethod { + name: string + content: string + parameters?: McpParameter[] + prerequisites?: string[] +} + export interface MarketplaceItemBase { id: string name: string @@ -11,7 +25,8 @@ export interface MarketplaceItemBase { export interface McpMarketplaceItem extends MarketplaceItemBase { type: "mcp" url: string - content: string | unknown[] + content: string | McpInstallationMethod[] + parameters?: McpParameter[] } export interface ModeMarketplaceItem extends MarketplaceItemBase { @@ -41,8 +56,7 @@ export interface MarketplaceInstalledMetadata { } export interface MarketplaceFilters { - search?: string type?: string - status?: string + search?: string tags?: string[] } From 9861f9d8ba4b23f4f3506eb2d330c535527081b5 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Tue, 17 Mar 2026 10:45:16 +0100 Subject: [PATCH 02/25] refactor(vscode): merge SkillsMarketplace into MarketplaceListView --- .../marketplace/MarketplaceListView.tsx | 52 ++++++++---- .../marketplace/MarketplaceView.tsx | 6 +- .../marketplace/SkillsMarketplace.tsx | 84 ------------------- 3 files changed, 39 insertions(+), 103 deletions(-) delete mode 100644 packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx index 5ebb22a9993..cfb0d174692 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx @@ -6,7 +6,7 @@ import { Spinner } from "@kilocode/kilo-ui/spinner" import type { MarketplaceItem, McpMarketplaceItem, - ModeMarketplaceItem, + SkillMarketplaceItem, MarketplaceInstalledMetadata, } from "../../types/marketplace" import { useLanguage } from "../../context/language" @@ -19,10 +19,10 @@ interface StatusOption { } interface Props { - items: (McpMarketplaceItem | ModeMarketplaceItem)[] + items: MarketplaceItem[] metadata: MarketplaceInstalledMetadata fetching: boolean - type: "mcp" | "mode" + type: "mcp" | "mode" | "skill" searchPlaceholder: string emptyMessage: string onInstall: (item: MarketplaceItem) => void @@ -41,10 +41,15 @@ export const MarketplaceListView = (props: Props) => { { value: "notInstalled", label: t("marketplace.filter.notInstalled") }, ] + const tagsFor = (item: MarketplaceItem): string[] => { + if (item.type === "skill") return [(item as SkillMarketplaceItem).displayCategory] + return item.tags ?? [] + } + const allTags = createMemo(() => { const set = new Set() for (const item of props.items) { - for (const tag of item.tags ?? []) set.add(tag) + for (const tag of tagsFor(item)) set.add(tag) } return Array.from(set).sort() }) @@ -65,13 +70,15 @@ export const MarketplaceListView = (props: Props) => { return props.items.filter((item) => { if (s === "installed" && !isInstalled(item.id, item.type, props.metadata)) return false if (s === "notInstalled" && isInstalled(item.id, item.type, props.metadata)) return false - if (active.length > 0 && !active.some((tag) => item.tags?.includes(tag))) return false + if (active.length > 0 && !active.some((tag) => tagsFor(item).includes(tag))) return false if (!q) return true + const skill = item.type === "skill" ? (item as SkillMarketplaceItem) : undefined return ( item.id.toLowerCase().includes(q) || item.name.toLowerCase().includes(q) || item.description.toLowerCase().includes(q) || - (item.author?.toLowerCase().includes(q) ?? false) + (item.author?.toLowerCase().includes(q) ?? false) || + (skill?.displayName.toLowerCase().includes(q) ?? false) ) }) }) @@ -116,17 +123,28 @@ export const MarketplaceListView = (props: Props) => { 0} fallback={

{props.emptyMessage}

}>
- {(item) => ( - {(tag) => {tag}}} - /> - )} + {(item) => { + const skill = item.type === "skill" ? (item as SkillMarketplaceItem) : undefined + const mcp = item.type === "mcp" ? (item as McpMarketplaceItem) : undefined + return ( + {(tag) => {tag}}} + /> + ) + }}
diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx index 7e546aad7b9..6746b0bebb6 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx @@ -13,7 +13,6 @@ import type { SkillMarketplaceItem, MarketplaceInstalledMetadata, } from "../../types/marketplace" -import { SkillsMarketplace } from "./SkillsMarketplace" import { MarketplaceListView } from "./MarketplaceListView" import { InstallModal } from "./InstallModal" import { RemoveDialog } from "./RemoveDialog" @@ -169,10 +168,13 @@ export const MarketplaceView = () => { - diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx deleted file mode 100644 index d71a250266e..00000000000 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/SkillsMarketplace.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { createSignal, createMemo, For, Show } from "solid-js" -import { TextField } from "@kilocode/kilo-ui/text-field" -import { RadioGroup } from "@kilocode/kilo-ui/radio-group" -import { Tag } from "@kilocode/kilo-ui/tag" -import { Spinner } from "@kilocode/kilo-ui/spinner" -import type { SkillMarketplaceItem, MarketplaceInstalledMetadata, MarketplaceItem } from "../../types/marketplace" -import { useLanguage } from "../../context/language" -import { ItemCard } from "./ItemCard" - -interface Props { - items: SkillMarketplaceItem[] - metadata: MarketplaceInstalledMetadata - fetching: boolean - onInstall: (item: MarketplaceItem) => void - onRemove: (item: MarketplaceItem, scope: "project" | "global") => void -} - -const ALL = "__all__" - -export const SkillsMarketplace = (props: Props) => { - const { t } = useLanguage() - const [search, setSearch] = createSignal("") - const [category, setCategory] = createSignal(ALL) - - const categories = createMemo(() => { - const set = new Set() - for (const item of props.items) { - if (item.displayCategory) set.add(item.displayCategory) - } - return [ALL, ...Array.from(set).sort()] - }) - - const filtered = createMemo(() => { - const q = search().toLowerCase() - const cat = category() - return props.items.filter((item) => { - if (cat !== ALL && item.displayCategory !== cat) return false - if (!q) return true - return ( - item.id.toLowerCase().includes(q) || - item.name.toLowerCase().includes(q) || - item.displayName.toLowerCase().includes(q) || - item.description.toLowerCase().includes(q) || - item.displayCategory.toLowerCase().includes(q) - ) - }) - }) - - return ( -
-
- -
-
- c} - label={(c: string) => (c === ALL ? t("marketplace.category.all") : c)} - onSelect={(v: string | undefined) => v && setCategory(v)} - /> -
- }> - 0} fallback={

{t("marketplace.empty.skills")}

}> -
- - {(item) => ( - {item.displayCategory}} - /> - )} - -
-
-
-
- ) -} From 785b37883a574b8720681641e7a73bc11e13a485 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Tue, 17 Mar 2026 10:46:10 +0100 Subject: [PATCH 03/25] fix(vscode): only show MCP tags used by at least 2 items --- .../src/components/marketplace/MarketplaceListView.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx index cfb0d174692..ee1f07b0486 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx @@ -47,11 +47,15 @@ export const MarketplaceListView = (props: Props) => { } const allTags = createMemo(() => { - const set = new Set() + const counts = new Map() for (const item of props.items) { - for (const tag of tagsFor(item)) set.add(tag) + for (const tag of tagsFor(item)) counts.set(tag, (counts.get(tag) ?? 0) + 1) } - return Array.from(set).sort() + const min = props.type === "mcp" ? 2 : 1 + return Array.from(counts.entries()) + .filter(([, n]) => n >= min) + .map(([tag]) => tag) + .sort() }) const toggleTag = (tag: string) => { From 02e7ddfd171515f8abcccae439d67f500a32a9e6 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Tue, 17 Mar 2026 10:54:58 +0100 Subject: [PATCH 04/25] fix(i18n): use generic search/empty keys for all marketplace tabs The MCP and Modes tabs referenced non-existent per-type i18n keys (marketplace.search.mcp, marketplace.empty.modes, etc.), causing raw key names to render as placeholder text. Switch all three tabs to the generic marketplace.search / marketplace.empty keys and remove the now-unused marketplace.search.skills / marketplace.empty.skills keys from all 16 locales. --- packages/kilo-i18n/src/ar.ts | 2 -- packages/kilo-i18n/src/br.ts | 2 -- packages/kilo-i18n/src/bs.ts | 2 -- packages/kilo-i18n/src/da.ts | 2 -- packages/kilo-i18n/src/de.ts | 2 -- packages/kilo-i18n/src/en.ts | 2 -- packages/kilo-i18n/src/es.ts | 2 -- packages/kilo-i18n/src/fr.ts | 2 -- packages/kilo-i18n/src/ja.ts | 2 -- packages/kilo-i18n/src/ko.ts | 2 -- packages/kilo-i18n/src/no.ts | 2 -- packages/kilo-i18n/src/pl.ts | 2 -- packages/kilo-i18n/src/ru.ts | 2 -- packages/kilo-i18n/src/th.ts | 2 -- packages/kilo-i18n/src/zh.ts | 2 -- packages/kilo-i18n/src/zht.ts | 2 -- .../src/components/marketplace/MarketplaceView.tsx | 12 ++++++------ 17 files changed, 6 insertions(+), 38 deletions(-) diff --git a/packages/kilo-i18n/src/ar.ts b/packages/kilo-i18n/src/ar.ts index 6bd89a624c6..935a63b24d6 100644 --- a/packages/kilo-i18n/src/ar.ts +++ b/packages/kilo-i18n/src/ar.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "المهارات", "marketplace.tab.mcpServers": "خوادم MCP", "marketplace.tab.modes": "الأوضاع", - "marketplace.search.skills": "البحث في المهارات...", "marketplace.category.all": "الكل", - "marketplace.empty.skills": "لم يتم العثور على مهارات", "marketplace.placeholder": "سيتم تنفيذه لاحقاً", "marketplace.card.installed": "مثبت", "marketplace.card.install": "تثبيت", diff --git a/packages/kilo-i18n/src/br.ts b/packages/kilo-i18n/src/br.ts index 6acda4e7942..9714a6e3c65 100644 --- a/packages/kilo-i18n/src/br.ts +++ b/packages/kilo-i18n/src/br.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "Servidores MCP", "marketplace.tab.modes": "Modos", - "marketplace.search.skills": "Buscar skills...", "marketplace.category.all": "Todos", - "marketplace.empty.skills": "Nenhuma skill encontrada", "marketplace.placeholder": "A ser implementado", "marketplace.card.installed": "Instalado", "marketplace.card.install": "Instalar", diff --git a/packages/kilo-i18n/src/bs.ts b/packages/kilo-i18n/src/bs.ts index a2a8be0ed1f..f225ff43beb 100644 --- a/packages/kilo-i18n/src/bs.ts +++ b/packages/kilo-i18n/src/bs.ts @@ -24,9 +24,7 @@ export const dict = { "marketplace.tab.skills": "Vještine", "marketplace.tab.mcpServers": "MCP Serveri", "marketplace.tab.modes": "Modovi", - "marketplace.search.skills": "Pretraži vještine...", "marketplace.category.all": "Sve", - "marketplace.empty.skills": "Nema pronađenih vještina", "marketplace.placeholder": "Biće implementirano", "marketplace.card.installed": "Instalirano", "marketplace.card.install": "Instaliraj", diff --git a/packages/kilo-i18n/src/da.ts b/packages/kilo-i18n/src/da.ts index 8c5ff923725..35e7bda83f2 100644 --- a/packages/kilo-i18n/src/da.ts +++ b/packages/kilo-i18n/src/da.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "MCP-servere", "marketplace.tab.modes": "Tilstande", - "marketplace.search.skills": "Søg efter skills...", "marketplace.category.all": "Alle", - "marketplace.empty.skills": "Ingen skills fundet", "marketplace.placeholder": "Skal implementeres", "marketplace.card.installed": "Installeret", "marketplace.card.install": "Installer", diff --git a/packages/kilo-i18n/src/de.ts b/packages/kilo-i18n/src/de.ts index 0ee1c8bd0c7..fed983ecbaf 100644 --- a/packages/kilo-i18n/src/de.ts +++ b/packages/kilo-i18n/src/de.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "MCP-Server", "marketplace.tab.modes": "Modi", - "marketplace.search.skills": "Skills suchen...", "marketplace.category.all": "Alle", - "marketplace.empty.skills": "Keine Skills gefunden", "marketplace.placeholder": "Noch nicht implementiert", "marketplace.card.installed": "Installiert", "marketplace.card.install": "Installieren", diff --git a/packages/kilo-i18n/src/en.ts b/packages/kilo-i18n/src/en.ts index d8ae2dee47d..e1726bb2881 100644 --- a/packages/kilo-i18n/src/en.ts +++ b/packages/kilo-i18n/src/en.ts @@ -21,9 +21,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "MCP Servers", "marketplace.tab.modes": "Modes", - "marketplace.search.skills": "Search skills...", "marketplace.category.all": "All", - "marketplace.empty.skills": "No skills found", "marketplace.placeholder": "To be implemented", "marketplace.card.installed": "Installed", "marketplace.card.install": "Install", diff --git a/packages/kilo-i18n/src/es.ts b/packages/kilo-i18n/src/es.ts index 570e93f2672..2560cd4cf50 100644 --- a/packages/kilo-i18n/src/es.ts +++ b/packages/kilo-i18n/src/es.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "Servidores MCP", "marketplace.tab.modes": "Modos", - "marketplace.search.skills": "Buscar skills...", "marketplace.category.all": "Todos", - "marketplace.empty.skills": "No se encontraron skills", "marketplace.placeholder": "Por implementar", "marketplace.card.installed": "Instalado", "marketplace.card.install": "Instalar", diff --git a/packages/kilo-i18n/src/fr.ts b/packages/kilo-i18n/src/fr.ts index 545f324f9e8..00bbb156e38 100644 --- a/packages/kilo-i18n/src/fr.ts +++ b/packages/kilo-i18n/src/fr.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "Serveurs MCP", "marketplace.tab.modes": "Modes", - "marketplace.search.skills": "Rechercher des skills...", "marketplace.category.all": "Tout", - "marketplace.empty.skills": "Aucune skill trouvée", "marketplace.placeholder": "À implémenter", "marketplace.card.installed": "Installé", "marketplace.card.install": "Installer", diff --git a/packages/kilo-i18n/src/ja.ts b/packages/kilo-i18n/src/ja.ts index c368f713a5e..e7841a7afd6 100644 --- a/packages/kilo-i18n/src/ja.ts +++ b/packages/kilo-i18n/src/ja.ts @@ -18,9 +18,7 @@ export const dict = { "marketplace.tab.skills": "スキル", "marketplace.tab.mcpServers": "MCPサーバー", "marketplace.tab.modes": "モード", - "marketplace.search.skills": "スキルを検索...", "marketplace.category.all": "すべて", - "marketplace.empty.skills": "スキルが見つかりません", "marketplace.placeholder": "未実装", "marketplace.card.installed": "インストール済み", "marketplace.card.install": "インストール", diff --git a/packages/kilo-i18n/src/ko.ts b/packages/kilo-i18n/src/ko.ts index 857dd478ea6..3dd65e72189 100644 --- a/packages/kilo-i18n/src/ko.ts +++ b/packages/kilo-i18n/src/ko.ts @@ -18,9 +18,7 @@ export const dict = { "marketplace.tab.skills": "스킬", "marketplace.tab.mcpServers": "MCP 서버", "marketplace.tab.modes": "모드", - "marketplace.search.skills": "스킬 검색...", "marketplace.category.all": "전체", - "marketplace.empty.skills": "스킬을 찾을 수 없습니다", "marketplace.placeholder": "구현 예정", "marketplace.card.installed": "설치됨", "marketplace.card.install": "설치", diff --git a/packages/kilo-i18n/src/no.ts b/packages/kilo-i18n/src/no.ts index 6878d64b0c3..449f8749ec0 100644 --- a/packages/kilo-i18n/src/no.ts +++ b/packages/kilo-i18n/src/no.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "MCP-servere", "marketplace.tab.modes": "Moduser", - "marketplace.search.skills": "Søk etter skills...", "marketplace.category.all": "Alle", - "marketplace.empty.skills": "Ingen skills funnet", "marketplace.placeholder": "Skal implementeres", "marketplace.card.installed": "Installert", "marketplace.card.install": "Installer", diff --git a/packages/kilo-i18n/src/pl.ts b/packages/kilo-i18n/src/pl.ts index 1cc4c5f420d..66a0c50c758 100644 --- a/packages/kilo-i18n/src/pl.ts +++ b/packages/kilo-i18n/src/pl.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Skills", "marketplace.tab.mcpServers": "Serwery MCP", "marketplace.tab.modes": "Tryby", - "marketplace.search.skills": "Szukaj skills...", "marketplace.category.all": "Wszystkie", - "marketplace.empty.skills": "Nie znaleziono skills", "marketplace.placeholder": "Do zaimplementowania", "marketplace.card.installed": "Zainstalowano", "marketplace.card.install": "Zainstaluj", diff --git a/packages/kilo-i18n/src/ru.ts b/packages/kilo-i18n/src/ru.ts index cf8a06e54a0..22936a6283d 100644 --- a/packages/kilo-i18n/src/ru.ts +++ b/packages/kilo-i18n/src/ru.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "Навыки", "marketplace.tab.mcpServers": "MCP-серверы", "marketplace.tab.modes": "Режимы", - "marketplace.search.skills": "Поиск навыков...", "marketplace.category.all": "Все", - "marketplace.empty.skills": "Навыки не найдены", "marketplace.placeholder": "Будет реализовано", "marketplace.card.installed": "Установлено", "marketplace.card.install": "Установить", diff --git a/packages/kilo-i18n/src/th.ts b/packages/kilo-i18n/src/th.ts index 196fbbecca9..5fea8e60398 100644 --- a/packages/kilo-i18n/src/th.ts +++ b/packages/kilo-i18n/src/th.ts @@ -19,9 +19,7 @@ export const dict = { "marketplace.tab.skills": "ทักษะ", "marketplace.tab.mcpServers": "เซิร์ฟเวอร์ MCP", "marketplace.tab.modes": "โหมด", - "marketplace.search.skills": "ค้นหาทักษะ...", "marketplace.category.all": "ทั้งหมด", - "marketplace.empty.skills": "ไม่พบทักษะ", "marketplace.placeholder": "ยังไม่ได้ดำเนินการ", "marketplace.card.installed": "ติดตั้งแล้ว", "marketplace.card.install": "ติดตั้ง", diff --git a/packages/kilo-i18n/src/zh.ts b/packages/kilo-i18n/src/zh.ts index 3ddf2b8c988..034435c970a 100644 --- a/packages/kilo-i18n/src/zh.ts +++ b/packages/kilo-i18n/src/zh.ts @@ -17,9 +17,7 @@ export const dict = { "marketplace.tab.skills": "技能", "marketplace.tab.mcpServers": "MCP 服务器", "marketplace.tab.modes": "模式", - "marketplace.search.skills": "搜索技能...", "marketplace.category.all": "全部", - "marketplace.empty.skills": "未找到技能", "marketplace.placeholder": "待实现", "marketplace.card.installed": "已安装", "marketplace.card.install": "安装", diff --git a/packages/kilo-i18n/src/zht.ts b/packages/kilo-i18n/src/zht.ts index ff74dab6e2f..09404c94306 100644 --- a/packages/kilo-i18n/src/zht.ts +++ b/packages/kilo-i18n/src/zht.ts @@ -17,9 +17,7 @@ export const dict = { "marketplace.tab.skills": "技能", "marketplace.tab.mcpServers": "MCP 伺服器", "marketplace.tab.modes": "模式", - "marketplace.search.skills": "搜尋技能...", "marketplace.category.all": "全部", - "marketplace.empty.skills": "找不到技能", "marketplace.placeholder": "待實作", "marketplace.card.installed": "已安裝", "marketplace.card.install": "安裝", diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx index 6746b0bebb6..733e1e62d8b 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceView.tsx @@ -147,8 +147,8 @@ export const MarketplaceView = () => { metadata={metadata()} fetching={fetching()} type="mcp" - searchPlaceholder={t("marketplace.search.mcp")} - emptyMessage={t("marketplace.empty.mcp")} + searchPlaceholder={t("marketplace.search")} + emptyMessage={t("marketplace.empty")} onInstall={handleInstall} onRemove={handleRemove} /> @@ -160,8 +160,8 @@ export const MarketplaceView = () => { metadata={metadata()} fetching={fetching()} type="mode" - searchPlaceholder={t("marketplace.search.modes")} - emptyMessage={t("marketplace.empty.modes")} + searchPlaceholder={t("marketplace.search")} + emptyMessage={t("marketplace.empty")} onInstall={handleInstall} onRemove={handleRemove} /> @@ -173,8 +173,8 @@ export const MarketplaceView = () => { metadata={metadata()} fetching={fetching()} type="skill" - searchPlaceholder={t("marketplace.search.skills")} - emptyMessage={t("marketplace.empty.skills")} + searchPlaceholder={t("marketplace.search")} + emptyMessage={t("marketplace.empty")} onInstall={handleInstall} onRemove={handleRemove} /> From c038bcfb148da7f38f63d83252ec2edccaf9107d Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Tue, 17 Mar 2026 11:05:21 +0100 Subject: [PATCH 05/25] fix(vscode): raise MCP tag threshold to 5 to reduce tag clutter --- .../src/components/marketplace/MarketplaceListView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx index ee1f07b0486..15651a20e63 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/MarketplaceListView.tsx @@ -51,7 +51,7 @@ export const MarketplaceListView = (props: Props) => { for (const item of props.items) { for (const tag of tagsFor(item)) counts.set(tag, (counts.get(tag) ?? 0) + 1) } - const min = props.type === "mcp" ? 2 : 1 + const min = props.type === "mcp" ? 5 : 1 return Array.from(counts.entries()) .filter(([, n]) => n >= min) .map(([tag]) => tag) From 13341256f6ef81ff90d9bd904c1798a5ff8f5187 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Tue, 17 Mar 2026 11:23:02 +0100 Subject: [PATCH 06/25] fix(vscode): cap marketplace card width with responsive auto-fill grid --- .../components/marketplace/marketplace.css | 9 ++----- .../src/stories/marketplace.stories.tsx | 27 ++++++++++++++----- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css b/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css index 5d958d7fdd2..c81470a5260 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/marketplace.css @@ -86,14 +86,9 @@ .marketplace-grid { display: grid; - grid-template-columns: 1fr; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; -} - -@media (min-width: 500px) { - .marketplace-grid { - grid-template-columns: repeat(2, 1fr); - } + max-width: 1200px; } /* Card */ diff --git a/packages/kilo-vscode/webview-ui/src/stories/marketplace.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/marketplace.stories.tsx index 253d7b99018..c6c30fb9528 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/marketplace.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/marketplace.stories.tsx @@ -2,14 +2,14 @@ /** * Stories for Marketplace components. * - * Renders SkillsMarketplace and ItemCard directly with mock data + * Renders MarketplaceListView and ItemCard directly with mock data * so no API requests are made. */ import type { Meta, StoryObj } from "storybook-solidjs-vite" import { StoryProviders } from "./StoryProviders" -import SkillsMarketplace from "../components/marketplace/SkillsMarketplace" -import ItemCard from "../components/marketplace/ItemCard" +import { MarketplaceListView } from "../components/marketplace/MarketplaceListView" +import { ItemCard } from "../components/marketplace/ItemCard" import type { SkillMarketplaceItem, MarketplaceInstalledMetadata } from "../types/marketplace" import "../components/marketplace/marketplace.css" @@ -116,10 +116,13 @@ export const SkillsTabWithItems: Story = { render: () => (
- @@ -133,10 +136,13 @@ export const SkillsTabWithInstalled: Story = { render: () => (
- @@ -150,7 +156,16 @@ export const SkillsTabEmpty: Story = { render: () => (
- +
), From 60c6cbab692aca9d775e08bfcef20f495e000225 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Mar 2026 10:39:42 +0000 Subject: [PATCH 07/25] chore: update kilo-vscode visual regression baselines --- .../marketplace/installed-skill-card-chromium-linux.png | 4 ++-- .../marketplace/skills-tab-with-installed-chromium-linux.png | 4 ++-- .../marketplace/skills-tab-with-items-chromium-linux.png | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/installed-skill-card-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/installed-skill-card-chromium-linux.png index 2822662560f..86b45cb700e 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/installed-skill-card-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/installed-skill-card-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bc9274e748f94e0ae979f688a87c52b9a3d579ac015a2a49f327f8c96d002db -size 11116 +oid sha256:0d5400d9db070953fcbf4cc3f8a4968b80c5bccb1a67ee63a463aa0539de1f41 +size 11190 diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-installed-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-installed-chromium-linux.png index 204b46b36ef..b7d88719025 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-installed-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-installed-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be90b297cbdef33fa1c3c67be7a42e4c00a5f3d68147a5fbf54a04ab7b261d04 -size 52330 +oid sha256:782c1fae4bed19825b56570e573d09cc80aa2180ca5c6a4c495867f207d95beb +size 54728 diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-items-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-items-chromium-linux.png index 1bda64ef203..899e1a2c4ad 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-items-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/marketplace/skills-tab-with-items-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d27dc2c85a377f7daab0b7de444a7d883a4503ab45c92c0c0a9155de89e2a221 -size 49775 +oid sha256:3d309302b177f97f198a2b889c96d08cf4578d219043d48a9d0251460be411c4 +size 52039 From e8575fbf02d9298b4222a31e85960b90b0ec62ef Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Tue, 17 Mar 2026 11:44:20 +0100 Subject: [PATCH 08/25] feat(vscode): increase marketplace card size and add expandable descriptions --- packages/kilo-i18n/src/ar.ts | 2 ++ packages/kilo-i18n/src/br.ts | 2 ++ packages/kilo-i18n/src/bs.ts | 2 ++ packages/kilo-i18n/src/da.ts | 2 ++ packages/kilo-i18n/src/de.ts | 2 ++ packages/kilo-i18n/src/en.ts | 2 ++ packages/kilo-i18n/src/es.ts | 2 ++ packages/kilo-i18n/src/fr.ts | 2 ++ packages/kilo-i18n/src/ja.ts | 2 ++ packages/kilo-i18n/src/ko.ts | 2 ++ packages/kilo-i18n/src/no.ts | 2 ++ packages/kilo-i18n/src/pl.ts | 2 ++ packages/kilo-i18n/src/ru.ts | 2 ++ packages/kilo-i18n/src/th.ts | 2 ++ packages/kilo-i18n/src/tr.ts | 4 ++++ packages/kilo-i18n/src/zh.ts | 2 ++ packages/kilo-i18n/src/zht.ts | 2 ++ .../src/components/marketplace/ItemCard.tsx | 18 ++++++++++++++++-- .../src/components/marketplace/marketplace.css | 18 +++++++++++++++++- 19 files changed, 69 insertions(+), 3 deletions(-) diff --git a/packages/kilo-i18n/src/ar.ts b/packages/kilo-i18n/src/ar.ts index 935a63b24d6..268cac7ca7d 100644 --- a/packages/kilo-i18n/src/ar.ts +++ b/packages/kilo-i18n/src/ar.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "تثبيت", "marketplace.card.remove": "إزالة", "marketplace.card.removeScope": "إزالة ({{scope}})", + "marketplace.card.showMore": "عرض المزيد", + "marketplace.card.showLess": "عرض أقل", "marketplace.install.title": "تثبيت {{name}}", "marketplace.install.scope": "النطاق", "marketplace.install.scope.project": "المشروع", diff --git a/packages/kilo-i18n/src/br.ts b/packages/kilo-i18n/src/br.ts index 9714a6e3c65..577971ee9a4 100644 --- a/packages/kilo-i18n/src/br.ts +++ b/packages/kilo-i18n/src/br.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Instalar", "marketplace.card.remove": "Remover", "marketplace.card.removeScope": "Remover ({{scope}})", + "marketplace.card.showMore": "Mostrar mais", + "marketplace.card.showLess": "Mostrar menos", "marketplace.install.title": "Instalar {{name}}", "marketplace.install.scope": "Escopo", "marketplace.install.scope.project": "Projeto", diff --git a/packages/kilo-i18n/src/bs.ts b/packages/kilo-i18n/src/bs.ts index f225ff43beb..2a816969422 100644 --- a/packages/kilo-i18n/src/bs.ts +++ b/packages/kilo-i18n/src/bs.ts @@ -30,6 +30,8 @@ export const dict = { "marketplace.card.install": "Instaliraj", "marketplace.card.remove": "Ukloni", "marketplace.card.removeScope": "Ukloni ({{scope}})", + "marketplace.card.showMore": "Prikaži više", + "marketplace.card.showLess": "Prikaži manje", "marketplace.install.title": "Instaliraj {{name}}", "marketplace.install.scope": "Opseg", "marketplace.install.scope.project": "Projekat", diff --git a/packages/kilo-i18n/src/da.ts b/packages/kilo-i18n/src/da.ts index 35e7bda83f2..c0230f60270 100644 --- a/packages/kilo-i18n/src/da.ts +++ b/packages/kilo-i18n/src/da.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Installer", "marketplace.card.remove": "Fjern", "marketplace.card.removeScope": "Fjern ({{scope}})", + "marketplace.card.showMore": "Vis mere", + "marketplace.card.showLess": "Vis mindre", "marketplace.install.title": "Installer {{name}}", "marketplace.install.scope": "Omfang", "marketplace.install.scope.project": "Projekt", diff --git a/packages/kilo-i18n/src/de.ts b/packages/kilo-i18n/src/de.ts index fed983ecbaf..0484fbaf44a 100644 --- a/packages/kilo-i18n/src/de.ts +++ b/packages/kilo-i18n/src/de.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Installieren", "marketplace.card.remove": "Entfernen", "marketplace.card.removeScope": "Entfernen ({{scope}})", + "marketplace.card.showMore": "Mehr anzeigen", + "marketplace.card.showLess": "Weniger anzeigen", "marketplace.install.title": "{{name}} installieren", "marketplace.install.scope": "Bereich", "marketplace.install.scope.project": "Projekt", diff --git a/packages/kilo-i18n/src/en.ts b/packages/kilo-i18n/src/en.ts index e1726bb2881..4f306eab419 100644 --- a/packages/kilo-i18n/src/en.ts +++ b/packages/kilo-i18n/src/en.ts @@ -27,6 +27,8 @@ export const dict = { "marketplace.card.install": "Install", "marketplace.card.remove": "Remove", "marketplace.card.removeScope": "Remove ({{scope}})", + "marketplace.card.showMore": "Show more", + "marketplace.card.showLess": "Show less", "marketplace.install.title": "Install {{name}}", "marketplace.install.scope": "Scope", "marketplace.install.scope.project": "Project", diff --git a/packages/kilo-i18n/src/es.ts b/packages/kilo-i18n/src/es.ts index 2560cd4cf50..2fbbe34051e 100644 --- a/packages/kilo-i18n/src/es.ts +++ b/packages/kilo-i18n/src/es.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Instalar", "marketplace.card.remove": "Eliminar", "marketplace.card.removeScope": "Eliminar ({{scope}})", + "marketplace.card.showMore": "Mostrar más", + "marketplace.card.showLess": "Mostrar menos", "marketplace.install.title": "Instalar {{name}}", "marketplace.install.scope": "Alcance", "marketplace.install.scope.project": "Proyecto", diff --git a/packages/kilo-i18n/src/fr.ts b/packages/kilo-i18n/src/fr.ts index 00bbb156e38..d71544eaaf6 100644 --- a/packages/kilo-i18n/src/fr.ts +++ b/packages/kilo-i18n/src/fr.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Installer", "marketplace.card.remove": "Supprimer", "marketplace.card.removeScope": "Supprimer ({{scope}})", + "marketplace.card.showMore": "Afficher plus", + "marketplace.card.showLess": "Afficher moins", "marketplace.install.title": "Installer {{name}}", "marketplace.install.scope": "Portée", "marketplace.install.scope.project": "Projet", diff --git a/packages/kilo-i18n/src/ja.ts b/packages/kilo-i18n/src/ja.ts index e7841a7afd6..ac16c110b31 100644 --- a/packages/kilo-i18n/src/ja.ts +++ b/packages/kilo-i18n/src/ja.ts @@ -24,6 +24,8 @@ export const dict = { "marketplace.card.install": "インストール", "marketplace.card.remove": "削除", "marketplace.card.removeScope": "削除 ({{scope}})", + "marketplace.card.showMore": "もっと見る", + "marketplace.card.showLess": "折りたたむ", "marketplace.install.title": "{{name}} のインストール", "marketplace.install.scope": "スコープ", "marketplace.install.scope.project": "プロジェクト", diff --git a/packages/kilo-i18n/src/ko.ts b/packages/kilo-i18n/src/ko.ts index 3dd65e72189..4c4d3683b80 100644 --- a/packages/kilo-i18n/src/ko.ts +++ b/packages/kilo-i18n/src/ko.ts @@ -24,6 +24,8 @@ export const dict = { "marketplace.card.install": "설치", "marketplace.card.remove": "제거", "marketplace.card.removeScope": "제거 ({{scope}})", + "marketplace.card.showMore": "더 보기", + "marketplace.card.showLess": "접기", "marketplace.install.title": "{{name}} 설치", "marketplace.install.scope": "범위", "marketplace.install.scope.project": "프로젝트", diff --git a/packages/kilo-i18n/src/no.ts b/packages/kilo-i18n/src/no.ts index 449f8749ec0..82ae6fe678c 100644 --- a/packages/kilo-i18n/src/no.ts +++ b/packages/kilo-i18n/src/no.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Installer", "marketplace.card.remove": "Fjern", "marketplace.card.removeScope": "Fjern ({{scope}})", + "marketplace.card.showMore": "Vis mer", + "marketplace.card.showLess": "Vis mindre", "marketplace.install.title": "Installer {{name}}", "marketplace.install.scope": "Omfang", "marketplace.install.scope.project": "Prosjekt", diff --git a/packages/kilo-i18n/src/pl.ts b/packages/kilo-i18n/src/pl.ts index 66a0c50c758..f80fd5635c5 100644 --- a/packages/kilo-i18n/src/pl.ts +++ b/packages/kilo-i18n/src/pl.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Zainstaluj", "marketplace.card.remove": "Usuń", "marketplace.card.removeScope": "Usuń ({{scope}})", + "marketplace.card.showMore": "Pokaż więcej", + "marketplace.card.showLess": "Pokaż mniej", "marketplace.install.title": "Instalacja: {{name}}", "marketplace.install.scope": "Zakres", "marketplace.install.scope.project": "Projekt", diff --git a/packages/kilo-i18n/src/ru.ts b/packages/kilo-i18n/src/ru.ts index 22936a6283d..cb105f7f619 100644 --- a/packages/kilo-i18n/src/ru.ts +++ b/packages/kilo-i18n/src/ru.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "Установить", "marketplace.card.remove": "Удалить", "marketplace.card.removeScope": "Удалить ({{scope}})", + "marketplace.card.showMore": "Показать больше", + "marketplace.card.showLess": "Показать меньше", "marketplace.install.title": "Установка {{name}}", "marketplace.install.scope": "Область", "marketplace.install.scope.project": "Проект", diff --git a/packages/kilo-i18n/src/th.ts b/packages/kilo-i18n/src/th.ts index 5fea8e60398..fff39198b1b 100644 --- a/packages/kilo-i18n/src/th.ts +++ b/packages/kilo-i18n/src/th.ts @@ -25,6 +25,8 @@ export const dict = { "marketplace.card.install": "ติดตั้ง", "marketplace.card.remove": "ลบ", "marketplace.card.removeScope": "ลบ ({{scope}})", + "marketplace.card.showMore": "แสดงเพิ่มเติม", + "marketplace.card.showLess": "แสดงน้อยลง", "marketplace.install.title": "ติดตั้ง {{name}}", "marketplace.install.scope": "ขอบเขต", "marketplace.install.scope.project": "โปรเจกต์", diff --git a/packages/kilo-i18n/src/tr.ts b/packages/kilo-i18n/src/tr.ts index 1b4c19c6fda..13c58a73cf8 100644 --- a/packages/kilo-i18n/src/tr.ts +++ b/packages/kilo-i18n/src/tr.ts @@ -14,4 +14,8 @@ export const dict = { // Reasoning block label "ui.reasoning.label": "Akıl Yürütme", + + // Marketplace card translations + "marketplace.card.showMore": "Daha fazla göster", + "marketplace.card.showLess": "Daha az göster", } diff --git a/packages/kilo-i18n/src/zh.ts b/packages/kilo-i18n/src/zh.ts index 034435c970a..eee8c336df0 100644 --- a/packages/kilo-i18n/src/zh.ts +++ b/packages/kilo-i18n/src/zh.ts @@ -23,6 +23,8 @@ export const dict = { "marketplace.card.install": "安装", "marketplace.card.remove": "移除", "marketplace.card.removeScope": "移除 ({{scope}})", + "marketplace.card.showMore": "展开", + "marketplace.card.showLess": "收起", "marketplace.install.title": "安装 {{name}}", "marketplace.install.scope": "作用域", "marketplace.install.scope.project": "项目", diff --git a/packages/kilo-i18n/src/zht.ts b/packages/kilo-i18n/src/zht.ts index 09404c94306..1fcb8749fb9 100644 --- a/packages/kilo-i18n/src/zht.ts +++ b/packages/kilo-i18n/src/zht.ts @@ -23,6 +23,8 @@ export const dict = { "marketplace.card.install": "安裝", "marketplace.card.remove": "移除", "marketplace.card.removeScope": "移除 ({{scope}})", + "marketplace.card.showMore": "展開", + "marketplace.card.showLess": "收起", "marketplace.install.title": "安裝 {{name}}", "marketplace.install.scope": "作用域", "marketplace.install.scope.project": "專案", diff --git a/packages/kilo-vscode/webview-ui/src/components/marketplace/ItemCard.tsx b/packages/kilo-vscode/webview-ui/src/components/marketplace/ItemCard.tsx index 96dfc2c5f78..2c18fee39a8 100644 --- a/packages/kilo-vscode/webview-ui/src/components/marketplace/ItemCard.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/marketplace/ItemCard.tsx @@ -1,4 +1,4 @@ -import { Show, JSX } from "solid-js" +import { Show, JSX, createSignal, onMount } from "solid-js" import { Button } from "@kilocode/kilo-ui/button" import { Card } from "@kilocode/kilo-ui/card" import { Tag } from "@kilocode/kilo-ui/tag" @@ -24,6 +24,13 @@ export const ItemCard = (props: Props) => { const scopes = () => installedScopes(props.item.id, props.item.type, props.metadata) const installed = () => scopes().length > 0 const name = () => props.displayName ?? props.item.name + const [expanded, setExpanded] = createSignal(false) + const [clamped, setClamped] = createSignal(false) + let ref: HTMLParagraphElement | undefined + + onMount(() => { + if (ref && ref.scrollHeight > ref.clientHeight) setClamped(true) + }) const openLink = (url: string) => { vscode.postMessage({ type: "openExternal", url }) @@ -52,7 +59,14 @@ export const ItemCard = (props: Props) => {
-

{props.item.description}

+

+ {props.item.description} +

+ + +