mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-10 22:41:14 +00:00
feat: add confirmation dialog and proper cleanup for marketplace mode removal (#6136)
Co-authored-by: Daniel Riccio <ricciodaniel98@gmail.com>
This commit is contained in:
parent
3977bbc0ce
commit
1e17b3b3bb
44 changed files with 887 additions and 277 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import * as vscode from "vscode"
|
||||
import * as path from "path"
|
||||
import * as fs from "fs/promises"
|
||||
import * as os from "os"
|
||||
|
||||
import * as yaml from "yaml"
|
||||
import stripBom from "strip-bom"
|
||||
|
|
@ -501,7 +502,7 @@ export class CustomModesManager {
|
|||
await this.onUpdate()
|
||||
}
|
||||
|
||||
public async deleteCustomMode(slug: string): Promise<void> {
|
||||
public async deleteCustomMode(slug: string, fromMarketplace = false): Promise<void> {
|
||||
try {
|
||||
const settingsPath = await this.getCustomModesFilePath()
|
||||
const roomodesPath = await this.getWorkspaceRoomodes()
|
||||
|
|
@ -517,6 +518,9 @@ export class CustomModesManager {
|
|||
throw new Error(t("common:customModes.errors.modeNotFound"))
|
||||
}
|
||||
|
||||
// Determine which mode to use for rules folder path calculation
|
||||
const modeToDelete = projectMode || globalMode
|
||||
|
||||
await this.queueWrite(async () => {
|
||||
// Delete from project first if it exists there
|
||||
if (projectMode && roomodesPath) {
|
||||
|
|
@ -528,6 +532,11 @@ export class CustomModesManager {
|
|||
await this.updateModesInFile(settingsPath, (modes) => modes.filter((m) => m.slug !== slug))
|
||||
}
|
||||
|
||||
// Delete associated rules folder
|
||||
if (modeToDelete) {
|
||||
await this.deleteRulesFolder(slug, modeToDelete, fromMarketplace)
|
||||
}
|
||||
|
||||
// Clear cache when modes are deleted
|
||||
this.clearCache()
|
||||
await this.refreshMergedState()
|
||||
|
|
@ -538,6 +547,54 @@ export class CustomModesManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the rules folder for a specific mode
|
||||
* @param slug - The mode slug
|
||||
* @param mode - The mode configuration to determine the scope
|
||||
*/
|
||||
private async deleteRulesFolder(slug: string, mode: ModeConfig, fromMarketplace = false): Promise<void> {
|
||||
try {
|
||||
// Determine the scope based on source (project or global)
|
||||
const scope = mode.source || "global"
|
||||
|
||||
// Determine the rules folder path
|
||||
let rulesFolderPath: string
|
||||
if (scope === "project") {
|
||||
const workspacePath = getWorkspacePath()
|
||||
if (workspacePath) {
|
||||
rulesFolderPath = path.join(workspacePath, ".roo", `rules-${slug}`)
|
||||
} else {
|
||||
return // No workspace, can't delete project rules
|
||||
}
|
||||
} else {
|
||||
// Global scope - use OS home directory
|
||||
const homeDir = os.homedir()
|
||||
rulesFolderPath = path.join(homeDir, ".roo", `rules-${slug}`)
|
||||
}
|
||||
|
||||
// Check if the rules folder exists and delete it
|
||||
const rulesFolderExists = await fileExistsAtPath(rulesFolderPath)
|
||||
if (rulesFolderExists) {
|
||||
try {
|
||||
await fs.rm(rulesFolderPath, { recursive: true, force: true })
|
||||
logger.info(`Deleted rules folder for mode ${slug}: ${rulesFolderPath}`)
|
||||
} catch (error) {
|
||||
logger.error(`Failed to delete rules folder for mode ${slug}: ${error}`)
|
||||
// Notify the user about the failure
|
||||
const messageKey = fromMarketplace
|
||||
? "common:marketplace.mode.rulesCleanupFailed"
|
||||
: "common:customModes.errors.rulesCleanupFailed"
|
||||
vscode.window.showWarningMessage(t(messageKey, { rulesFolderPath }))
|
||||
// Continue even if folder deletion fails
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting rules folder for mode ${slug}`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public async resetCustomModes(): Promise<void> {
|
||||
try {
|
||||
const filePath = await this.getCustomModesFilePath()
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ export class ClineProvider
|
|||
this.log(`Failed to initialize MCP Hub: ${error}`)
|
||||
})
|
||||
|
||||
this.marketplaceManager = new MarketplaceManager(this.context)
|
||||
this.marketplaceManager = new MarketplaceManager(this.context, this.customModesManager)
|
||||
}
|
||||
|
||||
// Adds a new Cline instance to clineStack, marking the start of a new task.
|
||||
|
|
|
|||
|
|
@ -2229,8 +2229,45 @@ export const webviewMessageHandler = async (
|
|||
try {
|
||||
await marketplaceManager.removeInstalledMarketplaceItem(message.mpItem, message.mpInstallOptions)
|
||||
await provider.postStateToWebview()
|
||||
|
||||
// Send success message to webview
|
||||
provider.postMessageToWebview({
|
||||
type: "marketplaceRemoveResult",
|
||||
success: true,
|
||||
slug: message.mpItem.id,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`Error removing marketplace item: ${error}`)
|
||||
|
||||
// Show error message to user
|
||||
vscode.window.showErrorMessage(
|
||||
`Failed to remove marketplace item: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
|
||||
// Send error message to webview
|
||||
provider.postMessageToWebview({
|
||||
type: "marketplaceRemoveResult",
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
slug: message.mpItem.id,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// MarketplaceManager not available or missing required parameters
|
||||
const errorMessage = !marketplaceManager
|
||||
? "Marketplace manager is not available"
|
||||
: "Missing required parameters for marketplace item removal"
|
||||
console.error(errorMessage)
|
||||
|
||||
vscode.window.showErrorMessage(errorMessage)
|
||||
|
||||
if (message.mpItem?.id) {
|
||||
provider.postMessageToWebview({
|
||||
type: "marketplaceRemoveResult",
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
slug: message.mpItem.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
break
|
||||
|
|
|
|||
8
src/i18n/locales/ca/common.json
generated
8
src/i18n/locales/ca/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Error en eliminar el mode personalitzat: {{error}}",
|
||||
"resetFailed": "Error en restablir els modes personalitzats: {{error}}",
|
||||
"modeNotFound": "Error d'escriptura: Mode no trobat",
|
||||
"noWorkspaceForProject": "No s'ha trobat cap carpeta d'espai de treball per al mode específic del projecte"
|
||||
"noWorkspaceForProject": "No s'ha trobat cap carpeta d'espai de treball per al mode específic del projecte",
|
||||
"rulesCleanupFailed": "El mode s'ha suprimit correctament, però no s'ha pogut suprimir la carpeta de regles a {{rulesFolderPath}}. És possible que l'hagis de suprimir manualment."
|
||||
},
|
||||
"scope": {
|
||||
"project": "projecte",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "El mode s'ha eliminat correctament, però no s'ha pogut eliminar la carpeta de regles a {{rulesFolderPath}}. És possible que l'hagis d'eliminar manualment."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "La teva organització requereix autenticació de Roo Code Cloud. Si us plau, inicia sessió per continuar.",
|
||||
|
|
|
|||
8
src/i18n/locales/de/common.json
generated
8
src/i18n/locales/de/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Fehler beim Löschen des benutzerdefinierten Modus: {{error}}",
|
||||
"resetFailed": "Fehler beim Zurücksetzen der benutzerdefinierten Modi: {{error}}",
|
||||
"modeNotFound": "Schreibfehler: Modus nicht gefunden",
|
||||
"noWorkspaceForProject": "Kein Arbeitsbereich-Ordner für projektspezifischen Modus gefunden"
|
||||
"noWorkspaceForProject": "Kein Arbeitsbereich-Ordner für projektspezifischen Modus gefunden",
|
||||
"rulesCleanupFailed": "Der Modus wurde erfolgreich gelöscht, aber der Regelordner unter {{rulesFolderPath}} konnte nicht gelöscht werden. Möglicherweise musst du ihn manuell löschen."
|
||||
},
|
||||
"scope": {
|
||||
"project": "projekt",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Der Modus wurde erfolgreich entfernt, aber der Regelordner unter {{rulesFolderPath}} konnte nicht gelöscht werden. Möglicherweise musst du ihn manuell löschen."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Deine Organisation erfordert eine Roo Code Cloud-Authentifizierung. Bitte melde dich an, um fortzufahren.",
|
||||
|
|
|
|||
|
|
@ -136,13 +136,19 @@
|
|||
"deleteFailed": "Failed to delete custom mode: {{error}}",
|
||||
"resetFailed": "Failed to reset custom modes: {{error}}",
|
||||
"modeNotFound": "Write error: Mode not found",
|
||||
"noWorkspaceForProject": "No workspace folder found for project-specific mode"
|
||||
"noWorkspaceForProject": "No workspace folder found for project-specific mode",
|
||||
"rulesCleanupFailed": "Mode deleted successfully, but failed to delete rules folder at {{rulesFolderPath}}. You may need to delete it manually."
|
||||
},
|
||||
"scope": {
|
||||
"project": "project",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Mode removed successfully, but failed to delete rules folder at {{rulesFolderPath}}. You may need to delete it manually."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Your organization requires Roo Code Cloud authentication. Please sign in to continue.",
|
||||
|
|
|
|||
8
src/i18n/locales/es/common.json
generated
8
src/i18n/locales/es/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Error al eliminar modo personalizado: {{error}}",
|
||||
"resetFailed": "Error al restablecer modos personalizados: {{error}}",
|
||||
"modeNotFound": "Error de escritura: Modo no encontrado",
|
||||
"noWorkspaceForProject": "No se encontró carpeta de espacio de trabajo para modo específico del proyecto"
|
||||
"noWorkspaceForProject": "No se encontró carpeta de espacio de trabajo para modo específico del proyecto",
|
||||
"rulesCleanupFailed": "El modo se eliminó correctamente, pero no se pudo eliminar la carpeta de reglas en {{rulesFolderPath}}. Es posible que debas eliminarla manualmente."
|
||||
},
|
||||
"scope": {
|
||||
"project": "proyecto",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "El modo se eliminó correctamente, pero no se pudo eliminar la carpeta de reglas en {{rulesFolderPath}}. Es posible que debas eliminarla manually."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Tu organización requiere autenticación de Roo Code Cloud. Por favor, inicia sesión para continuar.",
|
||||
|
|
|
|||
8
src/i18n/locales/fr/common.json
generated
8
src/i18n/locales/fr/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Échec de la suppression du mode personnalisé : {{error}}",
|
||||
"resetFailed": "Échec de la réinitialisation des modes personnalisés : {{error}}",
|
||||
"modeNotFound": "Erreur d'écriture : Mode non trouvé",
|
||||
"noWorkspaceForProject": "Aucun dossier d'espace de travail trouvé pour le mode spécifique au projet"
|
||||
"noWorkspaceForProject": "Aucun dossier d'espace de travail trouvé pour le mode spécifique au projet",
|
||||
"rulesCleanupFailed": "Le mode a été supprimé avec succès, mais la suppression du dossier de règles à l'adresse {{rulesFolderPath}} a échoué. Vous devrez peut-être le supprimer manuellement."
|
||||
},
|
||||
"scope": {
|
||||
"project": "projet",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Le mode a été supprimé avec succès, mais la suppression du dossier de règles à l'adresse {{rulesFolderPath}} a échoué. Vous devrez peut-être le supprimer manuellement."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Votre organisation nécessite une authentification Roo Code Cloud. Veuillez vous connecter pour continuer.",
|
||||
|
|
|
|||
8
src/i18n/locales/hi/common.json
generated
8
src/i18n/locales/hi/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "कस्टम मोड डिलीट विफल: {{error}}",
|
||||
"resetFailed": "कस्टम मोड रीसेट विफल: {{error}}",
|
||||
"modeNotFound": "लेखन त्रुटि: मोड नहीं मिला",
|
||||
"noWorkspaceForProject": "प्रोजेक्ट-विशिष्ट मोड के लिए वर्कस्पेस फ़ोल्डर नहीं मिला"
|
||||
"noWorkspaceForProject": "प्रोजेक्ट-विशिष्ट मोड के लिए वर्कस्पेस फ़ोल्डर नहीं मिला",
|
||||
"rulesCleanupFailed": "मोड सफलतापूर्वक हटा दिया गया, लेकिन {{rulesFolderPath}} पर नियम फ़ोल्डर को हटाने में विफल रहा। आपको इसे मैन्युअल रूप से हटाना पड़ सकता है।"
|
||||
},
|
||||
"scope": {
|
||||
"project": "परियोजना",
|
||||
"global": "वैश्विक"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "मोड सफलतापूर्वक हटा दिया गया, लेकिन {{rulesFolderPath}} पर नियम फ़ोल्डर को हटाने में विफल रहा। आपको इसे मैन्युअल रूप से हटाना पड़ सकता है।"
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "आपके संगठन को Roo Code Cloud प्रमाणीकरण की आवश्यकता है। कृपया जारी रखने के लिए साइन इन करें।",
|
||||
|
|
|
|||
8
src/i18n/locales/id/common.json
generated
8
src/i18n/locales/id/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Gagal menghapus mode kustom: {{error}}",
|
||||
"resetFailed": "Gagal mereset mode kustom: {{error}}",
|
||||
"modeNotFound": "Kesalahan tulis: Mode tidak ditemukan",
|
||||
"noWorkspaceForProject": "Tidak ditemukan folder workspace untuk mode khusus proyek"
|
||||
"noWorkspaceForProject": "Tidak ditemukan folder workspace untuk mode khusus proyek",
|
||||
"rulesCleanupFailed": "Mode berhasil dihapus, tetapi gagal menghapus folder aturan di {{rulesFolderPath}}. Kamu mungkin perlu menghapusnya secara manual."
|
||||
},
|
||||
"scope": {
|
||||
"project": "proyek",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Mode berhasil dihapus, tetapi gagal menghapus folder aturan di {{rulesFolderPath}}. Kamu mungkin perlu menghapusnya secara manual."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Organisasi kamu memerlukan autentikasi Roo Code Cloud. Silakan masuk untuk melanjutkan.",
|
||||
|
|
|
|||
8
src/i18n/locales/it/common.json
generated
8
src/i18n/locales/it/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Eliminazione modalità personalizzata fallita: {{error}}",
|
||||
"resetFailed": "Reset modalità personalizzate fallito: {{error}}",
|
||||
"modeNotFound": "Errore di scrittura: Modalità non trovata",
|
||||
"noWorkspaceForProject": "Nessuna cartella workspace trovata per la modalità specifica del progetto"
|
||||
"noWorkspaceForProject": "Nessuna cartella workspace trovata per la modalità specifica del progetto",
|
||||
"rulesCleanupFailed": "La modalità è stata eliminata con successo, ma non è stato possibile eliminare la cartella delle regole in {{rulesFolderPath}}. Potrebbe essere necessario eliminarla manualmente."
|
||||
},
|
||||
"scope": {
|
||||
"project": "progetto",
|
||||
"global": "globale"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "La modalità è stata rimossa con successo, ma non è stato possibile eliminare la cartella delle regole in {{rulesFolderPath}}. Potrebbe essere necessario eliminarla manualmente."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "La tua organizzazione richiede l'autenticazione Roo Code Cloud. Accedi per continuare.",
|
||||
|
|
|
|||
8
src/i18n/locales/ja/common.json
generated
8
src/i18n/locales/ja/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "カスタムモードの削除に失敗しました:{{error}}",
|
||||
"resetFailed": "カスタムモードのリセットに失敗しました:{{error}}",
|
||||
"modeNotFound": "書き込みエラー:モードが見つかりません",
|
||||
"noWorkspaceForProject": "プロジェクト固有モード用のワークスペースフォルダーが見つかりません"
|
||||
"noWorkspaceForProject": "プロジェクト固有モード用のワークスペースフォルダーが見つかりません",
|
||||
"rulesCleanupFailed": "モードは正常に削除されましたが、{{rulesFolderPath}} にあるルールフォルダの削除に失敗しました。手動で削除する必要がある場合があります。"
|
||||
},
|
||||
"scope": {
|
||||
"project": "プロジェクト",
|
||||
"global": "グローバル"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "モードは正常に削除されましたが、{{rulesFolderPath}} にあるルールフォルダの削除に失敗しました。手動で削除する必要がある場合があります。"
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "あなたの組織では Roo Code Cloud 認証が必要です。続行するにはサインインしてください。",
|
||||
|
|
|
|||
8
src/i18n/locales/ko/common.json
generated
8
src/i18n/locales/ko/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "사용자 정의 모드 삭제 실패: {{error}}",
|
||||
"resetFailed": "사용자 정의 모드 재설정 실패: {{error}}",
|
||||
"modeNotFound": "쓰기 오류: 모드를 찾을 수 없습니다",
|
||||
"noWorkspaceForProject": "프로젝트별 모드용 작업 공간 폴더를 찾을 수 없습니다"
|
||||
"noWorkspaceForProject": "프로젝트별 모드용 작업 공간 폴더를 찾을 수 없습니다",
|
||||
"rulesCleanupFailed": "모드가 성공적으로 삭제되었지만 {{rulesFolderPath}}의 규칙 폴더를 삭제하지 못했습니다. 수동으로 삭제해야 할 수도 있습니다."
|
||||
},
|
||||
"scope": {
|
||||
"project": "프로젝트",
|
||||
"global": "글로벌"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "모드가 성공적으로 제거되었지만 {{rulesFolderPath}}의 규칙 폴더를 삭제하지 못했습니다. 수동으로 삭제해야 할 수도 있습니다."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "조직에서 Roo Code Cloud 인증이 필요합니다. 계속하려면 로그인하세요.",
|
||||
|
|
|
|||
8
src/i18n/locales/nl/common.json
generated
8
src/i18n/locales/nl/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Aangepaste modus verwijderen mislukt: {{error}}",
|
||||
"resetFailed": "Aangepaste modi resetten mislukt: {{error}}",
|
||||
"modeNotFound": "Schrijffout: Modus niet gevonden",
|
||||
"noWorkspaceForProject": "Geen workspace map gevonden voor projectspecifieke modus"
|
||||
"noWorkspaceForProject": "Geen workspace map gevonden voor projectspecifieke modus",
|
||||
"rulesCleanupFailed": "Modus succesvol verwijderd, maar het verwijderen van de regelsmap op {{rulesFolderPath}} is mislukt. Je moet deze mogelijk handmatig verwijderen."
|
||||
},
|
||||
"scope": {
|
||||
"project": "project",
|
||||
"global": "globaal"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Modus succesvol verwijderd, maar het verwijderen van de regelsmap op {{rulesFolderPath}} is mislukt. Je moet deze mogelijk handmatig verwijderen."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Je organisatie vereist Roo Code Cloud-authenticatie. Log in om door te gaan.",
|
||||
|
|
|
|||
8
src/i18n/locales/pl/common.json
generated
8
src/i18n/locales/pl/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Usunięcie trybu niestandardowego nie powiodło się: {{error}}",
|
||||
"resetFailed": "Resetowanie trybów niestandardowych nie powiodło się: {{error}}",
|
||||
"modeNotFound": "Błąd zapisu: Tryb nie został znaleziony",
|
||||
"noWorkspaceForProject": "Nie znaleziono folderu obszaru roboczego dla trybu specyficznego dla projektu"
|
||||
"noWorkspaceForProject": "Nie znaleziono folderu obszaru roboczego dla trybu specyficznego dla projektu",
|
||||
"rulesCleanupFailed": "Tryb został pomyślnie usunięty, ale nie udało się usunąć folderu reguł w {{rulesFolderPath}}. Może być konieczne ręczne usunięcie."
|
||||
},
|
||||
"scope": {
|
||||
"project": "projekt",
|
||||
"global": "globalny"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Tryb został pomyślnie usunięty, ale nie udało się usunąć folderu reguł w {{rulesFolderPath}}. Może być konieczne ręczne usunięcie."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Twoja organizacja wymaga uwierzytelnienia Roo Code Cloud. Zaloguj się, aby kontynuować.",
|
||||
|
|
|
|||
8
src/i18n/locales/pt-BR/common.json
generated
8
src/i18n/locales/pt-BR/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Falha ao excluir modo personalizado: {{error}}",
|
||||
"resetFailed": "Falha ao redefinir modos personalizados: {{error}}",
|
||||
"modeNotFound": "Erro de escrita: Modo não encontrado",
|
||||
"noWorkspaceForProject": "Nenhuma pasta de workspace encontrada para modo específico do projeto"
|
||||
"noWorkspaceForProject": "Nenhuma pasta de workspace encontrada para modo específico do projeto",
|
||||
"rulesCleanupFailed": "O modo foi excluído com sucesso, mas falhou ao excluir a pasta de regras em {{rulesFolderPath}}. Você pode precisar excluí-la manualmente."
|
||||
},
|
||||
"scope": {
|
||||
"project": "projeto",
|
||||
"global": "global"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "O modo foi removido com sucesso, mas falhou ao excluir a pasta de regras em {{rulesFolderPath}}. Você pode precisar excluí-la manualmente."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Sua organização requer autenticação do Roo Code Cloud. Faça login para continuar.",
|
||||
|
|
|
|||
8
src/i18n/locales/ru/common.json
generated
8
src/i18n/locales/ru/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Не удалось удалить пользовательский режим: {{error}}",
|
||||
"resetFailed": "Не удалось сбросить пользовательские режимы: {{error}}",
|
||||
"modeNotFound": "Ошибка записи: Режим не найден",
|
||||
"noWorkspaceForProject": "Не найдена папка рабочего пространства для режима, специфичного для проекта"
|
||||
"noWorkspaceForProject": "Не найдена папка рабочего пространства для режима, специфичного для проекта",
|
||||
"rulesCleanupFailed": "Режим успешно удален, но не удалось удалить папку правил в {{rulesFolderPath}}. Возможно, вам придется удалить ее вручную."
|
||||
},
|
||||
"scope": {
|
||||
"project": "проект",
|
||||
"global": "глобальный"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Режим успешно удален, но не удалось удалить папку правил в {{rulesFolderPath}}. Возможно, вам придется удалить ее вручную."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Ваша организация требует аутентификации Roo Code Cloud. Войдите в систему, чтобы продолжить.",
|
||||
|
|
|
|||
8
src/i18n/locales/tr/common.json
generated
8
src/i18n/locales/tr/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Özel mod silme başarısız: {{error}}",
|
||||
"resetFailed": "Özel modları sıfırlama başarısız: {{error}}",
|
||||
"modeNotFound": "Yazma hatası: Mod bulunamadı",
|
||||
"noWorkspaceForProject": "Proje özel modu için çalışma alanı klasörü bulunamadı"
|
||||
"noWorkspaceForProject": "Proje özel modu için çalışma alanı klasörü bulunamadı",
|
||||
"rulesCleanupFailed": "Mod başarıyla silindi, ancak {{rulesFolderPath}} konumundaki kurallar klasörü silinemedi. Manuel olarak silmeniz gerekebilir."
|
||||
},
|
||||
"scope": {
|
||||
"project": "proje",
|
||||
"global": "küresel"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Mod başarıyla kaldırıldı, ancak {{rulesFolderPath}} konumundaki kurallar klasörü silinemedi. Manuel olarak silmeniz gerekebilir."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Kuruluşunuz Roo Code Cloud kimlik doğrulaması gerektiriyor. Devam etmek için giriş yapın.",
|
||||
|
|
|
|||
8
src/i18n/locales/vi/common.json
generated
8
src/i18n/locales/vi/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "Xóa chế độ tùy chỉnh thất bại: {{error}}",
|
||||
"resetFailed": "Đặt lại chế độ tùy chỉnh thất bại: {{error}}",
|
||||
"modeNotFound": "Lỗi ghi: Không tìm thấy chế độ",
|
||||
"noWorkspaceForProject": "Không tìm thấy thư mục workspace cho chế độ dành riêng cho dự án"
|
||||
"noWorkspaceForProject": "Không tìm thấy thư mục workspace cho chế độ dành riêng cho dự án",
|
||||
"rulesCleanupFailed": "Đã xóa chế độ thành công, nhưng không thể xóa thư mục quy tắc tại {{rulesFolderPath}}. Bạn có thể cần xóa thủ công."
|
||||
},
|
||||
"scope": {
|
||||
"project": "dự án",
|
||||
"global": "toàn cầu"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "Đã xóa chế độ thành công, nhưng không thể xóa thư mục quy tắc tại {{rulesFolderPath}}. Bạn có thể cần xóa thủ công."
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "Tổ chức của bạn yêu cầu xác thực Roo Code Cloud. Vui lòng đăng nhập để tiếp tục.",
|
||||
|
|
|
|||
8
src/i18n/locales/zh-CN/common.json
generated
8
src/i18n/locales/zh-CN/common.json
generated
|
|
@ -152,13 +152,19 @@
|
|||
"deleteFailed": "删除自定义模式失败:{{error}}",
|
||||
"resetFailed": "重置自定义模式失败:{{error}}",
|
||||
"modeNotFound": "写入错误:未找到模式",
|
||||
"noWorkspaceForProject": "未找到项目特定模式的工作区文件夹"
|
||||
"noWorkspaceForProject": "未找到项目特定模式的工作区文件夹",
|
||||
"rulesCleanupFailed": "模式删除成功,但无法删除位于 {{rulesFolderPath}} 的规则文件夹。您可能需要手动删除。"
|
||||
},
|
||||
"scope": {
|
||||
"project": "项目",
|
||||
"global": "全局"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "模式已成功移除,但无法删除位于 {{rulesFolderPath}} 的规则文件夹。您可能需要手动删除。"
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "您的组织需要 Roo Code Cloud 身份验证。请登录以继续。",
|
||||
|
|
|
|||
8
src/i18n/locales/zh-TW/common.json
generated
8
src/i18n/locales/zh-TW/common.json
generated
|
|
@ -147,13 +147,19 @@
|
|||
"deleteFailed": "刪除自訂模式失敗:{{error}}",
|
||||
"resetFailed": "重設自訂模式失敗:{{error}}",
|
||||
"modeNotFound": "寫入錯誤:未找到模式",
|
||||
"noWorkspaceForProject": "未找到專案特定模式的工作區資料夾"
|
||||
"noWorkspaceForProject": "未找到專案特定模式的工作區資料夾",
|
||||
"rulesCleanupFailed": "模式已成功刪除,但無法刪除位於 {{rulesFolderPath}} 的規則資料夾。您可能需要手動刪除。"
|
||||
},
|
||||
"scope": {
|
||||
"project": "專案",
|
||||
"global": "全域"
|
||||
}
|
||||
},
|
||||
"marketplace": {
|
||||
"mode": {
|
||||
"rulesCleanupFailed": "模式已成功移除,但無法刪除位於 {{rulesFolderPath}} 的規則資料夾。您可能需要手動刪除。"
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"errors": {
|
||||
"cloud_auth_required": "您的組織需要 Roo Code Cloud 身份驗證。請登入以繼續。",
|
||||
|
|
|
|||
|
|
@ -9,14 +9,18 @@ import { GlobalFileNames } from "../../shared/globalFileNames"
|
|||
import { ensureSettingsDirectoryExists } from "../../utils/globalContext"
|
||||
import { t } from "../../i18n"
|
||||
import { TelemetryService } from "@roo-code/telemetry"
|
||||
import type { CustomModesManager } from "../../core/config/CustomModesManager"
|
||||
|
||||
export class MarketplaceManager {
|
||||
private configLoader: RemoteConfigLoader
|
||||
private installer: SimpleInstaller
|
||||
|
||||
constructor(private readonly context: vscode.ExtensionContext) {
|
||||
constructor(
|
||||
private readonly context: vscode.ExtensionContext,
|
||||
private readonly customModesManager?: CustomModesManager,
|
||||
) {
|
||||
this.configLoader = new RemoteConfigLoader()
|
||||
this.installer = new SimpleInstaller(context)
|
||||
this.installer = new SimpleInstaller(context, customModesManager)
|
||||
}
|
||||
|
||||
async getMarketplaceItems(): Promise<{ items: MarketplaceItem[]; errors?: string[] }> {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import * as yaml from "yaml"
|
|||
import type { MarketplaceItem, MarketplaceItemType, InstallMarketplaceItemOptions, McpParameter } from "@roo-code/types"
|
||||
import { GlobalFileNames } from "../../shared/globalFileNames"
|
||||
import { ensureSettingsDirectoryExists } from "../../utils/globalContext"
|
||||
import type { CustomModesManager } from "../../core/config/CustomModesManager"
|
||||
|
||||
export interface InstallOptions extends InstallMarketplaceItemOptions {
|
||||
target: "project" | "global"
|
||||
|
|
@ -12,7 +13,10 @@ export interface InstallOptions extends InstallMarketplaceItemOptions {
|
|||
}
|
||||
|
||||
export class SimpleInstaller {
|
||||
constructor(private readonly context: vscode.ExtensionContext) {}
|
||||
constructor(
|
||||
private readonly context: vscode.ExtensionContext,
|
||||
private readonly customModesManager?: CustomModesManager,
|
||||
) {}
|
||||
|
||||
async installItem(item: MarketplaceItem, options: InstallOptions): Promise<{ filePath: string; line?: number }> {
|
||||
const { target } = options
|
||||
|
|
@ -40,6 +44,48 @@ export class SimpleInstaller {
|
|||
throw new Error("Mode content should not be an array")
|
||||
}
|
||||
|
||||
// If CustomModesManager is available, use importModeWithRules
|
||||
if (this.customModesManager) {
|
||||
// Transform marketplace content to import format (wrap in customModes array)
|
||||
const importData = {
|
||||
customModes: [yaml.parse(item.content)],
|
||||
}
|
||||
const importYaml = yaml.stringify(importData)
|
||||
|
||||
// Call customModesManager.importModeWithRules
|
||||
const result = await this.customModesManager.importModeWithRules(importYaml, target)
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "Failed to import mode")
|
||||
}
|
||||
|
||||
// Return the file path and line number for VS Code to open
|
||||
const filePath = await this.getModeFilePath(target)
|
||||
|
||||
// Try to find the line number where the mode was added
|
||||
let line: number | undefined
|
||||
try {
|
||||
const fileContent = await fs.readFile(filePath, "utf-8")
|
||||
const lines = fileContent.split("\n")
|
||||
const modeData = yaml.parse(item.content)
|
||||
|
||||
// Find the line containing the slug of the added mode
|
||||
if (modeData?.slug) {
|
||||
const slugLineIndex = lines.findIndex(
|
||||
(l) => l.includes(`slug: ${modeData.slug}`) || l.includes(`slug: "${modeData.slug}"`),
|
||||
)
|
||||
if (slugLineIndex >= 0) {
|
||||
line = slugLineIndex + 1 // Convert to 1-based line number
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// If we can't find the line number, that's okay
|
||||
}
|
||||
|
||||
return { filePath, line }
|
||||
}
|
||||
|
||||
// Fallback to original implementation if CustomModesManager is not available
|
||||
const filePath = await this.getModeFilePath(target)
|
||||
const modeData = yaml.parse(item.content)
|
||||
|
||||
|
|
@ -248,56 +294,38 @@ export class SimpleInstaller {
|
|||
}
|
||||
|
||||
private async removeMode(item: MarketplaceItem, target: "project" | "global"): Promise<void> {
|
||||
const filePath = await this.getModeFilePath(target)
|
||||
|
||||
try {
|
||||
const existing = await fs.readFile(filePath, "utf-8")
|
||||
let existingData: any
|
||||
|
||||
try {
|
||||
const parsed = yaml.parse(existing)
|
||||
// Ensure we have a valid object
|
||||
existingData = parsed && typeof parsed === "object" ? parsed : {}
|
||||
} catch (parseError) {
|
||||
// If we can't parse the file, we can't safely remove a mode
|
||||
const fileName = target === "project" ? ".roomodes" : "custom-modes.yaml"
|
||||
throw new Error(
|
||||
`Cannot remove mode: The ${fileName} file contains invalid YAML. ` +
|
||||
`Please fix the syntax errors before removing modes.`,
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure customModes array exists
|
||||
if (!existingData.customModes) {
|
||||
existingData.customModes = []
|
||||
}
|
||||
|
||||
// Parse the item content to get the slug
|
||||
let content: string
|
||||
if (Array.isArray(item.content)) {
|
||||
// Array of McpInstallationMethod objects - use first method
|
||||
content = item.content[0].content
|
||||
} else {
|
||||
content = item.content
|
||||
}
|
||||
const modeData = yaml.parse(content || "")
|
||||
|
||||
if (!modeData.slug) {
|
||||
return // Nothing to remove if no slug
|
||||
}
|
||||
|
||||
// Remove mode with matching slug
|
||||
existingData.customModes = existingData.customModes.filter((mode: any) => mode.slug !== modeData.slug)
|
||||
|
||||
// Always write back the file, even if empty
|
||||
await fs.writeFile(filePath, yaml.stringify(existingData, { lineWidth: 0 }), "utf-8")
|
||||
} catch (error: any) {
|
||||
if (error.code === "ENOENT") {
|
||||
// File doesn't exist, nothing to remove
|
||||
return
|
||||
}
|
||||
throw error
|
||||
if (!this.customModesManager) {
|
||||
throw new Error("CustomModesManager is not available")
|
||||
}
|
||||
|
||||
// Parse the item content to get the slug
|
||||
let content: string
|
||||
if (Array.isArray(item.content)) {
|
||||
// Array of McpInstallationMethod objects - use first method
|
||||
content = item.content[0].content
|
||||
} else {
|
||||
content = item.content || ""
|
||||
}
|
||||
|
||||
let modeSlug: string
|
||||
try {
|
||||
const modeData = yaml.parse(content)
|
||||
modeSlug = modeData.slug
|
||||
} catch (error) {
|
||||
throw new Error("Invalid mode content: unable to parse YAML")
|
||||
}
|
||||
|
||||
if (!modeSlug) {
|
||||
throw new Error("Mode missing slug identifier")
|
||||
}
|
||||
|
||||
// Get the current modes to determine the source
|
||||
const modes = await this.customModesManager.getCustomModes()
|
||||
const mode = modes.find((m) => m.slug === modeSlug)
|
||||
|
||||
// Use CustomModesManager to delete the mode configuration
|
||||
// This also handles rules folder deletion
|
||||
await this.customModesManager.deleteCustomMode(modeSlug, true)
|
||||
}
|
||||
|
||||
private async removeMcp(item: MarketplaceItem, target: "project" | "global"): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,19 @@ import { SimpleInstaller } from "../SimpleInstaller"
|
|||
import * as fs from "fs/promises"
|
||||
import * as yaml from "yaml"
|
||||
import * as vscode from "vscode"
|
||||
import * as os from "os"
|
||||
import type { MarketplaceItem } from "@roo-code/types"
|
||||
import type { CustomModesManager } from "../../../core/config/CustomModesManager"
|
||||
import * as path from "path"
|
||||
import { fileExistsAtPath } from "../../../utils/fs"
|
||||
|
||||
vi.mock("fs/promises")
|
||||
vi.mock("fs/promises", () => ({
|
||||
readFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
mkdir: vi.fn(),
|
||||
rm: vi.fn(),
|
||||
}))
|
||||
vi.mock("os")
|
||||
vi.mock("vscode", () => ({
|
||||
workspace: {
|
||||
workspaceFolders: [
|
||||
|
|
@ -20,20 +29,33 @@ vi.mock("vscode", () => ({
|
|||
},
|
||||
}))
|
||||
vi.mock("../../../utils/globalContext")
|
||||
vi.mock("../../../utils/fs")
|
||||
|
||||
const mockFs = fs as any
|
||||
const mockFs = vi.mocked(fs)
|
||||
|
||||
describe("SimpleInstaller", () => {
|
||||
let installer: SimpleInstaller
|
||||
let mockContext: vscode.ExtensionContext
|
||||
let mockCustomModesManager: CustomModesManager
|
||||
|
||||
beforeEach(() => {
|
||||
mockContext = {} as vscode.ExtensionContext
|
||||
installer = new SimpleInstaller(mockContext)
|
||||
mockCustomModesManager = {
|
||||
deleteCustomMode: vi.fn().mockResolvedValue(undefined),
|
||||
importModeWithRules: vi.fn().mockResolvedValue({ success: true }),
|
||||
getCustomModes: vi.fn().mockResolvedValue([]),
|
||||
} as any
|
||||
installer = new SimpleInstaller(mockContext, mockCustomModesManager)
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Mock mkdir to always succeed
|
||||
mockFs.mkdir.mockResolvedValue(undefined as any)
|
||||
// Mock rm to always succeed
|
||||
mockFs.rm.mockResolvedValue(undefined as any)
|
||||
// Mock os.homedir
|
||||
vi.mocked(os.homedir).mockReturnValue("/home/user")
|
||||
// Mock fileExistsAtPath to return false by default
|
||||
vi.mocked(fileExistsAtPath).mockResolvedValue(false)
|
||||
})
|
||||
|
||||
describe("installMode", () => {
|
||||
|
|
@ -50,128 +72,69 @@ describe("SimpleInstaller", () => {
|
|||
}),
|
||||
}
|
||||
|
||||
it("should install mode when .roomodes file does not exist", async () => {
|
||||
// Mock file not found error
|
||||
it("should install mode using CustomModesManager", async () => {
|
||||
// Mock file not found error for getModeFilePath
|
||||
const notFoundError = new Error("File not found") as any
|
||||
notFoundError.code = "ENOENT"
|
||||
mockFs.readFile.mockRejectedValueOnce(notFoundError)
|
||||
|
||||
const result = await installer.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(result.filePath).toBe(path.join("/test/workspace", ".roomodes"))
|
||||
expect(mockCustomModesManager.importModeWithRules).toHaveBeenCalled()
|
||||
|
||||
// Verify the import was called with correct YAML structure
|
||||
const importCall = (mockCustomModesManager.importModeWithRules as any).mock.calls[0]
|
||||
const importedYaml = importCall[0]
|
||||
const importedData = yaml.parse(importedYaml)
|
||||
expect(importedData.customModes).toHaveLength(1)
|
||||
expect(importedData.customModes[0].slug).toBe("test")
|
||||
})
|
||||
|
||||
it("should handle import failure from CustomModesManager", async () => {
|
||||
mockCustomModesManager.importModeWithRules = vi.fn().mockResolvedValue({
|
||||
success: false,
|
||||
error: "Import failed",
|
||||
})
|
||||
|
||||
await expect(installer.installItem(mockModeItem, { target: "project" })).rejects.toThrow("Import failed")
|
||||
})
|
||||
|
||||
it("should throw error for array content in mode", async () => {
|
||||
const arrayContentMode: MarketplaceItem = {
|
||||
...mockModeItem,
|
||||
content: ["content1", "content2"] as any,
|
||||
}
|
||||
|
||||
await expect(installer.installItem(arrayContentMode, { target: "project" })).rejects.toThrow(
|
||||
"Mode content should not be an array",
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw error for missing content", async () => {
|
||||
const noContentMode: MarketplaceItem = {
|
||||
...mockModeItem,
|
||||
content: undefined as any,
|
||||
}
|
||||
|
||||
await expect(installer.installItem(noContentMode, { target: "project" })).rejects.toThrow(
|
||||
"Mode item missing content",
|
||||
)
|
||||
})
|
||||
|
||||
it("should work without CustomModesManager (fallback)", async () => {
|
||||
const installerWithoutManager = new SimpleInstaller(mockContext)
|
||||
|
||||
// Mock file not found
|
||||
const notFoundError = new Error("File not found") as any
|
||||
notFoundError.code = "ENOENT"
|
||||
mockFs.readFile.mockRejectedValueOnce(notFoundError)
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
const result = await installer.installItem(mockModeItem, { target: "project" })
|
||||
const result = await installerWithoutManager.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(result.filePath).toBe(path.join("/test/workspace", ".roomodes"))
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
|
||||
// Verify the written content contains the new mode
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toHaveLength(1)
|
||||
expect(writtenData.customModes[0].slug).toBe("test")
|
||||
})
|
||||
|
||||
it("should install mode when .roomodes contains valid YAML", async () => {
|
||||
const existingContent = yaml.stringify({
|
||||
customModes: [{ slug: "existing", name: "Existing Mode", roleDefinition: "Existing", groups: [] }],
|
||||
})
|
||||
|
||||
mockFs.readFile.mockResolvedValueOnce(existingContent)
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
await installer.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
|
||||
// Should contain both existing and new mode
|
||||
expect(writtenData.customModes).toHaveLength(2)
|
||||
expect(writtenData.customModes.find((m: any) => m.slug === "existing")).toBeDefined()
|
||||
expect(writtenData.customModes.find((m: any) => m.slug === "test")).toBeDefined()
|
||||
})
|
||||
|
||||
it("should handle empty .roomodes file", async () => {
|
||||
// Empty file content
|
||||
mockFs.readFile.mockResolvedValueOnce("")
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
const result = await installer.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(result.filePath).toBe(path.join("/test/workspace", ".roomodes"))
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
|
||||
// Verify the written content contains the new mode
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toHaveLength(1)
|
||||
expect(writtenData.customModes[0].slug).toBe("test")
|
||||
})
|
||||
|
||||
it("should handle .roomodes file with null content", async () => {
|
||||
// File exists but yaml.parse returns null
|
||||
mockFs.readFile.mockResolvedValueOnce("---\n")
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
const result = await installer.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(result.filePath).toBe(path.join("/test/workspace", ".roomodes"))
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
|
||||
// Verify the written content contains the new mode
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toHaveLength(1)
|
||||
expect(writtenData.customModes[0].slug).toBe("test")
|
||||
})
|
||||
|
||||
it("should handle .roomodes file without customModes property", async () => {
|
||||
// File has valid YAML but no customModes property
|
||||
const contentWithoutCustomModes = yaml.stringify({ someOtherProperty: "value" })
|
||||
mockFs.readFile.mockResolvedValueOnce(contentWithoutCustomModes)
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
const result = await installer.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(result.filePath).toBe(path.join("/test/workspace", ".roomodes"))
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
|
||||
// Verify the written content contains the new mode and preserves other properties
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toHaveLength(1)
|
||||
expect(writtenData.customModes[0].slug).toBe("test")
|
||||
expect(writtenData.someOtherProperty).toBe("value")
|
||||
})
|
||||
|
||||
it("should throw error when .roomodes contains invalid YAML", async () => {
|
||||
const invalidYaml = "invalid: yaml: content: {"
|
||||
|
||||
mockFs.readFile.mockResolvedValueOnce(invalidYaml)
|
||||
|
||||
await expect(installer.installItem(mockModeItem, { target: "project" })).rejects.toThrow(
|
||||
"Cannot install mode: The .roomodes file contains invalid YAML",
|
||||
)
|
||||
|
||||
// Should NOT write to file
|
||||
expect(mockFs.writeFile).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should replace existing mode with same slug", async () => {
|
||||
const existingContent = yaml.stringify({
|
||||
customModes: [{ slug: "test", name: "Old Test Mode", roleDefinition: "Old role", groups: [] }],
|
||||
})
|
||||
|
||||
mockFs.readFile.mockResolvedValueOnce(existingContent)
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
await installer.installItem(mockModeItem, { target: "project" })
|
||||
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
|
||||
// Should contain only one mode with updated content
|
||||
expect(writtenData.customModes).toHaveLength(1)
|
||||
expect(writtenData.customModes[0].slug).toBe("test")
|
||||
expect(writtenData.customModes[0].name).toBe("Test Mode") // New name
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -254,75 +217,133 @@ describe("SimpleInstaller", () => {
|
|||
}),
|
||||
}
|
||||
|
||||
it("should throw error when .roomodes contains invalid YAML during removal", async () => {
|
||||
const invalidYaml = "invalid: yaml: content: {"
|
||||
it("should use CustomModesManager to delete mode and clean up rules folder", async () => {
|
||||
// Mock that the mode exists with project source
|
||||
vi.mocked(mockCustomModesManager.getCustomModes).mockResolvedValueOnce([
|
||||
{ slug: "test", name: "Test Mode", source: "project" } as any,
|
||||
])
|
||||
|
||||
mockFs.readFile.mockResolvedValueOnce(invalidYaml)
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
|
||||
await expect(installer.removeItem(mockModeItem, { target: "project" })).rejects.toThrow(
|
||||
"Cannot remove mode: The .roomodes file contains invalid YAML",
|
||||
// Should call deleteCustomMode with fromMarketplace flag set to true
|
||||
expect(mockCustomModesManager.deleteCustomMode).toHaveBeenCalledWith("test", true)
|
||||
// The rules folder deletion is now handled by CustomModesManager, not SimpleInstaller
|
||||
expect(fileExistsAtPath).not.toHaveBeenCalled()
|
||||
expect(mockFs.rm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should handle global mode removal with rules cleanup", async () => {
|
||||
// Mock that the mode exists with global source
|
||||
vi.mocked(mockCustomModesManager.getCustomModes).mockResolvedValueOnce([
|
||||
{ slug: "test", name: "Test Mode", source: "global" } as any,
|
||||
])
|
||||
|
||||
await installer.removeItem(mockModeItem, { target: "global" })
|
||||
|
||||
// Should call deleteCustomMode with fromMarketplace flag set to true
|
||||
expect(mockCustomModesManager.deleteCustomMode).toHaveBeenCalledWith("test", true)
|
||||
// The rules folder deletion is now handled by CustomModesManager, not SimpleInstaller
|
||||
expect(fileExistsAtPath).not.toHaveBeenCalled()
|
||||
expect(mockFs.rm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should handle case when rules folder does not exist", async () => {
|
||||
// Mock that the mode exists
|
||||
vi.mocked(mockCustomModesManager.getCustomModes).mockResolvedValueOnce([
|
||||
{ slug: "test", name: "Test Mode", source: "project" } as any,
|
||||
])
|
||||
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
|
||||
// Should call deleteCustomMode with fromMarketplace flag set to true
|
||||
expect(mockCustomModesManager.deleteCustomMode).toHaveBeenCalledWith("test", true)
|
||||
// The rules folder deletion is now handled by CustomModesManager, not SimpleInstaller
|
||||
expect(fileExistsAtPath).not.toHaveBeenCalled()
|
||||
expect(mockFs.rm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should throw error if deleteCustomMode fails", async () => {
|
||||
// Mock that the mode exists
|
||||
vi.mocked(mockCustomModesManager.getCustomModes).mockResolvedValueOnce([
|
||||
{ slug: "test", name: "Test Mode", source: "project" } as any,
|
||||
])
|
||||
// Mock that deleteCustomMode fails
|
||||
mockCustomModesManager.deleteCustomMode = vi.fn().mockRejectedValueOnce(new Error("Permission denied"))
|
||||
|
||||
// Should throw the error from deleteCustomMode
|
||||
await expect(installer.removeItem(mockModeItem, { target: "project" })).rejects.toThrow("Permission denied")
|
||||
|
||||
expect(mockCustomModesManager.deleteCustomMode).toHaveBeenCalledWith("test", true)
|
||||
})
|
||||
|
||||
it("should handle mode not found in custom modes list", async () => {
|
||||
// Mock that the mode doesn't exist in the list
|
||||
vi.mocked(mockCustomModesManager.getCustomModes).mockResolvedValueOnce([])
|
||||
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
|
||||
expect(mockCustomModesManager.deleteCustomMode).toHaveBeenCalledWith("test", true)
|
||||
// Should not attempt to delete rules folder
|
||||
expect(fileExistsAtPath).not.toHaveBeenCalled()
|
||||
expect(mockFs.rm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should throw error when mode content is invalid YAML", async () => {
|
||||
const invalidModeItem: MarketplaceItem = {
|
||||
...mockModeItem,
|
||||
content: "invalid: yaml: content: {",
|
||||
}
|
||||
|
||||
await expect(installer.removeItem(invalidModeItem, { target: "project" })).rejects.toThrow(
|
||||
"Invalid mode content: unable to parse YAML",
|
||||
)
|
||||
|
||||
// Should NOT write to file
|
||||
expect(mockFs.writeFile).not.toHaveBeenCalled()
|
||||
expect(mockCustomModesManager.deleteCustomMode).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should do nothing when file does not exist", async () => {
|
||||
const notFoundError = new Error("File not found") as any
|
||||
notFoundError.code = "ENOENT"
|
||||
mockFs.readFile.mockRejectedValueOnce(notFoundError)
|
||||
it("should throw error when mode has no slug", async () => {
|
||||
const noSlugModeItem: MarketplaceItem = {
|
||||
...mockModeItem,
|
||||
content: yaml.stringify({
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role",
|
||||
groups: ["read"],
|
||||
}),
|
||||
}
|
||||
|
||||
// Should not throw
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
await expect(installer.removeItem(noSlugModeItem, { target: "project" })).rejects.toThrow(
|
||||
"Mode missing slug identifier",
|
||||
)
|
||||
|
||||
expect(mockFs.writeFile).not.toHaveBeenCalled()
|
||||
expect(mockCustomModesManager.deleteCustomMode).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should handle empty .roomodes file during removal", async () => {
|
||||
// Empty file content
|
||||
mockFs.readFile.mockResolvedValueOnce("")
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
it("should handle array content format", async () => {
|
||||
const arrayContentItem: MarketplaceItem = {
|
||||
...mockModeItem,
|
||||
content: [
|
||||
{
|
||||
content: yaml.stringify({
|
||||
slug: "test-array",
|
||||
name: "Test Array Mode",
|
||||
roleDefinition: "Test role",
|
||||
groups: ["read"],
|
||||
}),
|
||||
},
|
||||
] as any,
|
||||
}
|
||||
|
||||
// Should not throw
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
await installer.removeItem(arrayContentItem, { target: "project" })
|
||||
|
||||
// Should write back a valid structure with empty customModes
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toEqual([])
|
||||
expect(mockCustomModesManager.deleteCustomMode).toHaveBeenCalledWith("test-array", true)
|
||||
})
|
||||
|
||||
it("should handle .roomodes file with null content during removal", async () => {
|
||||
// File exists but yaml.parse returns null
|
||||
mockFs.readFile.mockResolvedValueOnce("---\n")
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
it("should throw error when CustomModesManager is not available", async () => {
|
||||
const installerWithoutManager = new SimpleInstaller(mockContext)
|
||||
|
||||
// Should not throw
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
|
||||
// Should write back a valid structure with empty customModes
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toEqual([])
|
||||
})
|
||||
|
||||
it("should handle .roomodes file without customModes property during removal", async () => {
|
||||
// File has valid YAML but no customModes property
|
||||
const contentWithoutCustomModes = yaml.stringify({ someOtherProperty: "value" })
|
||||
mockFs.readFile.mockResolvedValueOnce(contentWithoutCustomModes)
|
||||
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
|
||||
|
||||
// Should not throw
|
||||
await installer.removeItem(mockModeItem, { target: "project" })
|
||||
|
||||
// Should write back the file with the same content (no modes to remove)
|
||||
expect(mockFs.writeFile).toHaveBeenCalled()
|
||||
const writtenContent = mockFs.writeFile.mock.calls[0][1] as string
|
||||
const writtenData = yaml.parse(writtenContent)
|
||||
expect(writtenData.customModes).toEqual([])
|
||||
expect(writtenData.someOtherProperty).toBe("value")
|
||||
await expect(installerWithoutManager.removeItem(mockModeItem, { target: "project" })).rejects.toThrow(
|
||||
"CustomModesManager is not available",
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ export interface ExtensionMessage {
|
|||
| "indexCleared"
|
||||
| "codebaseIndexConfig"
|
||||
| "marketplaceInstallResult"
|
||||
| "marketplaceRemoveResult"
|
||||
| "marketplaceData"
|
||||
| "shareTaskSuccess"
|
||||
| "codeIndexSettingsSaved"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useMemo, useState } from "react"
|
||||
import React, { useMemo, useState, useEffect } from "react"
|
||||
import { MarketplaceItem, TelemetryEventName } from "@roo-code/types"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { telemetryClient } from "@/utils/TelemetryClient"
|
||||
|
|
@ -10,6 +10,16 @@ import { Button } from "@/components/ui/button"
|
|||
import { StandardTooltip } from "@/components/ui"
|
||||
import { MarketplaceInstallModal } from "./MarketplaceInstallModal"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui"
|
||||
|
||||
interface ItemInstalledMetadata {
|
||||
type: string
|
||||
|
|
@ -29,6 +39,30 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
|
|||
const { t } = useAppTranslation()
|
||||
const { cwd } = useExtensionState()
|
||||
const [showInstallModal, setShowInstallModal] = useState(false)
|
||||
const [showRemoveConfirm, setShowRemoveConfirm] = useState(false)
|
||||
const [removeTarget, setRemoveTarget] = useState<"project" | "global">("project")
|
||||
const [removeError, setRemoveError] = useState<string | null>(null)
|
||||
|
||||
// Listen for removal result messages
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const message = event.data
|
||||
if (message.type === "marketplaceRemoveResult" && message.slug === item.id) {
|
||||
if (message.success) {
|
||||
// Removal succeeded - refresh marketplace data
|
||||
vscode.postMessage({
|
||||
type: "fetchMarketplaceData",
|
||||
})
|
||||
} else {
|
||||
// Removal failed - show error message to user
|
||||
setRemoveError(message.error || t("marketplace:items.unknownError"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleMessage)
|
||||
return () => window.removeEventListener("message", handleMessage)
|
||||
}, [item.id, t])
|
||||
|
||||
const typeLabel = useMemo(() => {
|
||||
const labels: Partial<Record<MarketplaceItem["type"], string>> = {
|
||||
|
|
@ -92,16 +126,8 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
|
|||
onClick={() => {
|
||||
// Determine which installation to remove (prefer project over global)
|
||||
const target = isInstalledInProject ? "project" : "global"
|
||||
vscode.postMessage({
|
||||
type: "removeInstalledMarketplaceItem",
|
||||
mpItem: item,
|
||||
mpInstallOptions: { target },
|
||||
})
|
||||
|
||||
// Request fresh marketplace data to update installed status
|
||||
vscode.postMessage({
|
||||
type: "fetchMarketplaceData",
|
||||
})
|
||||
setRemoveTarget(target)
|
||||
setShowRemoveConfirm(true)
|
||||
}}>
|
||||
{t("marketplace:items.card.remove")}
|
||||
</Button>
|
||||
|
|
@ -116,6 +142,13 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
|
|||
{t("marketplace:items.card.install")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Error message display */}
|
||||
{removeError && (
|
||||
<div className="text-vscode-errorForeground text-sm mt-2">
|
||||
{t("marketplace:items.removeFailed", { error: removeError })}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -169,6 +202,49 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
|
|||
onClose={() => setShowInstallModal(false)}
|
||||
hasWorkspace={!!cwd}
|
||||
/>
|
||||
|
||||
{/* Remove Confirmation Dialog */}
|
||||
<AlertDialog open={showRemoveConfirm} onOpenChange={setShowRemoveConfirm}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{item.type === "mode"
|
||||
? t("marketplace:removeConfirm.mode.title")
|
||||
: t("marketplace:removeConfirm.mcp.title")}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{item.type === "mode" ? (
|
||||
<>
|
||||
{t("marketplace:removeConfirm.mode.message", { modeName: item.name })}
|
||||
<div className="mt-2 text-sm">
|
||||
{t("marketplace:removeConfirm.mode.rulesWarning")}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
t("marketplace:removeConfirm.mcp.message", { mcpName: item.name })
|
||||
)}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{t("marketplace:removeConfirm.cancel")}</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
// Clear any previous error
|
||||
setRemoveError(null)
|
||||
|
||||
vscode.postMessage({
|
||||
type: "removeInstalledMarketplaceItem",
|
||||
mpItem: item,
|
||||
mpInstallOptions: { target: removeTarget },
|
||||
})
|
||||
|
||||
setShowRemoveConfirm(false)
|
||||
}}>
|
||||
{t("marketplace:removeConfirm.confirm")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
21
webview-ui/src/i18n/locales/ca/marketplace.json
generated
21
webview-ui/src/i18n/locales/ca/marketplace.json
generated
|
|
@ -70,8 +70,12 @@
|
|||
"installed": "Instal·lat",
|
||||
"removeProjectTooltip": "Eliminar del projecte actual",
|
||||
"removeGlobalTooltip": "Eliminar de la configuració global",
|
||||
"actionsMenuLabel": "Més accions"
|
||||
}
|
||||
"actionsMenuLabel": "Més accions",
|
||||
"official": "Oficial",
|
||||
"verified": "Verificat"
|
||||
},
|
||||
"removeFailed": "No s'ha pogut eliminar l'element: {{error}}",
|
||||
"unknownError": "S'ha produït un error desconegut"
|
||||
},
|
||||
"install": {
|
||||
"title": "Instal·lar {{name}}",
|
||||
|
|
@ -125,6 +129,19 @@
|
|||
"maxSources": "Màxim de {{max}} fonts permeses"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Eliminar el mode",
|
||||
"message": "Estàs segur que vols eliminar el mode \"{{modeName}}\"?",
|
||||
"rulesWarning": "Això també eliminarà qualsevol fitxer de regles associat per a aquest mode."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Eliminar el servidor MCP",
|
||||
"message": "Estàs segur que vols eliminar el servidor MCP \"{{mcpName}}\"?"
|
||||
},
|
||||
"cancel": "Cancel·lar",
|
||||
"confirm": "Eliminar"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Has trobat un problema amb un element del marketplace o tens suggeriments per a nous elements? <0>Obre una incidència de GitHub</0> per fer-nos-ho saber!"
|
||||
}
|
||||
|
|
|
|||
21
webview-ui/src/i18n/locales/de/marketplace.json
generated
21
webview-ui/src/i18n/locales/de/marketplace.json
generated
|
|
@ -70,8 +70,12 @@
|
|||
"installed": "Installiert",
|
||||
"removeProjectTooltip": "Aus aktuellem Projekt entfernen",
|
||||
"removeGlobalTooltip": "Aus globaler Konfiguration entfernen",
|
||||
"actionsMenuLabel": "Weitere Aktionen"
|
||||
}
|
||||
"actionsMenuLabel": "Weitere Aktionen",
|
||||
"official": "Offiziell",
|
||||
"verified": "Verifiziert"
|
||||
},
|
||||
"removeFailed": "Element konnte nicht entfernt werden: {{error}}",
|
||||
"unknownError": "Unbekannter Fehler aufgetreten"
|
||||
},
|
||||
"install": {
|
||||
"title": "{{name}} installieren",
|
||||
|
|
@ -125,6 +129,19 @@
|
|||
"maxSources": "Maximal {{max}} Quellen erlaubt"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Modus entfernen",
|
||||
"message": "Bist du sicher, dass du den Modus „{{modeName}}“ entfernen möchtest?",
|
||||
"rulesWarning": "Dadurch werden auch alle zugehörigen Regeldateien für diesen Modus entfernt."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP-Server entfernen",
|
||||
"message": "Bist du sicher, dass du den MCP-Server „{{mcpName}}“ entfernen möchtest?"
|
||||
},
|
||||
"cancel": "Abbrechen",
|
||||
"confirm": "Entfernen"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Problem mit einem Marketplace-Element gefunden oder Vorschläge für neue? <0>Öffne ein GitHub-Issue</0>, um es uns mitzuteilen!"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Remove from current project",
|
||||
"removeGlobalTooltip": "Remove from global configuration",
|
||||
"actionsMenuLabel": "More actions"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Failed to remove item: {{error}}",
|
||||
"unknownError": "Unknown error occurred"
|
||||
},
|
||||
"install": {
|
||||
"title": "Install {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Maximum of {{max}} sources allowed"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Remove Mode",
|
||||
"message": "Are you sure you want to remove the mode \"{{modeName}}\"?",
|
||||
"rulesWarning": "This will also remove any associated rules files for this mode."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Remove MCP Server",
|
||||
"message": "Are you sure you want to remove the MCP server \"{{mcpName}}\"?"
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Remove"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Found a problem with a marketplace item or have suggestions for new ones? <0>Open a GitHub issue</0> to let us know!"
|
||||
}
|
||||
|
|
|
|||
21
webview-ui/src/i18n/locales/es/marketplace.json
generated
21
webview-ui/src/i18n/locales/es/marketplace.json
generated
|
|
@ -70,8 +70,12 @@
|
|||
"installed": "Instalado",
|
||||
"removeProjectTooltip": "Eliminar del proyecto actual",
|
||||
"removeGlobalTooltip": "Eliminar de la configuración global",
|
||||
"actionsMenuLabel": "Más acciones"
|
||||
}
|
||||
"actionsMenuLabel": "Más acciones",
|
||||
"official": "Oficial",
|
||||
"verified": "Verificado"
|
||||
},
|
||||
"removeFailed": "No se pudo eliminar el elemento: {{error}}",
|
||||
"unknownError": "Ocurrió un error desconocido"
|
||||
},
|
||||
"install": {
|
||||
"title": "Instalar {{name}}",
|
||||
|
|
@ -125,6 +129,19 @@
|
|||
"maxSources": "Máximo de {{max}} fuentes permitidas"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Eliminar modo",
|
||||
"message": "¿Estás seguro de que quieres eliminar el modo \"{{modeName}}\"?",
|
||||
"rulesWarning": "Esto también eliminará cualquier archivo de reglas asociado para este modo."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Eliminar servidor MCP",
|
||||
"message": "¿Estás seguro de que quieres eliminar el servidor MCP \"{{mcpName}}\"?"
|
||||
},
|
||||
"cancel": "Cancelar",
|
||||
"confirm": "Eliminar"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "¿Encontraste un problema con un elemento del marketplace o tienes sugerencias para nuevos? ¡<0>Abre un issue en GitHub</0> para hacérnoslo saber!"
|
||||
}
|
||||
|
|
|
|||
23
webview-ui/src/i18n/locales/fr/marketplace.json
generated
23
webview-ui/src/i18n/locales/fr/marketplace.json
generated
|
|
@ -27,7 +27,7 @@
|
|||
},
|
||||
"tags": {
|
||||
"label": "Filtrer par étiquettes :",
|
||||
"clear": "Effacer les étiquettes",
|
||||
"clear": "Cliquez pour supprimer '{{count}}' des filtres",
|
||||
"placeholder": "Tapez pour rechercher et sélectionner des étiquettes...",
|
||||
"noResults": "Aucune étiquette correspondante trouvée",
|
||||
"selected": "Affichage des éléments avec l'une des étiquettes sélectionnées",
|
||||
|
|
@ -70,8 +70,12 @@
|
|||
"installed": "Installé",
|
||||
"removeProjectTooltip": "Supprimer du projet actuel",
|
||||
"removeGlobalTooltip": "Supprimer de la configuration globale",
|
||||
"actionsMenuLabel": "Plus d'actions"
|
||||
}
|
||||
"actionsMenuLabel": "Plus d'actions",
|
||||
"official": "Officiel",
|
||||
"verified": "Vérifié"
|
||||
},
|
||||
"removeFailed": "Échec de la suppression de l'élément : {{error}}",
|
||||
"unknownError": "Une erreur inconnue est survenue"
|
||||
},
|
||||
"install": {
|
||||
"title": "Installer {{name}}",
|
||||
|
|
@ -125,6 +129,19 @@
|
|||
"maxSources": "Maximum de {{max}} sources autorisées"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Supprimer le mode",
|
||||
"message": "Êtes-vous sûr de vouloir supprimer le mode « {{modeName}} » ?",
|
||||
"rulesWarning": "Cela supprimera également tous les fichiers de règles associés à ce mode."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Supprimer le serveur MCP",
|
||||
"message": "Êtes-vous sûr de vouloir supprimer le serveur MCP « {{mcpName}} » ?"
|
||||
},
|
||||
"cancel": "Annuler",
|
||||
"confirm": "Supprimer"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Vous avez trouvé un problème avec un élément du marketplace ou avez des suggestions pour de nouveaux éléments ? <0>Ouvrez une issue GitHub</0> pour nous le faire savoir !"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/hi/marketplace.json
generated
17
webview-ui/src/i18n/locales/hi/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "वर्तमान प्रोजेक्ट से हटाएं",
|
||||
"removeGlobalTooltip": "ग्लोबल कॉन्फ़िगरेशन से हटाएं",
|
||||
"actionsMenuLabel": "अधिक क्रियाएं"
|
||||
}
|
||||
},
|
||||
"removeFailed": "आइटम हटाने में विफल: {{error}}",
|
||||
"unknownError": "अज्ञात त्रुटि हुई"
|
||||
},
|
||||
"install": {
|
||||
"title": "{{name}} इंस्टॉल करें",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "अधिकतम {{max}} स्रोतों की अनुमति है"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "मोड हटाएं",
|
||||
"message": "क्या आप वाकई मोड \"{{modeName}}\" को हटाना चाहते हैं?",
|
||||
"rulesWarning": "यह इस मोड के लिए किसी भी संबंधित नियम फ़ाइल को भी हटा देगा।"
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP सर्वर हटाएं",
|
||||
"message": "क्या आप वाकई MCP सर्वर \"{{mcpName}}\" को हटाना चाहते हैं?"
|
||||
},
|
||||
"cancel": "रद्द करें",
|
||||
"confirm": "हटाएं"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "कोई marketplace आइटम के साथ समस्या है या नए आइटम के लिए सुझाव हैं? <0>GitHub issue खोलें</0> हमें बताने के लिए!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/id/marketplace.json
generated
17
webview-ui/src/i18n/locales/id/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Hapus dari proyek saat ini",
|
||||
"removeGlobalTooltip": "Hapus dari konfigurasi global",
|
||||
"actionsMenuLabel": "Aksi lainnya"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Gagal menghapus item: {{error}}",
|
||||
"unknownError": "Terjadi kesalahan yang tidak diketahui"
|
||||
},
|
||||
"install": {
|
||||
"title": "Instal {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Maksimal {{max}} sumber diizinkan"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Hapus Mode",
|
||||
"message": "Apakah Anda yakin ingin menghapus mode \"{{modeName}}\"?",
|
||||
"rulesWarning": "Ini juga akan menghapus semua file aturan terkait untuk mode ini."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Hapus Server MCP",
|
||||
"message": "Apakah Anda yakin ingin menghapus server MCP \"{{mcpName}}\"?"
|
||||
},
|
||||
"cancel": "Batal",
|
||||
"confirm": "Hapus"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Menemukan masalah dengan item marketplace atau punya saran untuk yang baru? <0>Buka GitHub issue</0> untuk memberi tahu kami!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/it/marketplace.json
generated
17
webview-ui/src/i18n/locales/it/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Rimuovi dal progetto corrente",
|
||||
"removeGlobalTooltip": "Rimuovi dalla configurazione globale",
|
||||
"actionsMenuLabel": "Altre azioni"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Impossibile rimuovere l'elemento: {{error}}",
|
||||
"unknownError": "Si è verificato un errore sconosciuto"
|
||||
},
|
||||
"install": {
|
||||
"title": "Installa {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Massimo {{max}} fonti consentite"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Rimuovi modalità",
|
||||
"message": "Sei sicuro di voler rimuovere la modalità \"{{modeName}}\"?",
|
||||
"rulesWarning": "Questo rimuoverà anche eventuali file di regole associati per questa modalità."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Rimuovi server MCP",
|
||||
"message": "Sei sicuro di voler rimuovere il server MCP \"{{mcpName}}\"?"
|
||||
},
|
||||
"cancel": "Annulla",
|
||||
"confirm": "Rimuovi"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Hai trovato un problema con un elemento del marketplace o hai suggerimenti per nuovi elementi? <0>Apri un issue GitHub</0> per farcelo sapere!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/ja/marketplace.json
generated
17
webview-ui/src/i18n/locales/ja/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "現在のプロジェクトから削除",
|
||||
"removeGlobalTooltip": "グローバル設定から削除",
|
||||
"actionsMenuLabel": "その他のアクション"
|
||||
}
|
||||
},
|
||||
"removeFailed": "アイテムの削除に失敗しました: {{error}}",
|
||||
"unknownError": "不明なエラーが発生しました"
|
||||
},
|
||||
"install": {
|
||||
"title": "{{name}}をインストール",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "最大{{max}}個のソースが許可されています"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "モードを削除",
|
||||
"message": "モード「{{modeName}}」を本当に削除しますか?",
|
||||
"rulesWarning": "これにより、このモードに関連するルールファイルも削除されます。"
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCPサーバーを削除",
|
||||
"message": "MCPサーバー「{{mcpName}}」を本当に削除しますか?"
|
||||
},
|
||||
"cancel": "キャンセル",
|
||||
"confirm": "削除"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Marketplaceアイテムで問題を見つけた、または新しいアイテムの提案がありますか?<0>GitHub issueを開いて</0>お知らせください!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/ko/marketplace.json
generated
17
webview-ui/src/i18n/locales/ko/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "현재 프로젝트에서 삭제",
|
||||
"removeGlobalTooltip": "전역 설정에서 삭제",
|
||||
"actionsMenuLabel": "추가 작업"
|
||||
}
|
||||
},
|
||||
"removeFailed": "항목을 제거하지 못했습니다: {{error}}",
|
||||
"unknownError": "알 수 없는 오류가 발생했습니다"
|
||||
},
|
||||
"install": {
|
||||
"title": "{{name}} 설치",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "최대 {{max}}개의 소스가 허용됩니다"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "모드 제거",
|
||||
"message": "정말로 '{{modeName}}' 모드를 제거하시겠습니까?",
|
||||
"rulesWarning": "이렇게 하면 이 모드와 관련된 모든 규칙 파일도 제거됩니다."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP 서버 제거",
|
||||
"message": "정말로 '{{mcpName}}' MCP 서버를 제거하시겠습니까?"
|
||||
},
|
||||
"cancel": "취소",
|
||||
"confirm": "제거"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Marketplace 아이템에 문제가 있거나 새로운 아이템에 대한 제안이 있나요? <0>GitHub issue를 열어서</0> 알려주세요!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/nl/marketplace.json
generated
17
webview-ui/src/i18n/locales/nl/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Verwijderen uit huidig project",
|
||||
"removeGlobalTooltip": "Verwijderen uit globale configuratie",
|
||||
"actionsMenuLabel": "Meer acties"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Item verwijderen mislukt: {{error}}",
|
||||
"unknownError": "Onbekende fout opgetreden"
|
||||
},
|
||||
"install": {
|
||||
"title": "{{name}} installeren",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Maximaal {{max}} bronnen toegestaan"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Modus verwijderen",
|
||||
"message": "Weet je zeker dat je de modus \"{{modeName}}\" wilt verwijderen?",
|
||||
"rulesWarning": "Dit verwijdert ook alle bijbehorende regelbestanden voor deze modus."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP-server verwijderen",
|
||||
"message": "Weet je zeker dat je de MCP-server \"{{mcpName}}\" wilt verwijderen?"
|
||||
},
|
||||
"cancel": "Annuleren",
|
||||
"confirm": "Verwijderen"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Heb je een probleem gevonden met een marketplace-item of heb je suggesties voor nieuwe items? <0>Open een GitHub issue</0> om het ons te laten weten!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/pl/marketplace.json
generated
17
webview-ui/src/i18n/locales/pl/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Usuń z bieżącego projektu",
|
||||
"removeGlobalTooltip": "Usuń z konfiguracji globalnej",
|
||||
"actionsMenuLabel": "Więcej akcji"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Nie udało się usunąć elementu: {{error}}",
|
||||
"unknownError": "Wystąpił nieznany błąd"
|
||||
},
|
||||
"install": {
|
||||
"title": "Zainstaluj {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Maksymalnie {{max}} źródeł dozwolonych"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Usuń tryb",
|
||||
"message": "Czy na pewno chcesz usunąć tryb „{{modeName}}”?",
|
||||
"rulesWarning": "Spowoduje to również usunięcie wszelkich powiązanych plików reguł dla tego trybu."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Usuń serwer MCP",
|
||||
"message": "Czy na pewno chcesz usunąć serwer MCP „{{mcpName}}”?"
|
||||
},
|
||||
"cancel": "Anuluj",
|
||||
"confirm": "Usuń"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Znalazłeś problem z elementem marketplace lub masz sugestie dotyczące nowych elementów? <0>Otwórz issue na GitHub</0>, aby nam o tym powiedzieć!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/pt-BR/marketplace.json
generated
17
webview-ui/src/i18n/locales/pt-BR/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Remover do projeto atual",
|
||||
"removeGlobalTooltip": "Remover da configuração global",
|
||||
"actionsMenuLabel": "Mais ações"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Falha ao remover o item: {{error}}",
|
||||
"unknownError": "Ocorreu um erro desconhecido"
|
||||
},
|
||||
"install": {
|
||||
"title": "Instalar {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Máximo de {{max}} fontes permitidas"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Remover Modo",
|
||||
"message": "Tem certeza de que deseja remover o modo \"{{modeName}}\"?",
|
||||
"rulesWarning": "Isso também removerá todos os arquivos de regras associados a este modo."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Remover Servidor MCP",
|
||||
"message": "Tem certeza de que deseja remover o servidor MCP \"{{mcpName}}\"?"
|
||||
},
|
||||
"cancel": "Cancelar",
|
||||
"confirm": "Remover"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Encontrou um problema com um item do marketplace ou tem sugestões para novos itens? <0>Abra um issue no GitHub</0> para nos avisar!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/ru/marketplace.json
generated
17
webview-ui/src/i18n/locales/ru/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Удалить из текущего проекта",
|
||||
"removeGlobalTooltip": "Удалить из глобальной конфигурации",
|
||||
"actionsMenuLabel": "Дополнительные действия"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Не удалось удалить элемент: {{error}}",
|
||||
"unknownError": "Произошла неизвестная ошибка"
|
||||
},
|
||||
"install": {
|
||||
"title": "Установить {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Максимум {{max}} источников разрешено"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Удалить режим",
|
||||
"message": "Вы уверены, что хотите удалить режим «{{modeName}}»?",
|
||||
"rulesWarning": "Это также удалит все связанные файлы правил для этого режима."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Удалить сервер MCP",
|
||||
"message": "Вы уверены, что хотите удалить сервер MCP «{{mcpName}}»?"
|
||||
},
|
||||
"cancel": "Отмена",
|
||||
"confirm": "Удалить"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Нашли проблему с элементом marketplace или есть предложения для новых элементов? <0>Откройте issue на GitHub</0>, чтобы сообщить нам!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/tr/marketplace.json
generated
17
webview-ui/src/i18n/locales/tr/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Mevcut projeden kaldır",
|
||||
"removeGlobalTooltip": "Global yapılandırmadan kaldır",
|
||||
"actionsMenuLabel": "Daha fazla eylem"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Öğe kaldırılamadı: {{error}}",
|
||||
"unknownError": "Bilinmeyen bir hata oluştu"
|
||||
},
|
||||
"install": {
|
||||
"title": "{{name}} Yükle",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Maksimum {{max}} kaynak izin verilir"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Modu Kaldır",
|
||||
"message": "\"{{modeName}}\" modunu kaldırmak istediğinizden emin misiniz?",
|
||||
"rulesWarning": "Bu, bu mod için ilişkili tüm kural dosyalarını da kaldıracaktır."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP Sunucusunu Kaldır",
|
||||
"message": "\"{{mcpName}}\" MCP sunucusunu kaldırmak istediğinizden emin misiniz?"
|
||||
},
|
||||
"cancel": "İptal",
|
||||
"confirm": "Kaldır"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Bir marketplace öğesi ile ilgili sorun bulduğun veya yeni öğeler için önerilerin var mı? <0>GitHub'da issue aç</0> ve bize bildir!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/vi/marketplace.json
generated
17
webview-ui/src/i18n/locales/vi/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "Xóa khỏi dự án hiện tại",
|
||||
"removeGlobalTooltip": "Xóa khỏi cấu hình toàn cục",
|
||||
"actionsMenuLabel": "Thêm hành động"
|
||||
}
|
||||
},
|
||||
"removeFailed": "Không thể xóa mục: {{error}}",
|
||||
"unknownError": "Đã xảy ra lỗi không xác định"
|
||||
},
|
||||
"install": {
|
||||
"title": "Cài đặt {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "Tối đa {{max}} nguồn được phép"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "Xóa chế độ",
|
||||
"message": "Bạn có chắc chắn muốn xóa chế độ \"{{modeName}}\" không?",
|
||||
"rulesWarning": "Thao tác này cũng sẽ xóa mọi tệp quy tắc được liên kết cho chế độ này."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "Xóa máy chủ MCP",
|
||||
"message": "Bạn có chắc chắn muốn xóa máy chủ MCP \"{{mcpName}}\" không?"
|
||||
},
|
||||
"cancel": "Hủy",
|
||||
"confirm": "Xóa"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "Bạn tìm thấy vấn đề với mục marketplace hoặc có đề xuất cho mục mới? <0>Mở issue GitHub</0> để cho chúng tôi biết!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/zh-CN/marketplace.json
generated
17
webview-ui/src/i18n/locales/zh-CN/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "从当前项目中移除",
|
||||
"removeGlobalTooltip": "从全局配置中移除",
|
||||
"actionsMenuLabel": "更多操作"
|
||||
}
|
||||
},
|
||||
"removeFailed": "删除失败: {{error}}",
|
||||
"unknownError": "发生未知错误"
|
||||
},
|
||||
"install": {
|
||||
"title": "安装 {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "最多允许 {{max}} 个源"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "删除模式",
|
||||
"message": "您确定要删除\"{{modeName}}\"模式吗?",
|
||||
"rulesWarning": "这也将删除此模式的任何关联规则文件。"
|
||||
},
|
||||
"mcp": {
|
||||
"title": "删除 MCP 服务器",
|
||||
"message": "您确定要删除 MCP 服务器\"{{mcpName}}\"吗?"
|
||||
},
|
||||
"cancel": "取消",
|
||||
"confirm": "删除"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "发现 marketplace 项目问题或有新项目建议?<0>在 GitHub 开启 issue</0> 告诉我们!"
|
||||
}
|
||||
|
|
|
|||
17
webview-ui/src/i18n/locales/zh-TW/marketplace.json
generated
17
webview-ui/src/i18n/locales/zh-TW/marketplace.json
generated
|
|
@ -71,7 +71,9 @@
|
|||
"removeProjectTooltip": "從目前專案中移除",
|
||||
"removeGlobalTooltip": "從全域設定中移除",
|
||||
"actionsMenuLabel": "更多動作"
|
||||
}
|
||||
},
|
||||
"removeFailed": "移除物品失敗: {{error}}",
|
||||
"unknownError": "發生未知錯誤"
|
||||
},
|
||||
"install": {
|
||||
"title": "安裝 {{name}}",
|
||||
|
|
@ -125,6 +127,19 @@
|
|||
"maxSources": "最多允許 {{max}} 個來源"
|
||||
}
|
||||
},
|
||||
"removeConfirm": {
|
||||
"mode": {
|
||||
"title": "移除模式",
|
||||
"message": "您確定要移除「{{modeName}}」模式嗎?",
|
||||
"rulesWarning": "這也會移除此模式的任何相關規則檔案。"
|
||||
},
|
||||
"mcp": {
|
||||
"title": "移除 MCP 伺服器",
|
||||
"message": "您確定要移除 MCP 伺服器「{{mcpName}}」嗎?"
|
||||
},
|
||||
"cancel": "取消",
|
||||
"confirm": "移除"
|
||||
},
|
||||
"footer": {
|
||||
"issueText": "發現 marketplace 項目問題或有新項目建議?<0>在 GitHub 開啟 issue</0> 告訴我們!"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue