// — the `needs_login_idle` / `needs_login_in_progress` // overlay. Ports app.js's `renderNeedsLoginIdle`/`renderLoginInProgress` // as real functional UI, not just a status label: an agent whose // credentials got rotated (or hit `/logout`) has NO other way back // online, so this is load-bearing, not cosmetic. Reuses agent.css's // `.status-needs-login`/`.btn-login`/`.loginform`/`.loginform-reveal` // rules verbatim (same class names as the old markup). // // mara (screenshot review): "reuse the popup component for the login, // the floating separate panels should be one card." The old markup's // `.agent-status-overlay > *` rule boxed every direct-child `

`/ // `

` separately (a fine translation of `el().append()`-per-line, // but not what a hand-authored component should do) — this renders one // `.login-card` instead, styled with the same "elevated surface" look // `Dropdown`'s `.ui-dropdown` already established (`@hive/shared/ // dropdown/Dropdown.css`), not `Dropdown` itself (its `options.map` // render is menu-item-shaped, not a generic content container — not a // fit for a login form + output pane). import { useState } from 'preact/hooks'; import type { SessionView } from '../types.js'; import { postLoginStart, postLoginCode, postLoginCancel } from '../lib/loginAction.js'; import './LoginFlow.css'; function LoginIdle({ onStarted }: { onStarted: () => void }) { const [busy, setBusy] = useState(false); const [error, setError] = useState(null); function submit(e: Event) { e.preventDefault(); setBusy(true); setError(null); postLoginStart().then((r) => { setBusy(false); if (r.ok) onStarted(); else setError(r.detail ?? 'failed'); }); } return ( ); } function LoginInProgress({ session, onDone }: { session: SessionView | null; onDone: () => void }) { const [code, setCode] = useState(''); const [reveal, setReveal] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const finished = session?.finished ?? false; function submitCode(e: Event) { e.preventDefault(); if (!code) return; setBusy(true); setError(null); // Success: the next `/api/state` poll flips `status` back to // `online` (or leaves it `needs_login_in_progress` on a bad code, // with the process's own stderr showing up in `output` below) — // nothing local to update here either way. postLoginCode(code).then((r) => { setBusy(false); if (!r.ok) setError(r.detail ?? 'failed'); }); } function cancel(e: Event) { e.preventDefault(); setBusy(true); postLoginCancel().then(() => { setBusy(false); onDone(); }); } return ( ); } export function LoginFlow({ status, session, onRefresh, }: { status: 'needs_login_idle' | 'needs_login_in_progress'; session: SessionView | null; onRefresh: () => void; }) { return (
{status === 'needs_login_idle' ? ( ) : ( )}
); }