Mirror of my GitHub Pages site www.spenser.black/
0

Configure Feed

Select the types of activity you want to include in your feed.

Add random nickname generator (#9)

authored by

Spenser Black and committed by
GitHub
(Jul 17, 2026, 6:25 PM UTC) 2a2a10bf 0f77f1f9

+260
+1
src/components/Header.astro
··· 7 7 ["Homepage", "/"], 8 8 ["Projects", "/projects/"], 9 9 ["Steamdown", "/steamdown/", { title: "Markdown for Steam" }], 10 + ["Nickname", "/wm-nickname/", { title: "Generate a nickname" }], 10 11 ]; 11 12 --- 12 13
+45
src/components/WmNickname.svelte
··· 1 + <script lang="ts"> 2 + import { coinFlip } from "../scripts/chance"; 3 + import { 4 + prefix as getPrefix, 5 + name as createName, 6 + suffix as getSuffix, 7 + } from "../scripts/nicknames"; 8 + 9 + /** 10 + * Creates a randomized name getter that has a chance to return null. 11 + * @param maker The callable that gets a randomized name. 12 + * @param nullChance If set, this is the chance that the result will be null. Defaults to a 50/50 chance. 13 + */ 14 + const createOptionalNameMaker = 15 + (maker: () => string, nullChance?: number) => () => 16 + coinFlip(nullChance) ? null : maker(); 17 + const createPrefix = createOptionalNameMaker(getPrefix, 0.25); 18 + const createSuffix = createOptionalNameMaker(getSuffix); 19 + 20 + let prefix = $state(createPrefix()); 21 + let name = $state(createName()); 22 + let suffix = $state(createSuffix()); 23 + 24 + const onSubmit = (e: SubmitEvent) => { 25 + e.preventDefault(); 26 + prefix = createPrefix(); 27 + name = createName(); 28 + suffix = createSuffix(); 29 + }; 30 + </script> 31 + 32 + <form id="nickname-form" onsubmit={onSubmit}> 33 + <button type="submit" id="nickname-generate" class="primary" 34 + >(Re)generate</button 35 + > 36 + </form> 37 + 38 + <output for="nickname-generate"> 39 + <p> 40 + <strong id="nickname" 41 + >{#if prefix != null}<u>{prefix}</u>{" "}{/if}<u>{name}</u 42 + >{#if suffix != null}{" "}<u>{suffix}</u>{/if}</strong 43 + > 44 + </p> 45 + </output>
+15
src/pages/wm-nickname.astro
··· 1 + --- 2 + import BaseLayout from "../layouts/BaseLayout.astro"; 3 + import WmNickname from "../components/WmNickname.svelte"; 4 + const title = "Water Margin nickname generator"; 5 + --- 6 + 7 + <BaseLayout title={title}> 8 + <p> 9 + This is a nickname generator inspired by the nicknames of the 108 heroes of 10 + my favorite book, Water Margin. If you're not satisfied with your nickname 11 + (there's a chance you'll get neither a prefix nor a suffix), feel free to 12 + click the regenerate button. 13 + </p> 14 + <WmNickname client:only="svelte" /> 15 + </BaseLayout>
+23
src/scripts/chance.ts
··· 1 + /** 2 + * @module chance Helpers for randomness. 3 + */ 4 + 5 + /** 6 + * Roughly a 50/50 chance of true or false. `chance` is optional, but should be a number between 0 and 1 if set. 7 + */ 8 + export const coinFlip = (chance?: number): boolean => 9 + Math.random() <= (chance ?? 0.5); 10 + 11 + /** 12 + * Picks a random item from an array. 13 + */ 14 + export const pick = <T>(array: T[]): T => 15 + array[Math.floor(Math.random() * array.length)]; 16 + 17 + /** 18 + * Creates function that randomly picks from the same array each time. 19 + */ 20 + export const createPick = 21 + <T>(array: T[]): (() => T) => 22 + () => 23 + pick(array);
+176
src/scripts/nicknames.ts
··· 1 + /** 2 + * @module nicknames 3 + * The adjectives and nouns are inspired by the 108 heroes of the book Water Margin. 4 + */ 5 + import { createPick } from "./chance"; 6 + 7 + const prefixes: string[] = [ 8 + "Divine", 9 + "Daylight", 10 + "The", 11 + "Iron", 12 + "Demon", 13 + "Small", 14 + "Leaping", 15 + "Fiery Eyed", 16 + "Arrow Struck", 17 + "Two Spears", 18 + "Sky", 19 + "Golden Haired", 20 + "Cloud", 21 + "Flowery Necked", 22 + "Great", 23 + "Ever Victorious", 24 + "Nimble", 25 + "Purple Bearded", 26 + "Two Bludgeons", 27 + "Marvelous", 28 + "Disliked", 29 + "Jade Armed", 30 + "Fiery", 31 + "Restless", 32 + "Winged", 33 + "Flying", 34 + "Muddy", 35 + "Hell's", 36 + "Striking", 37 + "Tiger Fighting", 38 + "Roaring", 39 + "Red Haired", 40 + "Little", 41 + "Tattooed", 42 + "Jade", 43 + "Iron Flute", 44 + "Jade", 45 + "Slightly Restrained", 46 + "Unrestrained", 47 + "Golden Winged", 48 + "Iron Faced", 49 + "Heavenly Eyes", 50 + "Living", 51 + "Water", 52 + "Golden Eyed", 53 + "Stone", 54 + "Welcome", 55 + "Night", 56 + "Urgent", 57 + "Nine Tailed", 58 + "River Churning", 59 + "Cave", 60 + "Lightning", 61 + "Fire God", 62 + "Clever", 63 + "Eight Arms", 64 + "Sacred Handed", 65 + "Double Tailed", 66 + "Double Headed", 67 + "Disgraced", 68 + "Sick", 69 + "White Speckled", 70 + "Glorious", 71 + "Elegant", 72 + "Blue Faced", 73 + "Dangerous", 74 + "Market", 75 + "Laughing", 76 + "Dry Land", 77 + "Intuitive", 78 + "Forest", 79 + "Heavenly", 80 + "Stinging", 81 + "Door", 82 + "Green Grass", 83 + "Grand", 84 + "Hairless", 85 + "Elder", 86 + "Devil Acting as", 87 + "Great", 88 + "Old", 89 + ]; 90 + 91 + const names: string[] = [ 92 + "Doctor", 93 + "Rat", 94 + "God", 95 + "Flower", 96 + "Arm", 97 + "Carver", 98 + "Whirlwind", 99 + "Tiger", 100 + "Traveler", 101 + "Beast", 102 + "General", 103 + "Dog", 104 + "King", 105 + "Dragon", 106 + "Tigress", 107 + "Halberd", 108 + "Suppressor", 109 + "Mathematician", 110 + "One", 111 + "Craftsman", 112 + "Star", 113 + "Ox", 114 + "Judge", 115 + "Hawk", 116 + "Thunder", 117 + "Demon", 118 + "Marquis", 119 + "Monk", 120 + "Qilin", 121 + "Immortal", 122 + "Clerk", 123 + "Thunderer", 124 + "Flea", 125 + "Rain", 126 + "Fan", 127 + "Diamond", 128 + "Witch", 129 + "Vanguard", 130 + "Leopard", 131 + "Tortoise", 132 + "Clam", 133 + "Goddess", 134 + "Calligrapher", 135 + "Scorpion", 136 + "Snake", 137 + "Expert", 138 + "Prodigy", 139 + "Brute", 140 + "Road Spirit", 141 + "Flute", 142 + "Arrow", 143 + "Gardener", 144 + "Fish", 145 + "Squire", 146 + "Tyrant", 147 + "Crocodile", 148 + "Lord", 149 + "Strategist", 150 + "Marshal", 151 + "Wasp", 152 + "Secretary", 153 + "Ogre", 154 + "Scholar", 155 + "Watchman", 156 + "Sword", 157 + ]; 158 + 159 + const suffixes: string[] = [ 160 + "of Three Mountains", 161 + "of Death", 162 + "of Disorder", 163 + "on the Drum", 164 + "Who Stands Their Ground", 165 + "in the Clouds", 166 + "Without Feathers", 167 + "of the Beautiful Whiskers", 168 + "of Heaven", 169 + "of the Imperial Guard", 170 + "Who Crosses the Road", 171 + "of the Western Pass", 172 + ]; 173 + 174 + export const prefix: () => string = createPick(prefixes); 175 + export const name: () => string = createPick(names); 176 + export const suffix: () => string = createPick(suffixes);