import { BadgeCheck, ShieldAlert, ShieldQuestion } from "lucide-react";
import { type FC, useEffect, useState } from "react";
import type { StoreEntry } from "@/api/store";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { m } from "@/paraglide/messages";
// Friction proportional to trust. The three install paths deliberately do NOT share a confirm
// button: a verified install is one click on a plain dialog, an external one makes you read that
// unom didn't review the code and names who did curate it, and a raw package spec makes you retype
// the spec and tick a box. The last one is never reachable by accident — no card, no button on a
// listing, only the small footer link on Browse.
/**
* Tiers 1 and 2: install a curated catalog entry. Verified entries get an ordinary confirm; an
* entry from an operator-added source gets the warning treatment and names its source, because
* "pinned and integrity-checked" is not the same claim as "reviewed".
*/
export const InstallDialog: FC<{
/** The entry being confirmed, or null when the dialog is closed. */
entry: StoreEntry | null;
onCancel: () => void;
onConfirm: (entry: StoreEntry) => void;
isPending: boolean;
}> = ({ entry, onCancel, onConfirm, isPending }) => {
const external = entry?.tier === "external";
return (
);
};
/**
* Tier 3: install a raw package spec. No catalog, no review, no pinning — so the dialog spells out
* exactly what that means and asks for two independent confirmations (retype the spec, tick the
* box) before it will even enable its button.
*/
export const SpecInstallDialog: FC<{
open: boolean;
onCancel: () => void;
onConfirm: (spec: string, password: string) => void;
isPending: boolean;
/** Set when the BFF rejected the password (401), so the dialog can say so and stay open. */
wrongPassword?: boolean;
}> = ({ open, onCancel, onConfirm, isPending, wrongPassword }) => {
const [spec, setSpec] = useState("");
const [echo, setEcho] = useState("");
const [accepted, setAccepted] = useState(false);
const [password, setPassword] = useState("");
// Every confirmation is cleared on exit, cancel AND confirm alike: reopening this dialog must
// never find it pre-armed with the last spec, a ticked box, or a typed password.
//
// Clearing hangs off `open` rather than off the two exit paths, because only one of them runs in
// this component: cancel goes through `onCancel`, but SUCCESS is the parent flipping `open`, and
// the dialog stays mounted either way. Setter identities are stable, so the effect needs no other
// dependency — a `reset()` helper in the list would be a new function every render.
useEffect(() => {
if (open) return;
setSpec("");
setEcho("");
setAccepted(false);
setPassword("");
}, [open]);
const wanted = spec.trim();
// Every gate must pass: the retyped spec matches exactly, the box is ticked, and the console
// password is re-entered (the BFF verifies it — a session cookie alone must not run new code).
const ready =
wanted.length > 0 &&
echo.trim() === wanted &&
accepted &&
password.length > 0;
return (
);
};