wrap chain source, rescan and wallet-rename fields in real forms

three input sites lived in a div instead of a form, so pressing enter did
nothing and the button was the only way in. chain source url, rescan search
depth, and the inline wallet-rename field all get a <form onSubmit>, with
every non-submit button in each marked type="button" (the rescan card's
reset button was previously untyped, which would have submitted the form
once wrapped).

CoinsView.tsx has no text inputs (checkbox + buttons only) and
SeedPhraseInput.tsx deliberately hijacks enter to advance between word boxes
rather than submit, so both are left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 10:42:35 +00:00
co-authored by Claude Opus 5
parent 00ea1acf75
commit 302d8b48c7
3 changed files with 23 additions and 17 deletions
+8 -8
View File
@@ -80,7 +80,9 @@ export const ChainSourceCard = () => {
const busy = save.isPending || forget.isPending; const busy = save.isPending || forget.isPending;
const meta = KINDS[kind]; const meta = KINDS[kind];
const submit = async () => { const submit = async (ev?: React.FormEvent) => {
ev?.preventDefault();
if (!trimmed || !dirty || busy) return;
setError(null); setError(null);
setHeight(null); setHeight(null);
try { try {
@@ -111,7 +113,8 @@ export const ChainSourceCard = () => {
}; };
return ( return (
<section className="max-w-2xl rounded-xl border border-border p-4"> // Typing a URL and pressing return is what anyone does; a div full of inputs swallows it silently.
<form onSubmit={submit} className="max-w-2xl rounded-xl border border-border p-4">
<h3 className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">Chain source</h3> <h3 className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">Chain source</h3>
<p className="mb-3 text-xs text-muted-foreground"> <p className="mb-3 text-xs text-muted-foreground">
Where on-chain wallets read balances and history. It sees every address this wallet asks about, so your own node Where on-chain wallets read balances and history. It sees every address this wallet asks about, so your own node
@@ -144,9 +147,6 @@ export const ChainSourceCard = () => {
<Input <Input
value={url} value={url}
onChange={(ev) => setUrl(ev.target.value)} onChange={(ev) => setUrl(ev.target.value)}
onKeyDown={(ev) => {
if (ev.key === 'Enter' && trimmed && dirty && !busy) void submit();
}}
placeholder={meta.placeholder} placeholder={meta.placeholder}
disabled={isLoading || busy} disabled={isLoading || busy}
className="h-8 font-mono text-xs" className="h-8 font-mono text-xs"
@@ -155,12 +155,12 @@ export const ChainSourceCard = () => {
spellCheck={false} spellCheck={false}
/> />
<div className="flex shrink-0 gap-2"> <div className="flex shrink-0 gap-2">
<Button size="sm" onClick={submit} disabled={!trimmed || !dirty || busy}> <Button type="submit" size="sm" disabled={!trimmed || !dirty || busy}>
{save.isPending ? <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" /> : null} {save.isPending ? <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" /> : null}
{save.isPending ? 'Connecting…' : 'Save & connect'} {save.isPending ? 'Connecting…' : 'Save & connect'}
</Button> </Button>
{data?.custom && ( {data?.custom && (
<Button size="sm" variant="outline" onClick={reset} disabled={busy} title="Back to the public default"> <Button type="button" size="sm" variant="outline" onClick={reset} disabled={busy} title="Back to the public default">
<RotateCcw className="mr-1.5 h-3.5 w-3.5" /> <RotateCcw className="mr-1.5 h-3.5 w-3.5" />
Reset Reset
</Button> </Button>
@@ -188,6 +188,6 @@ export const ChainSourceCard = () => {
: `Using the built-in default (${data?.defaultUrl ?? '—'}), which rate-limits and can see every address you look up.`}{' '} : `Using the built-in default (${data?.defaultUrl ?? '—'}), which rate-limits and can see every address you look up.`}{' '}
{meta.hint} {meta.hint}
</p> </p>
</section> </form>
); );
}; };
+9 -3
View File
@@ -37,8 +37,14 @@ export const RescanCard = ({ walletId }: RescanCardProps) => {
const parsed = Number(gapLimit); const parsed = Number(gapLimit);
const depth = gapLimit.trim() && Number.isInteger(parsed) && parsed > 0 ? parsed : null; const depth = gapLimit.trim() && Number.isInteger(parsed) && parsed > 0 ? parsed : null;
const submit = (ev?: React.FormEvent) => {
ev?.preventDefault();
if (running || rescan.isPending) return;
rescan.mutate(depth ? { gapLimit: depth } : {});
};
return ( return (
<section className="max-w-2xl rounded-xl border border-border p-4"> <form onSubmit={submit} className="max-w-2xl rounded-xl border border-border p-4">
<h3 className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground"> <h3 className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
Find missing coins Find missing coins
</h3> </h3>
@@ -50,9 +56,9 @@ export const RescanCard = ({ walletId }: RescanCardProps) => {
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<Button <Button
type="submit"
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => rescan.mutate(depth ? { gapLimit: depth } : {})}
disabled={running || rescan.isPending} disabled={running || rescan.isPending}
> >
{running || rescan.isPending ? ( {running || rescan.isPending ? (
@@ -82,7 +88,7 @@ export const RescanCard = ({ walletId }: RescanCardProps) => {
</div> </div>
{state && <Status state={state} />} {state && <Status state={state} />}
</section> </form>
); );
}; };
+6 -6
View File
@@ -206,7 +206,8 @@ const WalletNameField = ({ wallet, onSave, isPending }: WalletNameFieldProps) =>
const trimmed = draft.trim(); const trimmed = draft.trim();
const dirty = trimmed !== wallet.name; const dirty = trimmed !== wallet.name;
const commit = async () => { const commit = async (ev?: React.FormEvent) => {
ev?.preventDefault();
if (!trimmed || !dirty) { if (!trimmed || !dirty) {
setDraft(wallet.name); setDraft(wallet.name);
setEditing(false); setEditing(false);
@@ -242,7 +243,7 @@ const WalletNameField = ({ wallet, onSave, isPending }: WalletNameFieldProps) =>
} }
return ( return (
<div className="flex min-w-0 items-center gap-1.5"> <form onSubmit={commit} className="flex min-w-0 items-center gap-1.5">
<Input <Input
ref={inputRef} ref={inputRef}
autoFocus autoFocus
@@ -251,7 +252,6 @@ const WalletNameField = ({ wallet, onSave, isPending }: WalletNameFieldProps) =>
disabled={isPending} disabled={isPending}
onChange={(ev) => setDraft(ev.target.value)} onChange={(ev) => setDraft(ev.target.value)}
onKeyDown={(ev) => { onKeyDown={(ev) => {
if (ev.key === 'Enter') void commit();
if (ev.key === 'Escape') cancel(); if (ev.key === 'Escape') cancel();
}} }}
className="h-7 max-w-56 text-sm font-semibold" className="h-7 max-w-56 text-sm font-semibold"
@@ -259,13 +259,13 @@ const WalletNameField = ({ wallet, onSave, isPending }: WalletNameFieldProps) =>
autoComplete="off" autoComplete="off"
spellCheck={false} spellCheck={false}
/> />
<Button size="icon" variant="ghost" className="h-7 w-7" onClick={commit} disabled={isPending || !trimmed}> <Button type="submit" size="icon" variant="ghost" className="h-7 w-7" disabled={isPending || !trimmed}>
<Check className="h-3.5 w-3.5" /> <Check className="h-3.5 w-3.5" />
</Button> </Button>
<Button size="icon" variant="ghost" className="h-7 w-7" onClick={cancel} disabled={isPending}> <Button type="button" size="icon" variant="ghost" className="h-7 w-7" onClick={cancel} disabled={isPending}>
<X className="h-3.5 w-3.5" /> <X className="h-3.5 w-3.5" />
</Button> </Button>
</div> </form>
); );
}; };