-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: auto-switch to imported mode after successful import #8521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
a2792d1
2bdaff0
61d746e
90ab20b
bce52d6
34134f9
6fe074d
3e3d324
5183aec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,7 @@ interface ExportResult { | |
|
|
||
| interface ImportResult { | ||
| success: boolean | ||
| slug?: string | ||
| error?: string | ||
| } | ||
|
|
||
|
|
@@ -411,7 +412,7 @@ export class CustomModesManager { | |
| const errorMessage = `Invalid mode configuration: ${errorMessages}` | ||
| logger.error("Mode validation failed", { slug, errors: validationResult.error.errors }) | ||
| vscode.window.showErrorMessage(t("common:customModes.errors.updateFailed", { error: errorMessage })) | ||
| return | ||
| throw new Error(errorMessage) | ||
| } | ||
|
|
||
| const isProjectMode = config.source === "project" | ||
|
|
@@ -457,6 +458,7 @@ export class CustomModesManager { | |
| const errorMessage = error instanceof Error ? error.message : String(error) | ||
| logger.error("Failed to update custom mode", { slug, error: errorMessage }) | ||
| vscode.window.showErrorMessage(t("common:customModes.errors.updateFailed", { error: errorMessage })) | ||
| throw error | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -989,7 +991,8 @@ export class CustomModesManager { | |
| // Refresh the modes after import | ||
| await this.refreshMergedState() | ||
heyseth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return { success: true } | ||
| // Return the imported mode's slug so the UI can activate it | ||
| return { success: true, slug: importData.customModes[0]?.slug } | ||
heyseth marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Multi-mode import behavior: this returns only the first imported mode's slug ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What behavior do we want from Roo when the user imports a modes.yaml file containing multiple modes? Personally I think auto-selecting the first mode from the list is fine (this is the current behavior in my implementation). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thoughts? @hannesrudolph
heyseth marked this conversation as resolved.
Show resolved
Hide resolved
heyseth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : String(error) | ||
| logger.error("Failed to import mode with rules", { error: errorMessage }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { render, screen, waitFor } from "@/utils/test-utils" | ||
| import ModesView from "../ModesView" | ||
| import { ExtensionStateContext } from "@src/context/ExtensionStateContext" | ||
| import { vscode } from "@src/utils/vscode" | ||
|
|
||
| vitest.mock("@src/utils/vscode", () => ({ | ||
| vscode: { | ||
| postMessage: vitest.fn(), | ||
| }, | ||
| })) | ||
|
|
||
| const baseState = { | ||
| customModePrompts: {}, | ||
| listApiConfigMeta: [], | ||
| enhancementApiConfigId: "", | ||
| setEnhancementApiConfigId: vitest.fn(), | ||
| mode: "code", | ||
| customModes: [], | ||
| customSupportPrompts: [], | ||
| currentApiConfigName: "", | ||
| customInstructions: "", | ||
| setCustomInstructions: vitest.fn(), | ||
| } | ||
|
|
||
| describe("ModesView - auto switch after import", () => { | ||
| beforeEach(() => { | ||
| vitest.clearAllMocks() | ||
| }) | ||
|
|
||
| it("switches to imported mode when import succeeds and slug is provided", async () => { | ||
heyseth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const importedMode = { | ||
| slug: "imported-mode", | ||
| name: "Imported Mode", | ||
| roleDefinition: "Role", | ||
| groups: ["read"] as const, | ||
| source: "global" as const, | ||
| } | ||
|
|
||
| render( | ||
| <ExtensionStateContext.Provider value={{ ...baseState, customModes: [importedMode] } as any}> | ||
| <ModesView onDone={vitest.fn()} /> | ||
| </ExtensionStateContext.Provider>, | ||
| ) | ||
|
|
||
| const trigger = screen.getByTestId("mode-select-trigger") | ||
| expect(trigger).toHaveTextContent("Code") | ||
|
|
||
| // Simulate extension sending successful import result with slug | ||
| window.dispatchEvent( | ||
| new MessageEvent("message", { | ||
| data: { type: "importModeResult", success: true, slug: "imported-mode" }, | ||
| }), | ||
| ) | ||
|
|
||
| // Backend switch message sent | ||
| await waitFor(() => { | ||
| expect(vscode.postMessage).toHaveBeenCalledWith({ type: "mode", text: "imported-mode" }) | ||
| }) | ||
|
|
||
| // UI reflects new mode selection | ||
| await waitFor(() => { | ||
| expect(trigger).toHaveTextContent("Imported Mode") | ||
| }) | ||
| }) | ||
|
|
||
| it("does not switch when import fails or slug missing", async () => { | ||
| render( | ||
| <ExtensionStateContext.Provider value={{ ...baseState } as any}> | ||
| <ModesView onDone={vitest.fn()} /> | ||
| </ExtensionStateContext.Provider>, | ||
| ) | ||
|
|
||
| const trigger = screen.getByTestId("mode-select-trigger") | ||
| expect(trigger).toHaveTextContent("Code") | ||
|
|
||
| // Import failure | ||
| window.dispatchEvent( | ||
| new MessageEvent("message", { data: { type: "importModeResult", success: false, error: "x" } }), | ||
| ) | ||
|
|
||
| await waitFor(() => { | ||
| expect(vscode.postMessage).not.toHaveBeenCalledWith({ type: "mode", text: expect.any(String) }) | ||
| }) | ||
| expect(trigger).toHaveTextContent("Code") | ||
|
|
||
| // Success but no slug provided | ||
| window.dispatchEvent(new MessageEvent("message", { data: { type: "importModeResult", success: true } })) | ||
|
|
||
| await waitFor(() => { | ||
| expect(vscode.postMessage).not.toHaveBeenCalledWith({ type: "mode", text: expect.any(String) }) | ||
| }) | ||
| expect(trigger).toHaveTextContent("Code") | ||
| }) | ||
| }) | ||
Uh oh!
There was an error while loading. Please reload this page.