From b0d189575359e376fc8edfcca8a3ae27ad717379 Mon Sep 17 00:00:00 2001 From: Dicha Zelianivan Arkana <51877647+elianiva@users.noreply.github.com> Date: Sat, 24 May 2025 16:09:26 +0700 Subject: [PATCH] chore(marketplace): finishing touches (#14) * fix(marketplace): done should redirect to browse * test(marketplace): update outdated tests * refactor(marketplace): handle opening in non-workspace * feat(marketplace): put behind a feature flag * fix(marketplace): missing translations * fix: solve lint errors, make build-able * fix: use `cwd` instead of `filePaths` for ws detection * fix(marketplace): should dedupe items with same id from multiple registries * fix: tab cycle should not reach hidden content * fix: `mcp` filter --------- Co-authored-by: NamesMT --- src/exports/roo-code.d.ts | 3 ++ src/exports/types.ts | 3 ++ src/schemas/index.ts | 3 +- .../marketplace/MarketplaceManager.ts | 20 ++++++-- src/shared/__tests__/experiments.test.ts | 42 +++++++++++++++ src/shared/experiments.ts | 2 + webview-ui/src/App.tsx | 28 +++++++--- .../marketplace/MarketplaceListView.tsx | 2 +- .../marketplace/MarketplaceView.tsx | 22 +++++--- .../MarketplaceViewStateManager.ts | 8 +-- .../__tests__/MarketplaceListView.test.tsx | 7 +-- .../MarketplaceSourcesConfig.test.tsx | 9 +--- .../__tests__/MarketplaceView.test.tsx | 13 ++--- .../MarketplaceViewStateManager.test.ts | 47 +++++++++-------- .../components/MarketplaceItemCard.tsx | 51 +++++++++++-------- .../__tests__/MarketplaceItemCard.test.tsx | 19 +++++++ .../src/i18n/locales/ca/marketplace.json | 3 +- webview-ui/src/i18n/locales/ca/settings.json | 5 ++ .../src/i18n/locales/de/marketplace.json | 3 +- webview-ui/src/i18n/locales/de/settings.json | 5 ++ .../src/i18n/locales/en/marketplace.json | 3 +- webview-ui/src/i18n/locales/en/settings.json | 5 ++ .../src/i18n/locales/es/marketplace.json | 3 +- webview-ui/src/i18n/locales/es/settings.json | 5 ++ .../src/i18n/locales/fr/marketplace.json | 3 +- webview-ui/src/i18n/locales/fr/settings.json | 5 ++ .../src/i18n/locales/hi/marketplace.json | 3 +- webview-ui/src/i18n/locales/hi/settings.json | 5 ++ .../src/i18n/locales/it/marketplace.json | 3 +- webview-ui/src/i18n/locales/it/settings.json | 5 ++ .../src/i18n/locales/ja/marketplace.json | 3 +- webview-ui/src/i18n/locales/ja/settings.json | 5 ++ .../src/i18n/locales/ko/marketplace.json | 3 +- webview-ui/src/i18n/locales/ko/settings.json | 5 ++ .../src/i18n/locales/nl/marketplace.json | 3 +- webview-ui/src/i18n/locales/nl/settings.json | 5 ++ .../src/i18n/locales/pl/marketplace.json | 3 +- webview-ui/src/i18n/locales/pl/settings.json | 5 ++ .../src/i18n/locales/pt-BR/marketplace.json | 3 +- .../src/i18n/locales/pt-BR/settings.json | 7 ++- .../src/i18n/locales/ru/marketplace.json | 3 +- webview-ui/src/i18n/locales/ru/settings.json | 5 ++ .../src/i18n/locales/tr/marketplace.json | 3 +- webview-ui/src/i18n/locales/tr/settings.json | 5 ++ .../src/i18n/locales/vi/marketplace.json | 3 +- webview-ui/src/i18n/locales/vi/settings.json | 7 ++- .../src/i18n/locales/zh-CN/marketplace.json | 3 +- .../src/i18n/locales/zh-CN/settings.json | 23 +++++---- .../src/i18n/locales/zh-TW/marketplace.json | 3 +- .../src/i18n/locales/zh-TW/settings.json | 5 ++ 50 files changed, 319 insertions(+), 118 deletions(-) diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index 6f3fb0b3f4..d7bad38854 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -104,6 +104,7 @@ type GlobalSettings = { | { autoCondenseContext: boolean powerSteering: boolean + marketplace: boolean } | undefined language?: @@ -843,6 +844,7 @@ type IpcMessage = | { autoCondenseContext: boolean powerSteering: boolean + marketplace: boolean } | undefined language?: @@ -1325,6 +1327,7 @@ type TaskCommand = | { autoCondenseContext: boolean powerSteering: boolean + marketplace: boolean } | undefined language?: diff --git a/src/exports/types.ts b/src/exports/types.ts index c6c21f824a..496a11dc76 100644 --- a/src/exports/types.ts +++ b/src/exports/types.ts @@ -104,6 +104,7 @@ type GlobalSettings = { | { autoCondenseContext: boolean powerSteering: boolean + marketplace: boolean } | undefined language?: @@ -857,6 +858,7 @@ type IpcMessage = | { autoCondenseContext: boolean powerSteering: boolean + marketplace: boolean } | undefined language?: @@ -1341,6 +1343,7 @@ type TaskCommand = | { autoCondenseContext: boolean powerSteering: boolean + marketplace: boolean } | undefined language?: diff --git a/src/schemas/index.ts b/src/schemas/index.ts index ee14325854..9068faa87a 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -387,7 +387,7 @@ export type CommandExecutionStatus = z.infer const experimentsSchema = z.object({ autoCondenseContext: z.boolean(), powerSteering: z.boolean(), + marketplace: z.boolean(), }) export type Experiments = z.infer diff --git a/src/services/marketplace/MarketplaceManager.ts b/src/services/marketplace/MarketplaceManager.ts index e7357feb80..0c9539866d 100644 --- a/src/services/marketplace/MarketplaceManager.ts +++ b/src/services/marketplace/MarketplaceManager.ts @@ -82,9 +82,19 @@ export class MarketplaceManager { async getMarketplaceItems( enabledSources: MarketplaceSource[], ): Promise<{ items: MarketplaceItem[]; errors?: string[] }> { - const items: MarketplaceItem[] = [] + const dedupedItems: Record = {} const errors: string[] = [] + function _dedupeAndAddItem(item: MarketplaceItem) { + const id = item.id + const existingItem = dedupedItems[id] + if (existingItem) { + if (existingItem.version >= item.version) return + } + + dedupedItems[id] = item + } + // Process sources sequentially with locking for (const source of enabledSources) { if (this.isSourceLocked(source.url)) { @@ -105,7 +115,7 @@ export class MarketplaceManager { sourceName: source.name || this.getRepoNameFromUrl(source.url), sourceUrl: source.url, })) - items.push(...itemsWithSource) + itemsWithSource.forEach(_dedupeAndAddItem) } }) } catch (error) { @@ -118,13 +128,13 @@ export class MarketplaceManager { } // Store the current items - this.currentItems = items + this.currentItems = Object.values(dedupedItems) // Preserve original unfiltered items - this.originalItems = items + this.originalItems = this.currentItems // Return both items and errors const result = { - items, + items: this.originalItems, ...(errors.length > 0 && { errors }), } diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index 9d0e6dab9c..edd0c989d3 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -24,6 +24,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: false, autoCondenseContext: false, + marketplace: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) @@ -32,6 +33,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: true, autoCondenseContext: false, + marketplace: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true) }) @@ -40,6 +42,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: false, autoCondenseContext: false, + marketplace: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) @@ -48,6 +51,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: false, autoCondenseContext: false, + marketplace: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT)).toBe(false) }) @@ -56,8 +60,46 @@ describe("experiments", () => { const experiments: Record = { powerSteering: false, autoCondenseContext: true, + marketplace: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT)).toBe(true) }) }) + describe("MARKETPLACE", () => { + it("is configured correctly", () => { + expect(EXPERIMENT_IDS.MARKETPLACE).toBe("marketplace") + expect(experimentConfigsMap.MARKETPLACE).toMatchObject({ + enabled: false, + }) + }) + }) + + describe("isEnabled for MARKETPLACE", () => { + it("returns false when MARKETPLACE experiment is not enabled", () => { + const experiments: Record = { + powerSteering: false, + autoCondenseContext: false, + marketplace: false, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(false) + }) + + it("returns true when MARKETPLACE experiment is enabled", () => { + const experiments: Record = { + powerSteering: false, + autoCondenseContext: false, + marketplace: true, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(true) + }) + + it("returns false when MARKETPLACE experiment is not present", () => { + const experiments: Record = { + powerSteering: false, + autoCondenseContext: false, + // marketplace missing + } as any + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.MARKETPLACE)).toBe(false) + }) + }) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index aa2c246bb6..ec96d250ee 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -5,6 +5,7 @@ export type { ExperimentId } export const EXPERIMENT_IDS = { POWER_STEERING: "powerSteering", + MARKETPLACE: "marketplace", AUTO_CONDENSE_CONTEXT: "autoCondenseContext", } as const satisfies Record @@ -18,6 +19,7 @@ interface ExperimentConfig { export const experimentConfigsMap: Record = { POWER_STEERING: { enabled: false }, + MARKETPLACE: { enabled: false }, AUTO_CONDENSE_CONTEXT: { enabled: false }, // Keep this last, there is a slider below it in the UI } diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index c0375dcde4..5fdf64b5ff 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -3,7 +3,7 @@ import { useEvent } from "react-use" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { ExtensionMessage } from "@roo/shared/ExtensionMessage" -import TranslationProvider from "./i18n/TranslationContext" +import TranslationProvider, { useAppTranslation } from "./i18n/TranslationContext" import { MarketplaceViewStateManager } from "./components/marketplace/MarketplaceViewStateManager" import { vscode } from "./utils/vscode" @@ -30,8 +30,16 @@ const tabsByMessageAction: Partial { - const { didHydrateState, showWelcome, shouldShowAnnouncement, telemetrySetting, telemetryKey, machineId } = - useExtensionState() + const { + didHydrateState, + showWelcome, + shouldShowAnnouncement, + telemetrySetting, + telemetryKey, + machineId, + experiments, + } = useExtensionState() + const { t } = useAppTranslation() // Create a persistent state manager const marketplaceStateManager = useMemo(() => new MarketplaceViewStateManager(), []) @@ -124,9 +132,17 @@ const App = () => { {tab === "settings" && ( setTab("chat")} targetSection={currentSection} /> )} - {tab === "marketplace" && ( - switchTab("chat")} /> - )} + {tab === "marketplace" && + (experiments.marketplace ? ( + switchTab("chat")} /> + ) : ( +
+
{t("settings:experimental.MARKETPLACE.name")}
+
+ {t("settings:experimental.MARKETPLACE.warning")} +
+
+ ))} - + {t("marketplace:filters.type.mcp server")} diff --git a/webview-ui/src/components/marketplace/MarketplaceView.tsx b/webview-ui/src/components/marketplace/MarketplaceView.tsx index 7fe60dc6a0..3e18e33ad2 100644 --- a/webview-ui/src/components/marketplace/MarketplaceView.tsx +++ b/webview-ui/src/components/marketplace/MarketplaceView.tsx @@ -95,7 +95,15 @@ export function MarketplaceView({ stateManager, onDone }: MarketplaceViewProps) {t("marketplace:refresh")} - @@ -145,8 +153,8 @@ export function MarketplaceView({ stateManager, onDone }: MarketplaceViewProps)
diff --git a/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts b/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts index 0e4e51b1d5..c32e10265f 100644 --- a/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts +++ b/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts @@ -260,15 +260,17 @@ export class MarketplaceViewStateManager { } case "FETCH_ERROR": { - // Preserve current filters and sources - const { filters, sources, activeTab } = this.state + // Preserve current filters, sources, and items + const { filters, sources, activeTab, allItems, displayItems } = this.state - // Reset state but preserve filters and sources + // Reset state but preserve filters, sources, and items this.state = { ...this.getDefaultState(), filters, sources, activeTab, + allItems, + displayItems, isFetching: false, } this.notifyStateChange() diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.test.tsx b/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.test.tsx index 58016e9561..8e96975f94 100644 --- a/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.test.tsx +++ b/webview-ui/src/components/marketplace/__tests__/MarketplaceListView.test.tsx @@ -5,14 +5,12 @@ import { ViewState } from "../MarketplaceViewStateManager" import userEvent from "@testing-library/user-event" import { TooltipProvider } from "@/components/ui/tooltip" -// Mock translation hook jest.mock("@/i18n/TranslationContext", () => ({ useAppTranslation: () => ({ - t: (key: string) => key, // Return the key as-is for easy testing + t: (key: string) => key, }), })) -// Mock ResizeObserver class MockResizeObserver { observe() {} unobserve() {} @@ -21,7 +19,6 @@ class MockResizeObserver { global.ResizeObserver = MockResizeObserver -// Mock state manager with initial state const mockTransition = jest.fn() const mockState: ViewState = { allItems: [], @@ -45,12 +42,10 @@ const mockState: ViewState = { }, } -// Mock useStateManager hook jest.mock("../useStateManager", () => ({ useStateManager: () => [mockState, { transition: mockTransition }], })) -// Mock all lucide-react icons jest.mock("lucide-react", () => { return new Proxy( {}, diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceSourcesConfig.test.tsx b/webview-ui/src/components/marketplace/__tests__/MarketplaceSourcesConfig.test.tsx index 8b31b5e6a4..9dfa8bf482 100644 --- a/webview-ui/src/components/marketplace/__tests__/MarketplaceSourcesConfig.test.tsx +++ b/webview-ui/src/components/marketplace/__tests__/MarketplaceSourcesConfig.test.tsx @@ -3,14 +3,12 @@ import { MarketplaceSourcesConfig } from "../MarketplaceSourcesConfigView" import { MarketplaceViewStateManager } from "../MarketplaceViewStateManager" import { validateSource, ValidationError } from "@roo/shared/MarketplaceValidation" -// Mock the translation hook jest.mock("@/i18n/TranslationContext", () => ({ useAppTranslation: () => ({ - t: (key: string) => key, // Return the key as-is for testing + t: (key: string) => key, }), })) -// Mock the validateSource function jest.mock("@roo/shared/MarketplaceValidation", () => ({ validateSource: jest.fn(), })) @@ -20,13 +18,11 @@ describe("MarketplaceSourcesConfig", () => { beforeEach(() => { stateManager = new MarketplaceViewStateManager() - // Reset state manager to have no sources stateManager.transition({ type: "UPDATE_SOURCES", payload: { sources: [] }, }) jest.clearAllMocks() - // Default mock implementation for validateSource ;(validateSource as jest.Mock).mockReturnValue([]) }) @@ -83,7 +79,6 @@ describe("MarketplaceSourcesConfig", () => { fireEvent.change(urlInput, { target: { value: "" } }) // Set URL to empty fireEvent.blur(urlInput) // Trigger blur to activate client-side validation - // This error is displayed as a field-specific error message const errorMessage = await screen.findByText("marketplace:sources.errors.emptyUrl", { selector: "p.text-xs.text-red-500", }) @@ -112,7 +107,6 @@ describe("MarketplaceSourcesConfig", () => { }) it("shows error when max sources reached", async () => { - // Add max number of sources with unique URLs const maxSources = Array(10) .fill(null) .map((_, i) => ({ @@ -233,7 +227,6 @@ describe("MarketplaceSourcesConfig", () => { const longName = "This is a very long source name that exceeds limit" fireEvent.change(nameInput, { target: { value: longName } }) - // The component should truncate to 20 chars expect(nameInput).toHaveValue(longName.slice(0, 20)) }) diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceView.test.tsx b/webview-ui/src/components/marketplace/__tests__/MarketplaceView.test.tsx index ddda448509..4e39f2aea7 100644 --- a/webview-ui/src/components/marketplace/__tests__/MarketplaceView.test.tsx +++ b/webview-ui/src/components/marketplace/__tests__/MarketplaceView.test.tsx @@ -7,25 +7,22 @@ import { TooltipProvider } from "@/components/ui/tooltip" import type { RocketConfig } from "config-rocket" import { ExtensionStateContext } from "@/context/ExtensionStateContext" -// Mock vscode API - IMPORTANT: This mock must be at the very top of the file const mockPostMessage = jest.fn() jest.mock("@src/utils/vscode", () => ({ vscode: { postMessage: mockPostMessage, - getState: jest.fn(() => ({})), // Mock getState as well if it's used + getState: jest.fn(() => ({})), setState: jest.fn(), }, })) -// Mock translation hook jest.mock("@/i18n/TranslationContext", () => ({ useAppTranslation: () => ({ - t: (key: string) => key, // Return the key as-is for easy testing + t: (key: string) => key, }), })) -// Mock useEvent from react-use -let mockUseEventHandler: ((event: MessageEvent) => void) | undefined // Declare outside mock +let mockUseEventHandler: ((event: MessageEvent) => void) | undefined jest.mock("react-use", () => ({ useEvent: jest.fn((eventName, handler) => { if (eventName === "message") { @@ -34,7 +31,6 @@ jest.mock("react-use", () => ({ }), })) -// Mock ResizeObserver class MockResizeObserver { observe() {} unobserve() {} @@ -237,6 +233,7 @@ describe("MarketplaceView", () => { experiments: { autoCondenseContext: false, powerSteering: false, + marketplace: true, }, marketplaceSources: [], }}> @@ -254,7 +251,7 @@ describe("MarketplaceView", () => { expect(screen.getByText("marketplace:done")).toBeInTheDocument() }) - it("calls onDone when Done button is clicked", async () => { + it("calls onDone when Done button is clicked and active tab is browse or installed", async () => { const user = userEvent.setup() const onDoneMock = jest.fn() renderWithProviders({ onDone: onDoneMock }) diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.test.ts b/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.test.ts index 1d752778e0..4dda0ca7c4 100644 --- a/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.test.ts +++ b/webview-ui/src/components/marketplace/__tests__/MarketplaceViewStateManager.test.ts @@ -46,7 +46,7 @@ describe("MarketplaceViewStateManager", () => { }) describe("Initial State", () => { - it.skip("should initialize with default state", () => { + it("should initialize with default state", () => { const state = manager.getState() expect(state).toEqual({ allItems: [], @@ -55,6 +55,10 @@ describe("MarketplaceViewStateManager", () => { activeTab: "browse", refreshingUrls: [], sources: [DEFAULT_MARKETPLACE_SOURCE], + installedMetadata: { + project: {}, + global: {}, + }, filters: { type: "", search: "", @@ -103,13 +107,12 @@ describe("MarketplaceViewStateManager", () => { }) describe("Fetch Transitions", () => { - it.skip("should handle FETCH_ITEMS transition", async () => { + it("should handle FETCH_ITEMS transition", async () => { jest.clearAllMocks() // Clear mock to ignore initialize() call await manager.transition({ type: "FETCH_ITEMS" }) expect(vscode.postMessage).toHaveBeenCalledWith({ type: "fetchMarketplaceItems", - bool: true, }) const state = manager.getState() @@ -398,11 +401,12 @@ describe("MarketplaceViewStateManager", () => { }) describe("Error Handling", () => { - it.skip("should handle fetch timeout", async () => { + it("should handle fetch timeout", async () => { await manager.transition({ type: "FETCH_ITEMS" }) - // Fast-forward past the timeout + // Fast-forward past the timeout and simulate error message jest.advanceTimersByTime(30000) + manager.handleMessage({ type: "marketplaceButtonClicked", text: "error" }) const state = manager.getState() expect(state.isFetching).toBe(false) @@ -583,7 +587,7 @@ describe("MarketplaceViewStateManager", () => { expect(state.isFetching).toBe(false) }) - it.skip("should handle marketplace button click for refresh", () => { + it("should handle marketplace button click for refresh", () => { manager.handleMessage({ type: "marketplaceButtonClicked", }) @@ -592,7 +596,6 @@ describe("MarketplaceViewStateManager", () => { expect(state.isFetching).toBe(true) expect(vscode.postMessage).toHaveBeenCalledWith({ type: "fetchMarketplaceItems", - bool: true, }) }) }) @@ -608,7 +611,7 @@ describe("MarketplaceViewStateManager", () => { expect(state.activeTab).toBe("settings") }) - it.skip("should trigger initial fetch when switching to browse with no items", async () => { + it("should trigger initial fetch when switching to browse with no items", async () => { jest.clearAllMocks() // Clear mock to ignore initialize() call // Start in settings tab @@ -625,7 +628,6 @@ describe("MarketplaceViewStateManager", () => { expect(vscode.postMessage).toHaveBeenCalledWith({ type: "fetchMarketplaceItems", - bool: true, }) }) @@ -656,7 +658,7 @@ describe("MarketplaceViewStateManager", () => { }) }) - it.skip("should automatically fetch when sources are modified and viewing browse tab", async () => { + it("should automatically fetch when sources are modified and viewing browse tab", async () => { jest.clearAllMocks() // Clear mock to ignore initialize() call // Add some items first @@ -680,7 +682,6 @@ describe("MarketplaceViewStateManager", () => { // Should trigger fetch due to source modification expect(vscode.postMessage).toHaveBeenCalledWith({ type: "fetchMarketplaceItems", - bool: true, }) }) @@ -698,11 +699,12 @@ describe("MarketplaceViewStateManager", () => { }) describe("Fetch Timeout Handling", () => { - it.skip("should handle fetch timeout", async () => { + it("should handle fetch timeout", async () => { await manager.transition({ type: "FETCH_ITEMS" }) - // Fast-forward past the timeout + // Fast-forward past the timeout and simulate error message jest.advanceTimersByTime(30000) + manager.handleMessage({ type: "marketplaceButtonClicked", text: "error" }) const state = manager.getState() expect(state.isFetching).toBe(false) @@ -756,7 +758,7 @@ describe("MarketplaceViewStateManager", () => { expect(state.activeTab).toBe("settings") }) - it.skip("should make minimal state updates when timeout occurs in browse tab", async () => { + it("should make minimal state updates when timeout occurs in browse tab", async () => { // First ensure we're in browse tab await manager.transition({ type: "SET_ACTIVE_TAB", @@ -770,26 +772,24 @@ describe("MarketplaceViewStateManager", () => { payload: { items: testItems }, }) - // Start a new fetch - await manager.transition({ type: "FETCH_ITEMS" }) - // Track state changes let stateChangeCount = 0 const unsubscribe = manager.onStateChange(() => { stateChangeCount++ }) - // Reset the counter since we've already had state changes - stateChangeCount = 0 + // Start a new fetch + await manager.transition({ type: "FETCH_ITEMS" }) - // Fast-forward past the timeout + // Fast-forward past the timeout and simulate error message jest.advanceTimersByTime(30000) + manager.handleMessage({ type: "marketplaceButtonClicked", text: "error" }) // Clean up the handler unsubscribe() - // Verify we got a state update - expect(stateChangeCount).toBe(1) + // Verify we got a state update (one for FETCH_ITEMS, one for FETCH_ERROR) + expect(stateChangeCount).toBe(2) // Verify the items were preserved const state = manager.getState() @@ -825,7 +825,7 @@ describe("MarketplaceViewStateManager", () => { jest.useRealTimers() }) - it.skip("should trigger fetch for remaining source after source deletion when in browse tab", async () => { + it("should trigger fetch for remaining source after source deletion when in browse tab", async () => { // Start with two sources const sources = [ { url: "https://github.com/test/repo1", enabled: true }, @@ -855,7 +855,6 @@ describe("MarketplaceViewStateManager", () => { // Verify that a fetch was triggered for the remaining source expect(vscode.postMessage).toHaveBeenCalledWith({ type: "fetchMarketplaceItems", - bool: true, }) // Verify state has the remaining source diff --git a/webview-ui/src/components/marketplace/components/MarketplaceItemCard.tsx b/webview-ui/src/components/marketplace/components/MarketplaceItemCard.tsx index b854b99b30..0c1a46627a 100644 --- a/webview-ui/src/components/marketplace/components/MarketplaceItemCard.tsx +++ b/webview-ui/src/components/marketplace/components/MarketplaceItemCard.tsx @@ -13,6 +13,7 @@ import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { Rocket, Server, Package, Sparkles, ChevronDown } from "lucide-react" +import { useExtensionState } from "@/context/ExtensionStateContext" interface MarketplaceItemCardProps { item: MarketplaceItem @@ -42,6 +43,7 @@ export const MarketplaceItemCard: React.FC = ({ setActiveTab, }) => { const { t } = useAppTranslation() + const { cwd } = useExtensionState() const typeLabel = useMemo(() => { const labels: Partial> = { @@ -137,28 +139,35 @@ export const MarketplaceItemCard: React.FC = ({
- + + + - {installed.project - ? t("marketplace:items.card.removeProject") - : t("marketplace:items.card.installProject")} + {!cwd + ? t("marketplace:items.card.noWorkspaceTooltip") + : installed.project + ? t("marketplace:items.card.removeProject") + : t("marketplace:items.card.installProject")} = ({ triggerNode={ diff --git a/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.test.tsx b/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.test.tsx index 707d2ad037..a316c90815 100644 --- a/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.test.tsx +++ b/webview-ui/src/components/marketplace/components/__tests__/MarketplaceItemCard.test.tsx @@ -310,6 +310,25 @@ describe("MarketplaceItemCard", () => { }) }) + it("disables install button and shows tooltip when no workspace is open", async () => { + // Mock useExtensionState to simulate no workspace + // eslint-disable-next-line @typescript-eslint/no-require-imports + jest.spyOn(require("@/context/ExtensionStateContext"), "useExtensionState").mockReturnValue({ + filePaths: [], + } as any) + + const user = userEvent.setup() + renderWithProviders() + + const installButton = screen.getByRole("button", { name: "Install Project" }) + expect(installButton).toBeDisabled() + + // Hover to trigger tooltip + await user.hover(installButton) + const tooltip = await screen.findByText("Open a workspace to install marketplace items") + expect(tooltip).toBeInTheDocument() + }) + describe("MarketplaceItemCard expandable section badge", () => { it("shows badge count for matched sub-items", () => { const packageItem: MarketplaceItem = { diff --git a/webview-ui/src/i18n/locales/ca/marketplace.json b/webview-ui/src/i18n/locales/ca/marketplace.json index cbb91167ff..773207374c 100644 --- a/webview-ui/src/i18n/locales/ca/marketplace.json +++ b/webview-ui/src/i18n/locales/ca/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Eliminar", "removeGlobal": "Eliminar (Global)", "viewSource": "Veure", - "viewOnSource": "Veure a {{source}}" + "viewOnSource": "Veure a {{source}}", + "noWorkspaceTooltip": "Obre un espai de treball per instal·lar elements del marketplace" } }, "installProjectTooltip": "Instal·lació del projecte", diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index a3f042d958..0bdafd1550 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Utilitzar eina diff de blocs múltiples experimental", "description": "Quan està activat, Roo utilitzarà l'eina diff de blocs múltiples. Això intentarà actualitzar múltiples blocs de codi a l'arxiu en una sola petició." + }, + "MARKETPLACE": { + "name": "Habilitar Marketplace a Roo Code", + "description": "Quan està habilitat, Roo podrà instal·lar i gestionar elements del Marketplace.", + "warning": "El Marketplace encara no està habilitat. Si voleu ser un dels primers a adoptar-lo, activeu-lo a la configuració experimental." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/de/marketplace.json b/webview-ui/src/i18n/locales/de/marketplace.json index 52a4d4b06a..cde4c0ef4e 100644 --- a/webview-ui/src/i18n/locales/de/marketplace.json +++ b/webview-ui/src/i18n/locales/de/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Entfernen", "removeGlobal": "Entfernen (Global)", "viewSource": "Ansehen", - "viewOnSource": "Auf {{source}} ansehen" + "viewOnSource": "Auf {{source}} ansehen", + "noWorkspaceTooltip": "Öffne einen Arbeitsbereich, um Marketplace-Elemente zu installieren" } }, "installProjectTooltip": "Projektinstallation", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 6978731bb6..3d103d79f8 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Experimentelles Multi-Block-Diff-Werkzeug verwenden", "description": "Wenn aktiviert, verwendet Roo das Multi-Block-Diff-Werkzeug. Dies versucht, mehrere Codeblöcke in der Datei in einer Anfrage zu aktualisieren." + }, + "MARKETPLACE": { + "name": "Marktplatz in Roo Code aktivieren", + "description": "Wenn aktiviert, kann Roo Elemente vom Marktplatz installieren und verwalten.", + "warning": "Der Marktplatz ist noch nicht aktiviert. Wenn du ein Early Adopter sein möchtest, aktiviere ihn bitte in den Experimentellen Einstellungen." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/en/marketplace.json b/webview-ui/src/i18n/locales/en/marketplace.json index cc384e8419..8e3335e7b9 100644 --- a/webview-ui/src/i18n/locales/en/marketplace.json +++ b/webview-ui/src/i18n/locales/en/marketplace.json @@ -66,7 +66,8 @@ "removeProject": "Remove", "removeGlobal": "Remove (Global)", "viewSource": "View", - "viewOnSource": "View on {{source}}" + "viewOnSource": "View on {{source}}", + "noWorkspaceTooltip": "Open a workspace to install marketplace items" } }, "sources": { diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 56c0dfb9b9..8885851da1 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Use experimental multi block diff tool", "description": "When enabled, Roo will use multi block diff tool. This will try to update multiple code blocks in the file in one request." + }, + "MARKETPLACE": { + "name": "Enable Marketplace in Roo Code", + "description": "When enabled, Roo will be able to install and manage items from the Marketplace.", + "warning": "The Marketplace is not yet enabled. If you want to be an early adopter, please enable it in the Experimental Settings." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/es/marketplace.json b/webview-ui/src/i18n/locales/es/marketplace.json index 5de3ec4458..57bb065de5 100644 --- a/webview-ui/src/i18n/locales/es/marketplace.json +++ b/webview-ui/src/i18n/locales/es/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Eliminar", "removeGlobal": "Eliminar (Global)", "viewSource": "Ver", - "viewOnSource": "Ver en {{source}}" + "viewOnSource": "Ver en {{source}}", + "noWorkspaceTooltip": "Abre un espacio de trabajo para instalar elementos del marketplace" } }, "installProjectTooltip": "Instalación del proyecto", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 8c4aa271f9..f992c4e363 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Usar herramienta experimental de diff de bloques múltiples", "description": "Cuando está habilitado, Roo usará la herramienta de diff de bloques múltiples. Esto intentará actualizar múltiples bloques de código en el archivo en una sola solicitud." + }, + "MARKETPLACE": { + "name": "Habilitar Marketplace en Roo Code", + "description": "Cuando está habilitado, Roo podrá instalar y administrar elementos del Marketplace.", + "warning": "El Marketplace aún no está habilitado. Si desea ser un adoptador temprano, habilítelo en la Configuración experimental." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/fr/marketplace.json b/webview-ui/src/i18n/locales/fr/marketplace.json index 3e09ccfd74..563695c793 100644 --- a/webview-ui/src/i18n/locales/fr/marketplace.json +++ b/webview-ui/src/i18n/locales/fr/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Supprimer", "removeGlobal": "Supprimer (Global)", "viewSource": "Voir", - "viewOnSource": "Voir sur {{source}}" + "viewOnSource": "Voir sur {{source}}", + "noWorkspaceTooltip": "Ouvrez un espace de travail pour installer les éléments du marketplace" } }, "installProjectTooltip": "Installation du projet", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index bcef1acfac..3b923baba7 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Utiliser l'outil diff multi-blocs expérimental", "description": "Lorsqu'il est activé, Roo utilisera l'outil diff multi-blocs. Cela tentera de mettre à jour plusieurs blocs de code dans le fichier en une seule requête." + }, + "MARKETPLACE": { + "name": "Activer le Marketplace dans Roo Code", + "description": "Lorsqu'il est activé, Roo pourra installer et gérer des éléments du Marketplace.", + "warning": "Le Marketplace n'est pas encore activé. Si vous souhaitez être un adopteur précoce, veuillez l'activer dans les paramètres expérimentaux." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/hi/marketplace.json b/webview-ui/src/i18n/locales/hi/marketplace.json index 5f9d95004c..2ff4041106 100644 --- a/webview-ui/src/i18n/locales/hi/marketplace.json +++ b/webview-ui/src/i18n/locales/hi/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "हटाएं", "removeGlobal": "हटाएं (ग्लोबल)", "viewSource": "देखें", - "viewOnSource": "{{source}} पर देखें" + "viewOnSource": "{{source}} पर देखें", + "noWorkspaceTooltip": "मार्केटप्लेस आइटम इंस्टॉल करने के लिए एक कार्यक्षेत्र खोलें" } }, "installProjectTooltip": "परियोजना स्थापना", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index a348ca0502..76cb10be00 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "प्रायोगिक मल्टी ब्लॉक diff उपकरण का उपयोग करें", "description": "जब सक्षम किया जाता है, तो Roo मल्टी ब्लॉक diff उपकरण का उपयोग करेगा। यह एक अनुरोध में फ़ाइल में कई कोड ब्लॉक अपडेट करने का प्रयास करेगा।" + }, + "MARKETPLACE": { + "name": "Roo Code में मार्केटप्लेस सक्षम करें", + "description": "जब सक्षम होता है, तो Roo मार्केटप्लेस से आइटम स्थापित और प्रबंधित करने में सक्षम होगा।", + "warning": "मार्केटप्लेस अभी तक सक्षम नहीं है। यदि आप शुरुआती अपनाने वाले बनना चाहते हैं, तो कृपया इसे प्रायोगिक सेटिंग्स में सक्षम करें।" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/it/marketplace.json b/webview-ui/src/i18n/locales/it/marketplace.json index 46e46d0401..9fbbce09e1 100644 --- a/webview-ui/src/i18n/locales/it/marketplace.json +++ b/webview-ui/src/i18n/locales/it/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Rimuovi", "removeGlobal": "Rimuovi (Globale)", "viewSource": "Visualizza", - "viewOnSource": "Visualizza su {{source}}" + "viewOnSource": "Visualizza su {{source}}", + "noWorkspaceTooltip": "Apri un'area di lavoro per installare gli elementi del marketplace" } }, "installProjectTooltip": "Installazione del progetto", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 4d64f5f8fc..295ac1ce40 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Usa strumento diff multi-blocco sperimentale", "description": "Quando abilitato, Roo utilizzerà lo strumento diff multi-blocco. Questo tenterà di aggiornare più blocchi di codice nel file in una singola richiesta." + }, + "MARKETPLACE": { + "name": "Abilita Marketplace in Roo Code", + "description": "Quando abilitato, Roo sarà in grado di installare e gestire elementi dal Marketplace.", + "warning": "Il Marketplace non è ancora abilitato. Se vuoi essere un early adopter, abilitalo nelle Impostazioni sperimentali." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ja/marketplace.json b/webview-ui/src/i18n/locales/ja/marketplace.json index 61deb6a88b..57ab7c0927 100644 --- a/webview-ui/src/i18n/locales/ja/marketplace.json +++ b/webview-ui/src/i18n/locales/ja/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "削除", "removeGlobal": "削除 (グローバル)", "viewSource": "表示", - "viewOnSource": "{{source}}で表示" + "viewOnSource": "{{source}}で表示", + "noWorkspaceTooltip": "マーケットプレイスアイテムをインストールするには、ワークスペースを開いてください" } }, "installProjectTooltip": "プロジェクトのインストール", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 911bf2293c..5865c31b94 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "実験的なマルチブロックdiffツールを使用する", "description": "有効にすると、Rooはマルチブロックdiffツールを使用します。これにより、1つのリクエストでファイル内の複数のコードブロックを更新しようとします。" + }, + "MARKETPLACE": { + "name": "Roo Codeでマーケットプレイスを有効にする", + "description": "有効にすると、Rooはマーケットプレイスからアイテムをインストールおよび管理できるようになります。", + "warning": "マーケットプレイスはまだ有効になっていません。早期導入者になりたい場合は、実験的設定で有効にしてください。" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ko/marketplace.json b/webview-ui/src/i18n/locales/ko/marketplace.json index 4d227bf404..55d7ff6956 100644 --- a/webview-ui/src/i18n/locales/ko/marketplace.json +++ b/webview-ui/src/i18n/locales/ko/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "제거", "removeGlobal": "제거 (글로벌)", "viewSource": "보기", - "viewOnSource": "{{source}}에서 보기" + "viewOnSource": "{{source}}에서 보기", + "noWorkspaceTooltip": "마켓플레이스 항목을 설치하려면 작업 영역을 여세요" } }, "installProjectTooltip": "프로젝트 설치", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 00ceb60b1a..70b903be23 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "실험적 다중 블록 diff 도구 사용", "description": "활성화하면 Roo가 다중 블록 diff 도구를 사용합니다. 이것은 하나의 요청에서 파일의 여러 코드 블록을 업데이트하려고 시도합니다." + }, + "MARKETPLACE": { + "name": "Roo Code에서 마켓플레이스 활성화", + "description": "활성화하면 Roo는 마켓플레이스에서 항목을 설치하고 관리할 수 있습니다.", + "warning": "마켓플레이스는 아직 활성화되지 않았습니다. 얼리 어답터가 되려면 실험적 설정에서 활성화하세요." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/nl/marketplace.json b/webview-ui/src/i18n/locales/nl/marketplace.json index 56c026a659..4718dcc60d 100644 --- a/webview-ui/src/i18n/locales/nl/marketplace.json +++ b/webview-ui/src/i18n/locales/nl/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Verwijderen", "removeGlobal": "Verwijderen (Globaal)", "viewSource": "Bekijken", - "viewOnSource": "Bekijken op {{source}}" + "viewOnSource": "Bekijken op {{source}}", + "noWorkspaceTooltip": "Open een werkruimte om marketplace-items te installeren" } }, "installProjectTooltip": "Projectinstallatie", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index dadd4c7089..97c342f1c7 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Experimentele multi-block diff-tool gebruiken", "description": "Indien ingeschakeld, gebruikt Roo de multi-block diff-tool. Hiermee wordt geprobeerd meerdere codeblokken in het bestand in één verzoek bij te werken." + }, + "MARKETPLACE": { + "name": "Marktplaats in Roo Code inschakelen", + "description": "Indien ingeschakeld, kan Roo items van de Marktplaats installeren en beheren.", + "warning": "De Marktplaats is nog niet ingeschakeld. Als je een vroege gebruiker wilt zijn, schakel deze dan in de Experimentele instellingen in." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pl/marketplace.json b/webview-ui/src/i18n/locales/pl/marketplace.json index 29526ad5b0..72620d8071 100644 --- a/webview-ui/src/i18n/locales/pl/marketplace.json +++ b/webview-ui/src/i18n/locales/pl/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Usuń", "removeGlobal": "Usuń (Globalny)", "viewSource": "Zobacz", - "viewOnSource": "Zobacz na {{source}}" + "viewOnSource": "Zobacz na {{source}}", + "noWorkspaceTooltip": "Otwórz obszar roboczy, aby zainstalować elementy Marketplace" } }, "installProjectTooltip": "Instalacja projektu", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index a2eb9f1b4b..660e6425cc 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Użyj eksperymentalnego narzędzia diff wieloblokowego", "description": "Po włączeniu, Roo użyje narzędzia diff wieloblokowego. Spróbuje to zaktualizować wiele bloków kodu w pliku w jednym żądaniu." + }, + "MARKETPLACE": { + "name": "Włącz Marketplace w Roo Code", + "description": "Po włączeniu, Roo będzie w stanie instalować i zarządzać elementami z Marketplace.", + "warning": "Marketplace nie jest jeszcze włączony. Jeśli chcesz być wczesnym użytkownikiem, włącz go w Ustawieniach eksperymentalnych." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pt-BR/marketplace.json b/webview-ui/src/i18n/locales/pt-BR/marketplace.json index 5fd6580f48..bea167259d 100644 --- a/webview-ui/src/i18n/locales/pt-BR/marketplace.json +++ b/webview-ui/src/i18n/locales/pt-BR/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Remover", "removeGlobal": "Remover (Global)", "viewSource": "Ver", - "viewOnSource": "Ver no {{source}}" + "viewOnSource": "Ver no {{source}}", + "noWorkspaceTooltip": "Abra um espaço de trabalho para instalar itens do marketplace" } }, "installProjectTooltip": "Instalação do projeto", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 05afd90b76..d0d5a1ae42 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -30,7 +30,7 @@ "terminal": "Terminal", "experimental": "Experimental", "language": "Idioma", - "about": "Sobre" + "about": "Sobre Roo Code" }, "autoApprove": { "description": "Permitir que o Roo realize operações automaticamente sem exigir aprovação. Ative essas configurações apenas se confiar totalmente na IA e compreender os riscos de segurança associados.", @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Usar ferramenta diff de múltiplos blocos experimental", "description": "Quando ativado, o Roo usará a ferramenta diff de múltiplos blocos. Isso tentará atualizar vários blocos de código no arquivo em uma única solicitação." + }, + "MARKETPLACE": { + "name": "Ativar Marketplace no Roo Code", + "description": "Quando ativado, o Roo poderá instalar e gerenciar itens do Marketplace.", + "warning": "O Marketplace ainda não está ativado. Se você quiser ser um adotante inicial, ative-o nas Configurações experimentais." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ru/marketplace.json b/webview-ui/src/i18n/locales/ru/marketplace.json index 5a9559363e..f2f9955ece 100644 --- a/webview-ui/src/i18n/locales/ru/marketplace.json +++ b/webview-ui/src/i18n/locales/ru/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Удалить", "removeGlobal": "Удалить (Глобально)", "viewSource": "Просмотреть", - "viewOnSource": "Просмотреть на {{source}}" + "viewOnSource": "Просмотреть на {{source}}", + "noWorkspaceTooltip": "Откройте рабочую область для установки элементов marketplace" } }, "installProjectTooltip": "Установка проекта", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index db78af77de..358c2e5405 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Использовать экспериментальный мультиблочный инструмент диффа", "description": "Если включено, Roo будет использовать мультиблочный инструмент диффа, пытаясь обновить несколько блоков кода за один запрос." + }, + "MARKETPLACE": { + "name": "Включить Marketplace в Roo Code", + "description": "Если включено, Roo сможет устанавливать элементы из Marketplace и управлять ими.", + "warning": "Marketplace ещё не включён. Если вы хотите стать ранним пользователем, включите его в экспериментальных настройках." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/tr/marketplace.json b/webview-ui/src/i18n/locales/tr/marketplace.json index 2fbdfd7ab5..d40af6b478 100644 --- a/webview-ui/src/i18n/locales/tr/marketplace.json +++ b/webview-ui/src/i18n/locales/tr/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Kaldır", "removeGlobal": "Kaldır (Global)", "viewSource": "Görüntüle", - "viewOnSource": "{{source}} üzerinde görüntüle" + "viewOnSource": "{{source}} üzerinde görüntüle", + "noWorkspaceTooltip": "Marketplace öğelerini yüklemek için bir çalışma alanı açın" } }, "installProjectTooltip": "Proje kurulumu", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 2f4b1fa971..9ff53b4bb6 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Deneysel çoklu blok diff aracını kullan", "description": "Etkinleştirildiğinde, Roo çoklu blok diff aracını kullanacaktır. Bu, tek bir istekte dosyadaki birden fazla kod bloğunu güncellemeye çalışacaktır." + }, + "MARKETPLACE": { + "name": "Roo Code'da Pazaryeri'ni etkinleştir", + "description": "Etkinleştirildiğinde, Roo Pazaryeri'nden öğeleri yükleyebilir ve yönetebilir.", + "warning": "Pazaryeri henüz etkinleştirilmedi. Erken benimseyen olmak istiyorsanız, lütfen Deneysel Ayarlar'da etkinleştirin." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/vi/marketplace.json b/webview-ui/src/i18n/locales/vi/marketplace.json index 2293812b1b..53ec3f3fff 100644 --- a/webview-ui/src/i18n/locales/vi/marketplace.json +++ b/webview-ui/src/i18n/locales/vi/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "Gỡ cài đặt", "removeGlobal": "Gỡ cài đặt (Toàn cục)", "viewSource": "Xem", - "viewOnSource": "Xem trên {{source}}" + "viewOnSource": "Xem trên {{source}}", + "noWorkspaceTooltip": "Mở một không gian làm việc để cài đặt các mục marketplace" } }, "installProjectTooltip": "Cài đặt dự án", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index a3e3706839..b90722871d 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -30,7 +30,7 @@ "terminal": "Terminal", "experimental": "Thử nghiệm", "language": "Ngôn ngữ", - "about": "Giới thiệu" + "about": "Giới thiệu Roo Code" }, "autoApprove": { "description": "Cho phép Roo tự động thực hiện các hoạt động mà không cần phê duyệt. Chỉ bật những cài đặt này nếu bạn hoàn toàn tin tưởng AI và hiểu rõ các rủi ro bảo mật liên quan.", @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Sử dụng công cụ diff đa khối thử nghiệm", "description": "Khi được bật, Roo sẽ sử dụng công cụ diff đa khối. Điều này sẽ cố gắng cập nhật nhiều khối mã trong tệp trong một yêu cầu." + }, + "MARKETPLACE": { + "name": "Bật Marketplace trong Roo Code", + "description": "Khi được bật, Roo sẽ có thể cài đặt và quản lý các mục từ Marketplace.", + "warning": "Marketplace chưa được bật. Nếu bạn muốn trở thành người dùng sớm, vui lòng bật nó trong Cài đặt thử nghiệm." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-CN/marketplace.json b/webview-ui/src/i18n/locales/zh-CN/marketplace.json index ad5914d63d..53e7e683b0 100644 --- a/webview-ui/src/i18n/locales/zh-CN/marketplace.json +++ b/webview-ui/src/i18n/locales/zh-CN/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "移除(项目)", "removeGlobal": "移除(全局)", "viewSource": "查看", - "viewOnSource": "在 {{source}} 上查看" + "viewOnSource": "在 {{source}} 上查看", + "noWorkspaceTooltip": "打开工作区以安装市场项目" } }, "installProjectTooltip": "项目安装", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index b51810918e..4ef0a61ed3 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -412,24 +412,29 @@ "description": "智能上下文压缩使用 LLM 调用来总结过去的对话,在任务上下文窗口达到预设阈值时进行,而不是在上下文填满时丢弃旧消息。" }, "DIFF_STRATEGY_UNIFIED": { - "name": "启用diff更新工具", - "description": "可减少因模型错误导致的重复尝试,但可能引发意外操作。启用前请确保理解风险并会仔细检查所有修改。" + "name": "使用实验性统一差异更新策略", + "description": "启用实验性统一差异更新策略。此策略可能会减少因模型错误导致的重试次数,但可能导致意外行为或不正确的编辑。仅在您理解风险并愿意仔细审查所有更改时才启用。" }, "SEARCH_AND_REPLACE": { - "name": "启用搜索和替换工具", + "name": "使用实验性搜索和替换工具", "description": "启用实验性搜索和替换工具,允许 Roo 在一个请求中替换搜索词的多个实例。" }, "INSERT_BLOCK": { - "name": "启用插入内容工具", - "description": "允许 Roo 在特定行号插入内容,无需处理差异。" + "name": "使用实验性插入内容工具", + "description": "启用实验性插入内容工具,允许 Roo 在特定行号插入内容,无需创建差异。" }, "POWER_STEERING": { - "name": "启用增强导向模式", - "description": "开启后,Roo 将更频繁地向模型推送当前模式定义的详细信息,从而强化对角色设定和自定义指令的遵循力度。注意:此模式会提升每条消息的 token 消耗量。" + "name": "使用实验性“增强导向”模式", + "description": "启用后,Roo 将更频繁地提醒模型其当前模式定义的详细信息。这将导致更严格地遵守角色定义和自定义指令,但每条消息将使用更多 Token。" }, "MULTI_SEARCH_AND_REPLACE": { - "name": "允许批量搜索和替换", - "description": "启用后,Roo 将尝试在一个请求中进行批量搜索和替换。" + "name": "使用实验性多块差异工具", + "description": "启用后,Roo 将使用多块差异工具。这将尝试在一个请求中更新文件中的多个代码块。" + }, + "MARKETPLACE": { + "name": "在 Roo Code 中启用应用商店", + "description": "启用后,Roo 将能够从应用商店安装和管理项目。", + "warning": "应用商店尚未启用。如果您想成为早期采用者,请在实验性设置中启用它。" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-TW/marketplace.json b/webview-ui/src/i18n/locales/zh-TW/marketplace.json index cb1150b252..8d5d590415 100644 --- a/webview-ui/src/i18n/locales/zh-TW/marketplace.json +++ b/webview-ui/src/i18n/locales/zh-TW/marketplace.json @@ -65,7 +65,8 @@ "removeProject": "移除", "removeGlobal": "移除 (全域)", "viewSource": "檢視", - "viewOnSource": "在 {{source}} 上檢視" + "viewOnSource": "在 {{source}} 上檢視", + "noWorkspaceTooltip": "開啟工作區以安裝市集項目" } }, "installProjectTooltip": "專案安裝", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index b20b111155..8e86b9e226 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -430,6 +430,11 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "使用實驗性多區塊差異比對工具", "description": "啟用後,Roo 將使用多區塊差異比對工具,嘗試在單一請求中更新檔案內的多個程式碼區塊。" + }, + "MARKETPLACE": { + "name": "在 Roo Code 中啟用 Marketplace", + "description": "啟用後,Roo 將能夠從 Marketplace 安裝和管理項目。", + "warning": "Marketplace 尚未啟用。如果您想成為早期採用者,請在實驗性設定中啟用它。" } }, "promptCaching": {