import { createSignal, onMount, Show } from "solid-js"; import { generatePassword } from "../../shared/generate"; import type { PasswordPolicy } from "../../shared/types"; import { sendMessage, triggerPipPulse, triggerRing } from "../App"; interface GeneratorProps { onBack: () => void; onUse: (password: string) => void; policy?: PasswordPolicy; } export function GeneratorView(props: GeneratorProps) { const [password, setPassword] = createSignal(""); const [length, setLength] = createSignal(24); const [upper, setUpper] = createSignal(true); const [lower, setLower] = createSignal(true); const [numbers, setNumbers] = createSignal(true); const [symbols, setSymbols] = createSignal(true); const [copied, setCopied] = createSignal(false); const [detectedPolicy, setDetectedPolicy] = createSignal(null); function initFromPolicy(policy: PasswordPolicy) { if (policy.minLength) setLength(Math.max(length(), policy.minLength)); if (policy.maxLength) setLength(Math.min(length(), policy.maxLength)); if (policy.forbidsSymbols) setSymbols(false); if (policy.requiresSymbols) setSymbols(true); } onMount(async () => { if (props.policy) { setDetectedPolicy(props.policy); initFromPolicy(props.policy); } else { const res = await sendMessage({ type: "GET_SITE_PASSWORD_POLICY" }); const data = (res as { ok: boolean; data?: PasswordPolicy }).data; if (data && Object.keys(data).length > 0) { setDetectedPolicy(data); initFromPolicy(data); } } regenerate(); }); function regenerate(nextLength = length()) { setPassword( generatePassword({ length: nextLength, includeUppercase: upper(), includeLowercase: lower(), includeNumbers: numbers(), includeSymbols: symbols(), }) ); } async function copyPassword(button?: HTMLButtonElement) { const response = await sendMessage({ type: "COPY", payload: { text: password(), label: "password" }, }); if ((response as { ok: boolean }).ok) { triggerRing(); triggerPipPulse(); setCopied(true); if (button) { button.classList.remove("is-retreat"); const drain = document.createElement("span"); drain.className = "peach-copy-drain"; const bar = document.createElement("span"); bar.className = "peach-copy-drain-bar"; drain.appendChild(bar); button.appendChild(drain); window.setTimeout(() => { setCopied(false); button.classList.remove("is-copied"); button.classList.add("is-retreat"); drain.remove(); window.setTimeout(() => button.classList.remove("is-retreat"), 160); }, 1400); return; } window.setTimeout(() => setCopied(false), 1400); } } return (
password generator
site requirements detected

{password()}

length {length()}
{ const nextLength = parseInt((e.currentTarget as HTMLInputElement).value, 10); setLength(nextLength); regenerate(nextLength); }} />
{[ ["Uppercase A-Z", upper, setUpper], ["Lowercase a-z", lower, setLower], ["Numbers 0-9", numbers, setNumbers], ["Symbols !@#$", symbols, setSymbols], ].map(([label, getter, setter]) => (
{label as string}
))}
); }