/** * Web pairing screen — ticket #13, lot F2. * * Shown by {@link WebApp} when the client is not paired. The user types the code * the server printed at first launch; on submit we `POST /api/pair {code}` via * the {@link WebSession}. On success the server sets the HttpOnly session cookie * and we advance to the workspace; a wrong code shows a clear message. * * Transport-neutral at the component seam: it talks to the injected * {@link WebSession}, never to `@tauri-apps/api` (the CI guard `no-direct-invoke` * stays green). Pure web-only UI — desktop never mounts it. */ import { useState, type FormEvent } from "react"; import type { GatewayError } from "@/domain"; import { Button, Field, Input, Panel } from "@/shared"; import type { WebSession } from "@/adapters/http"; interface PairingScreenProps { /** The shared web session performing the `POST /api/pair` handshake. */ session: WebSession; /** Called once pairing succeeds (cookie set) so the app can advance. */ onPaired: () => void; } function describe(e: unknown): string { if (e && typeof e === "object" && "message" in e) { return String((e as GatewayError).message); } return String(e); } export function PairingScreen({ session, onPaired }: PairingScreenProps) { const [code, setCode] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); async function submit(e: FormEvent): Promise { e.preventDefault(); const trimmed = code.trim(); if (!trimmed || busy) return; setBusy(true); setError(null); try { await session.pair(trimmed); onPaired(); } catch (err) { setError(describe(err)); } finally { setBusy(false); } } return (

Appairer cet appareil

Saisissez le code affiché par le serveur IdeA pour connecter ce navigateur.

{({ id, describedBy }) => ( setCode(e.target.value)} placeholder="p. ex. 4821-93" autoFocus autoComplete="one-time-code" // #69 — phones: summon the numeric-ish keypad for a pairing code // and keep the OS from "helpfully" capitalising/correcting it. inputMode="numeric" autoCapitalize="off" autoCorrect="off" spellCheck={false} disabled={busy} invalid={!!error} /> )} {error && (

{error}

)}
); }