From f3fb07e41f66e327cf689949a5c155d04109f66b Mon Sep 17 00:00:00 2001 From: cynarAI <155274210+cynarAI@users.noreply.github.com> Date: Mon, 5 Oct 2026 06:16:36 +0200 Subject: [PATCH] fix(web): keep focus in place after removing a media API key (#148) Remove unmounts itself once clicked, so focus fell to the body and the settings dialog pulled it back to its popup a frame later. Whatever field the user (or a test) focused in that frame lost focus again; in CI the cleared transcription URL was still sent on the next save. Remove now moves focus to the key field before it disappears. Claude-Session: https://claude.ai/code/session_01UfVneQG6488JGLiHYAc9RZ Co-authored-by: Claude --- apps/web/e2e/media-settings.spec.ts | 1 + apps/web/src/components/MediaSettings.tsx | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/web/e2e/media-settings.spec.ts b/apps/web/e2e/media-settings.spec.ts index 4f50ad7..bf606b7 100644 --- a/apps/web/e2e/media-settings.spec.ts +++ b/apps/web/e2e/media-settings.spec.ts @@ -105,6 +105,7 @@ test("the deployment owner points media at their own servers", async ({ page }, await expect(comfyKey).toHaveAttribute("placeholder", "Saved"); await comfy.getByRole("button", { name: "Remove", exact: true }).click(); + await expect(comfyKey).toBeFocused(); await transcription.getByLabel("Server URL", { exact: true }).fill(""); await panel.getByRole("button", { name: "Save", exact: true }).click(); await expect.poll(() => saved.length).toBe(2); diff --git a/apps/web/src/components/MediaSettings.tsx b/apps/web/src/components/MediaSettings.tsx index ad93abb..2ff24d2 100644 --- a/apps/web/src/components/MediaSettings.tsx +++ b/apps/web/src/components/MediaSettings.tsx @@ -1,7 +1,7 @@ import { Trans, useLingui } from "@lingui/react/macro"; import type { MediaSettingsUpdate, MediaSettingsView } from "@rakazo/contracts"; import { Button, Field, FieldLabel, Input, Textarea } from "@rakazo/ui-web"; -import { useEffect, useId, useState } from "react"; +import { useEffect, useId, useRef, useState } from "react"; import { rpc } from "../lib/rpc"; import { SuccessPop } from "./ai/primitives"; @@ -28,6 +28,7 @@ function ApiKeyField({ }) { const { t } = useLingui(); const id = useId(); + const inputRef = useRef(null); const showSaved = saved && !draft.remove; return ( @@ -41,13 +42,19 @@ function ApiKeyField({ variant="ghost" size="xs" disabled={disabled} - onClick={() => onChange({ value: "", remove: true })} + onClick={() => { + // The button unmounts once clicked; hand focus to the field so the dialog + // does not pull it back to its popup a frame later. + inputRef.current?.focus(); + onChange({ value: "", remove: true }); + }} > Remove ) : null}