import { useEffect, useState } from "react"; import { api } from "../api/client"; import CopyButton from "../components/CopyButton"; import { generateServerName, generateUsername, generatePassword, passwordEntropyBits, passwordStrengthLabel, type ServerNameTheme, type UsernameOptions, type PasswordOptions, } from "../utils/generators"; export default function Generator() { // ─── Server name ─────────────────────────────────────────────────────── const [existingServerNames, setExistingServerNames] = useState([]); const [theme, setTheme] = useState("mixed"); const [serverName, setServerName] = useState(""); useEffect(() => { api.servers .list() .then((res) => setExistingServerNames(res.servers.map((s) => s.name))) .catch(() => { // Best-effort — collision-avoidance just won't have anything to check against. }); }, []); function rollServerName() { setServerName(generateServerName(theme, existingServerNames)); } // ─── Username ──────────────────────────────────────────────────────── const [usernameOptions, setUsernameOptions] = useState({ separator: "-", includeNumber: true }); const [username, setUsername] = useState(""); function rollUsername() { setUsername(generateUsername(usernameOptions)); } // ─── Password ──────────────────────────────────────────────────────── const [passwordOptions, setPasswordOptions] = useState({ length: 20, uppercase: true, lowercase: true, numbers: true, symbols: true, excludeAmbiguous: false, }); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); function rollPassword() { setPassword(generatePassword(passwordOptions)); } const noCharsetSelected = !passwordOptions.uppercase && !passwordOptions.lowercase && !passwordOptions.numbers && !passwordOptions.symbols; const entropy = passwordEntropyBits(passwordOptions); const strength = passwordStrengthLabel(entropy); return ( <>

Generator

Everything here is generated locally in your browser — nothing is sent to or stored on the server.

Server name

Picks from Swedish girl names and Disney characters, avoiding names already in use.

{serverName && }

Username

An adjective + animal, optionally with a number — easy to say, hard to collide.

{username && }

Password

setPasswordOptions((o) => ({ ...o, length: Number(e.target.value) }))} />
{( [ ["uppercase", "A–Z"], ["lowercase", "a–z"], ["numbers", "0–9"], ["symbols", "!@#$…"], ] as const ).map(([key, label]) => (
))}
{noCharsetSelected &&
Pick at least one character type.
}
{password && ( <> )}
{password && (
{strength.label} (~{entropy} bits of entropy)
)}
); }