diff --git a/webview-ui/src/components/settings/ImageGenerationSettings.tsx b/webview-ui/src/components/settings/ImageGenerationSettings.tsx
index 667a31d579..feb56ac5b5 100644
--- a/webview-ui/src/components/settings/ImageGenerationSettings.tsx
+++ b/webview-ui/src/components/settings/ImageGenerationSettings.tsx
@@ -36,14 +36,32 @@ export const ImageGenerationSettings = ({
imageGenerationSettings.selectedModel || IMAGE_GENERATION_MODELS[0].value,
)
- // Update parent state when local state changes
+ // Update local state when apiConfiguration changes (e.g., when switching profiles)
useEffect(() => {
+ setOpenRouterApiKey(imageGenerationSettings.openRouterApiKey || "")
+ setSelectedModel(imageGenerationSettings.selectedModel || IMAGE_GENERATION_MODELS[0].value)
+ }, [imageGenerationSettings.openRouterApiKey, imageGenerationSettings.selectedModel])
+
+ // Helper function to update settings
+ const updateSettings = (newApiKey: string, newModel: string) => {
const newSettings = {
- openRouterApiKey,
- selectedModel,
+ openRouterApiKey: newApiKey,
+ selectedModel: newModel,
}
- setApiConfigurationField("openRouterImageGenerationSettings", newSettings)
- }, [openRouterApiKey, selectedModel, setApiConfigurationField])
+ setApiConfigurationField("openRouterImageGenerationSettings", newSettings, true)
+ }
+
+ // Handle API key changes
+ const handleApiKeyChange = (value: string) => {
+ setOpenRouterApiKey(value)
+ updateSettings(value, selectedModel)
+ }
+
+ // Handle model selection changes
+ const handleModelChange = (value: string) => {
+ setSelectedModel(value)
+ updateSettings(openRouterApiKey, value)
+ }
return (
@@ -67,7 +85,7 @@ export const ImageGenerationSettings = ({
setOpenRouterApiKey(e.target.value)}
+ onInput={(e: any) => handleApiKeyChange(e.target.value)}
placeholder={t("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder")}
className="w-full"
type="password"
@@ -91,7 +109,7 @@ export const ImageGenerationSettings = ({
setSelectedModel(e.target.value)}
+ onChange={(e: any) => handleModelChange(e.target.value)}
className="w-full">
{IMAGE_GENERATION_MODELS.map((model) => (
diff --git a/webview-ui/src/components/settings/__tests__/ImageGenerationSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/ImageGenerationSettings.spec.tsx
new file mode 100644
index 0000000000..ef3808c20b
--- /dev/null
+++ b/webview-ui/src/components/settings/__tests__/ImageGenerationSettings.spec.tsx
@@ -0,0 +1,94 @@
+import { render, fireEvent } from "@testing-library/react"
+import { vi } from "vitest"
+import { ImageGenerationSettings } from "../ImageGenerationSettings"
+import type { ProviderSettings } from "@roo-code/types"
+
+// Mock the translation context
+vi.mock("@/i18n/TranslationContext", () => ({
+ useAppTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+describe("ImageGenerationSettings", () => {
+ const mockSetApiConfigurationField = vi.fn()
+ const mockOnChange = vi.fn()
+
+ const defaultProps = {
+ enabled: false,
+ onChange: mockOnChange,
+ apiConfiguration: {} as ProviderSettings,
+ setApiConfigurationField: mockSetApiConfigurationField,
+ }
+
+ beforeEach(() => {
+ vi.clearAllMocks()
+ })
+
+ describe("Initial Mount Behavior", () => {
+ it("should not call setApiConfigurationField on initial mount with empty configuration", () => {
+ render()
+
+ // Should NOT call setApiConfigurationField on initial mount to prevent dirty state
+ expect(mockSetApiConfigurationField).not.toHaveBeenCalled()
+ })
+
+ it("should not call setApiConfigurationField on initial mount with existing configuration", () => {
+ const apiConfiguration = {
+ openRouterImageGenerationSettings: {
+ openRouterApiKey: "existing-key",
+ selectedModel: "google/gemini-2.5-flash-image-preview:free",
+ },
+ } as ProviderSettings
+
+ render()
+
+ // Should NOT call setApiConfigurationField on initial mount to prevent dirty state
+ expect(mockSetApiConfigurationField).not.toHaveBeenCalled()
+ })
+ })
+
+ describe("User Interaction Behavior", () => {
+ it("should call setApiConfigurationField when user changes API key", async () => {
+ const { getByPlaceholderText } = render()
+
+ const apiKeyInput = getByPlaceholderText(
+ "settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder",
+ )
+
+ // Simulate user typing
+ fireEvent.input(apiKeyInput, { target: { value: "new-api-key" } })
+
+ // Should call setApiConfigurationField with isUserAction=true
+ expect(mockSetApiConfigurationField).toHaveBeenCalledWith(
+ "openRouterImageGenerationSettings",
+ {
+ openRouterApiKey: "new-api-key",
+ selectedModel: "google/gemini-2.5-flash-image-preview",
+ },
+ true, // This should be true for user actions
+ )
+ })
+
+ // Note: Testing VSCode dropdown components is complex due to their custom nature
+ // The key functionality (not marking as dirty on initial mount) is already tested above
+ })
+
+ describe("Conditional Rendering", () => {
+ it("should render input fields when enabled is true", () => {
+ const { getByPlaceholderText } = render()
+
+ expect(
+ getByPlaceholderText("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder"),
+ ).toBeInTheDocument()
+ })
+
+ it("should not render input fields when enabled is false", () => {
+ const { queryByPlaceholderText } = render()
+
+ expect(
+ queryByPlaceholderText("settings:experimental.IMAGE_GENERATION.openRouterApiKeyPlaceholder"),
+ ).not.toBeInTheDocument()
+ })
+ })
+})