···11<script lang="ts">
22- import { coinFlip } from "../scripts/chance";
33- import {
44- prefix as getPrefix,
55- name as createName,
66- suffix as getSuffix,
77- } from "../scripts/nicknames";
22+ import Seeded from "./WmNicknameSeeded.svelte";
33+ import Randomized from "./WmNicknameRandomized.svelte";
8499- /**
1010- * Creates a randomized name getter that has a chance to return null.
1111- * @param maker The callable that gets a randomized name.
1212- * @param nullChance If set, this is the chance that the result will be null. Defaults to a 50/50 chance.
1313- */
1414- const createOptionalNameMaker =
1515- (maker: () => string, nullChance?: number) => () =>
1616- coinFlip(nullChance) ? null : maker();
1717- const createPrefix = createOptionalNameMaker(getPrefix, 0.25);
1818- const createSuffix = createOptionalNameMaker(getSuffix);
55+ type Generator = "seeded" | "randomized";
66+ let generator = $state<Generator>("seeded");
197208 let prefix = $state<string | null>(null);
2121- let name = $state(createName());
99+ let name = $state<undefined | string>("");
2210 let suffix = $state<string | null>(null);
23112424- const setNameDecorators = () => {
2525- suffix = createSuffix();
2626- // NOTE If the suffix is null, guarantee a prefix to ensure that the full nickname
2727- // is unique.
2828- if (suffix == null) {
2929- prefix = getPrefix();
3030- } else {
3131- prefix = createPrefix();
3232- }
3333- };
3434- setNameDecorators();
3535-3636- const onSubmit = (e: SubmitEvent) => {
1212+ const onSubmit = (e: SubmitEvent): void => {
3713 e.preventDefault();
3838- name = createName();
3939- setNameDecorators();
4014 };
4115</script>
42164343-<form id="nickname-form" onsubmit={onSubmit}>
4444- <button type="submit" id="nickname-generate" class="primary"
4545- >(Re)generate</button
4646- >
1717+<form id="form-nickname-generator" onsubmit={onSubmit}>
1818+ <div class="option-input">
1919+ <input
2020+ type="radio"
2121+ id="radio-seeded"
2222+ name="generator"
2323+ value="seeded"
2424+ bind:group={generator}
2525+ />
2626+ <label for="radio-seeded">From ID</label>
2727+ </div>
2828+ <div class="option-input">
2929+ <input
3030+ type="radio"
3131+ id="radio-randomized"
3232+ name="generator"
3333+ value="randomized"
3434+ bind:group={generator}
3535+ />
3636+ <label for="radio-randomized">Randomized</label>
3737+ </div>
4738</form>
3939+{#if generator === "seeded"}
4040+ <Seeded bind:prefix bind:name bind:suffix />
4141+{:else}
4242+ <Randomized bind:prefix bind:name bind:suffix />
4343+{/if}
48444949-<output for="nickname-generate">
5050- <p>
5151- <strong id="nickname"
5252- >{#if prefix != null}<u>{prefix}</u>{" "}{/if}<u>{name}</u
5353- >{#if suffix != null}{" "}<u>{suffix}</u>{/if}</strong
5454- >
5555- </p>
5656-</output>
4545+<p>
4646+ {#if name}
4747+ Your nickname is
4848+ <output for="nickname-generate">
4949+ <strong id="nickname"
5050+ >{#if prefix != null}<u>{prefix}</u>{" "}{/if}<u>{name}</u
5151+ >{#if suffix != null}{" "}<u>{suffix}</u>{/if}</strong
5252+ >
5353+ </output>
5454+ {/if}
5555+</p>
+60
src/components/WmNicknameRandomized.svelte
···11+<script lang="ts">
22+ import { onMount } from "svelte";
33+ import { coinFlip } from "../scripts/chance";
44+ import {
55+ prefix as getPrefix,
66+ name as createName,
77+ suffix as getSuffix,
88+ } from "../scripts/nicknames";
99+1010+ /**
1111+ * Creates a randomized name getter that has a chance to return null.
1212+ * @param maker The callable that gets a randomized name.
1313+ * @param nullChance If set, this is the chance that the result will be null. Defaults to a 50/50 chance.
1414+ */
1515+ const createOptionalNameMaker =
1616+ (maker: () => string, nullChance?: number) => () =>
1717+ coinFlip(nullChance) ? null : maker();
1818+ const createPrefix = createOptionalNameMaker(getPrefix, 0.25);
1919+ const createSuffix = createOptionalNameMaker(getSuffix);
2020+2121+ const initialName = createName();
2222+ const initialSuffix = createSuffix();
2323+ // NOTE If the suffix is null, guarantee a prefix to ensure that the full nickname is
2424+ // unique.
2525+ const initialPrefix = initialSuffix == null ? getPrefix() : createPrefix();
2626+2727+ let {
2828+ prefix = $bindable<string | null>(initialPrefix),
2929+ name = $bindable(initialName),
3030+ suffix = $bindable<string | null>(initialSuffix),
3131+ } = $props();
3232+3333+ const setNameDecorators = () => {
3434+ suffix = createSuffix();
3535+ // NOTE If the suffix is null, guarantee a prefix to ensure that the full nickname
3636+ // is unique.
3737+ if (suffix == null) {
3838+ prefix = getPrefix();
3939+ } else {
4040+ prefix = createPrefix();
4141+ }
4242+ };
4343+ onMount(setNameDecorators);
4444+4545+ const onSubmit = (e: SubmitEvent) => {
4646+ e.preventDefault();
4747+ name = createName();
4848+ setNameDecorators();
4949+ };
5050+</script>
5151+5252+<form id="nickname-form" onsubmit={onSubmit}>
5353+ <p>
5454+ Click the button below to generate your nickname. Feel free to keep clicking
5555+ until you get one you like.
5656+ </p>
5757+ <button type="submit" id="nickname-generate" class="primary"
5858+ >(Re)generate</button
5959+ >
6060+</form>
+64
src/components/WmNicknameSeeded.svelte
···11+<script lang="ts">
22+ import { onMount } from "svelte";
33+ import { prefixes, names, suffixes } from "../scripts/nicknames";
44+ import { uuidV5 } from "../scripts/uuid";
55+66+ let {
77+ prefix = $bindable<string | null>(null),
88+ name = $bindable(""),
99+ suffix = $bindable<string | null>(null),
1010+ } = $props();
1111+ let seed = $state("");
1212+1313+ /**
1414+ * Limits an unbound index to the length of the given array, returning the indexed value.
1515+ */
1616+ const arrGetBound = <T,>(arr: T[], unbound: number): T =>
1717+ arr[unbound % arr.length];
1818+1919+ const setSeed = (value: string): void => {
2020+ seed = value;
2121+ if (value === "") {
2222+ prefix = null;
2323+ name = "";
2424+ suffix = null;
2525+ } else {
2626+ const buffer = uuidV5(value, true);
2727+2828+ // NOTE Naive implementation of transforming a UUID into a single numerical value.
2929+ const hash = buffer.reduce((hash, b) => ((hash << 8) | b) % 0xfffff, 0);
3030+3131+ // NOTE Very simple and naive implementation of chances.
3232+ suffix = hash % 2 === 0 ? null : arrGetBound(suffixes, hash);
3333+ prefix =
3434+ hash % 4 === 0 && suffix != null ? null : arrGetBound(prefixes, hash);
3535+ name = arrGetBound(names, hash);
3636+ }
3737+ };
3838+3939+ const onSubmit = (e: SubmitEvent): void => {
4040+ e.preventDefault();
4141+ };
4242+4343+ // NOTE Reset nickname on mount to clear out the nickname from the randomized version.
4444+ onMount(() => {
4545+ prefix = null;
4646+ name = "";
4747+ suffix = null;
4848+ });
4949+</script>
5050+5151+<form id="nickname-form" onsubmit={onSubmit}>
5252+ <p>
5353+ Enter a <em>public</em> identifier (like your name or a
5454+ <abbr title="Decentralized Identifier">DID</abbr>).
5555+ <em>Don't use a private identifier!</em>
5656+ </p>
5757+ <label for="input-seed">ID</label>
5858+ <input
5959+ type="text"
6060+ id="input-seed"
6161+ name="seed"
6262+ bind:value={() => seed, setSeed}
6363+ />
6464+</form>
+2-2
src/pages/wm-nickname.astro
···77<BaseLayout title={title}>
88 <p>
99 This is a nickname generator inspired by the nicknames of the 108 heroes of
1010- my favorite book, Water Margin. If you're not satisfied with your nickname,
1111- feel free to click the regenerate button.
1010+ my favorite book, Water Margin. Pick a type of nickname generator and then
1111+ generate your nickname!
1212 </p>
1313 <WmNickname client:only="svelte" />
1414</BaseLayout>
···11+/**
22+ * @module uuid UUID helpers.
33+ */
44+import { v5 as uuidV5Impl } from "uuid";
55+66+const namespace = uuidV5Impl("https://www.spenser.black/", uuidV5Impl.URL);
77+88+/**
99+ *
1010+ * @param value The value to use for generating a UUID v5.
1111+ * @param raw Set to `true` to return a `Uint8Array` rather than a `string`.
1212+ */
1313+export function uuidV5(value: string, raw?: false): string;
1414+export function uuidV5(value: string, raw: true): Uint8Array;
1515+export function uuidV5(value: string, raw?: boolean): string | Uint8Array {
1616+ if (raw) {
1717+ const buffer = new Uint8Array(16);
1818+ return uuidV5Impl(value, namespace, buffer);
1919+ }
2020+ return uuidV5Impl(value, namespace);
2121+}