Watch
0
0
Fork
You've already forked hyperhive
0

swarm ui: a stored claude login closes the dialog and shows a toast

The login dialog stayed open with a success line after the code was
accepted, though nothing is left to do in it. It now closes and a toast
says the login is stored. The toast is a new swarm-ui primitive: a short
note raised with showToast, rendered by a ToastHost at the app root, gone
after a few seconds or on click.
This commit is contained in:
iris 2026-11-09 20:50:32 +01:00
commit 73f875eab8
3 changed files with 211 additions and 15 deletions

View file

@ -1,4 +0,0 @@
/* <ClaudeLoginDialog> */
.claude-login-ok {
margin-block: 1em;
}

View file

@ -13,8 +13,8 @@ import { Button } from "../../ui/button/Button.js";
import { FormStack } from "../../ui/form-stack/FormStack.js";
import { Dialog } from "../../ui/dialog/Dialog.js";
import { ExternalLink } from "../../ui/external-link/ExternalLink.js";
import { Hint } from "../../ui/hint/Hint.js";
import { Panel } from "../../ui/panel/Panel.js";
import { showToast } from "../../ui/toast/toasts.js";
import { TextField } from "../../ui/text-field/TextField.js";
import {
afterCode,
@ -25,9 +25,9 @@ import {
leavesLoginRunning,
pastedCode,
startOutlivedDialog,
storedMessage,
type LoginStep,
} from "./ClaudeLoginSteps.js";
import "./ClaudeLoginDialog.css";
async function problemOf(r: Response): Promise<ProblemDetails | null> {
return r.ok ? null : await readApiError(r);
@ -80,16 +80,24 @@ export function ClaudeLoginDialog({
if (step.step !== "code" || pasted === null) return;
const { url } = step;
setStep({ step: "submitting", url });
let next: LoginStep;
try {
const r = await fetch(claudeLoginPath(agent, true), {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ code: pasted }),
});
setStep(afterCode(await problemOf(r)));
next = afterCode(await problemOf(r));
} catch (err) {
setStep(afterCode({ detail: String(err) }));
next = afterCode({ detail: String(err) });
}
if (next.step === "stored") {
// Nothing is left to do in the dialog: close it and say so in a toast.
onClose();
showToast(storedMessage(agent), "ok");
return;
}
setStep(next);
setCode("");
}
@ -122,12 +130,6 @@ export function ClaudeLoginDialog({
</FormStack>
</>
)}
{step.step === "stored" && (
<Hint tone="ok" class="claude-login-ok">
Stored. <strong>{agent}</strong> picks the login up within a minute
and resumes.
</Hint>
)}
{step.step === "failed" && (
<ApiErrorPanel
context="the claude login failed; close and start again"

View file

@ -28,6 +28,11 @@ export function afterCode(problem: ProblemDetails | null): LoginStep {
return problem === null ? { step: "stored" } : { step: "failed", problem };
}
// What the toast says once the login is stored.
export function storedMessage(agent: string): string {
return `Login stored. ${agent} picks it up within a minute and resumes.`;
}
// Whether closing the dialog leaves a login running that should be
// stopped.
export function leavesLoginRunning(s: LoginStep): boolean {