all repos — websitemeow @ afcd0c95ce0be522002e26ee5560869b251e52d7

gloprify page !!
vi did:web:vt3e.cat
Sat, 25 Jul 2026 20:10:39 +0100
commit

afcd0c95ce0be522002e26ee5560869b251e52d7

parent

b60e2f51b2345d10492cee471824bf70105814ca

3 files changed, 371 insertions(+), 0 deletions(-)

jump to
M src/layouts/Layout.astrosrc/layouts/Layout.astro

@@ -203,5 +203,8 @@

.content { padding: calc(var(--padding) * 2) calc(var(--padding) / 2); } + &:last-of-type .content { + padding-bottom: var(--padding); + } } </style>
A src/pages/glorpify.astro

@@ -0,0 +1,368 @@

+--- +import { Layout } from "@/layouts"; +--- + +<Layout> + <Fragment slot="left"> + <header class="page-head"> + <img + src="/kittens.jpg" + alt="two small orange & white kittens huddled together on a white surface. there is white text overlay over each of the kittens, the first reads 'you are made of stardust', the last reads 'and i am made of dirt'. there is a date stamp in the bottom right corner, it says '04.04.2008'." + /> + <div class="title"> + <h1 class="name">glorpify</h1> + </div> + <p class="subtitle">let's alien it up .</p> + </header> + </Fragment> + + <Fragment slot="right"> + <section> + <header> + <h2>translator</h2> + <p>convert earth speak into glorpian</p> + </header> + + <div class="content translator-container"> + <div class="field"> + <label for="input">human text</label> + <textarea id="input" placeholder="type something here..." rows="4" + ></textarea> + </div> + + <div class="controls"> + <label class="checkbox-label"> + <input type="checkbox" id="suffix-toggle" checked /> + <span>include random alien noises</span> + </label> + + <div class="action-buttons"> + <button type="button" id="clear-btn" class="link-btn">clear</button> + <button type="button" id="copy-btn" class="link-btn primary"> + copy output + </button> + </div> + </div> + + <div class="field"> + <label for="output">glorpified output</label> + <textarea + id="output" + readonly + placeholder="zeep zorp geeble..." + rows="4"></textarea> + </div> + </div> + </section> + + <script> + const slang: Record<string, string> = { + hello: "zeep zorp", + hi: "meep", + cat: "gnarp", + dog: "glorb", + human: "geeble", + person: "geeble", + people: "geebles", + what: "what the glorp", + cool: "bogos binted", + awesome: "bogos binted", + alien: "glorp", + aliens: "glorps", + }; + + const vowelMap: Record<string, string> = { + a: "arp", + e: "eep", + i: "ip", + o: "orp", + u: "oop", + A: "Arp", + E: "Eep", + I: "Ip", + O: "Orp", + U: "Oop", + }; + + const suffixes = [ + "glorp", + "gnarp gnarp", + "gleep glorp", + "zeep zorp", + "meep mop", + "geeble", + "bogos binted", + ]; + + function stringHash(str: string): number { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = (hash << 5) - hash + str.charCodeAt(i); + hash |= 0; + } + return Math.abs(hash); + } + + function glorpifyWord(word: string): string { + const lower = word.toLowerCase(); + const isWordUppercase = word === word.toUpperCase(); + + if (slang[lower] !== undefined) { + const replacement = slang[lower]; + if (isWordUppercase) return replacement.toUpperCase(); + if (word[0] === word[0]?.toUpperCase()) + return replacement.charAt(0).toUpperCase() + replacement.slice(1); + + return replacement; + } + + return word.replace(/[aeiouAEIOU]+/g, (match) => { + const firstChar = match[0]; + const replacement = firstChar ? vowelMap[firstChar] || match : match; + return isWordUppercase ? replacement.toUpperCase() : replacement; + }); + } + + export function glorpify( + input: string, + addSuffix: boolean = true, + ): string { + if (!input.trim()) return ""; + + const tokens = input.match(/\w+|[^\w]+/g) || []; + let glorpifiedContent = tokens + .map((token) => (/^\w+$/.test(token) ? glorpifyWord(token) : token)) + .join(""); + + if (addSuffix) { + const hash = stringHash(input); + if (hash % 3 === 0) { + const randomSuffix = suffixes[hash % suffixes.length]; + const cleanEnd = glorpifiedContent.replace(/[.!?,]+$/, ""); + const punctuation = glorpifiedContent.slice(cleanEnd.length) || "!"; + glorpifiedContent = `${cleanEnd} ...${randomSuffix}${punctuation}`; + } + } + + return glorpifiedContent; + } + + const inputEl = document.getElementById("input") as HTMLTextAreaElement; + const outputEl = document.getElementById("output") as HTMLTextAreaElement; + const suffixToggle = document.getElementById( + "suffix-toggle", + ) as HTMLInputElement; + const copyBtn = document.getElementById("copy-btn") as HTMLButtonElement; + const clearBtn = document.getElementById( + "clear-btn", + ) as HTMLButtonElement; + + function updateOutput() { + if (inputEl && outputEl) + outputEl.value = glorpify(inputEl.value, suffixToggle?.checked); + } + + inputEl?.addEventListener("input", updateOutput); + suffixToggle?.addEventListener("change", updateOutput); + + clearBtn?.addEventListener("click", () => { + if (inputEl && outputEl) { + inputEl.value = ""; + outputEl.value = ""; + inputEl.focus(); + } + }); + + copyBtn?.addEventListener("click", async () => { + if (!outputEl.value) return; + try { + await navigator.clipboard.writeText(outputEl.value); + const prevText = copyBtn.innerText; + copyBtn.innerText = "copied! 👽"; + setTimeout(() => { + copyBtn.innerText = prevText; + }, 1500); + } catch (err) { + console.error("failed to copy: ", err); + } + }); + </script> + </Fragment> +</Layout> + +<style> + .translator-container { + display: flex; + flex-direction: column; + gap: 0.25rem; + } + + .field { + display: flex; + flex-direction: column; + gap: 0.25rem; + + label { + font-size: 0.85rem; + font-weight: 700; + color: hsl(var(--subtext0)); + text-transform: lowercase; + padding-left: calc(var(--padding) * 1.5); + } + } + + textarea { + width: 100%; + background-color: hsl(var(--surface0)); + color: hsl(var(--text)); + border-radius: var(--inner-radii); + resize: vertical; + padding: calc(var(--padding) * 1.5); + font-family: inherit; + outline-offset: -2px; + outline-width: 4px; + border: none; + + &[readonly] { + background-color: hsla(var(--surface0) / 0.8); + color: hsl(var(--subtext0)); + cursor: not-allowed; + } + } + + .controls { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 0.25rem; + margin-bottom: calc(var(--padding) * 1.5); + + padding-left: calc(var(--padding) * 1.5); + padding-right: calc(var(--padding) * 1.5); + } + + .action-buttons { + display: flex; + align-items: center; + gap: 0.25rem; + } + + .link-btn { + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + border-radius: 0.5rem; + padding: 0.25rem 0.85rem; + font-family: inherit; + font-size: 0.85rem; + font-weight: 700; + color: hsl(var(--text)); + background: hsl(var(--surface0)); + + &:hover, + &:focus-visible { + background: hsl(var(--surface1)); + } + &:active { + background: hsla(var(--surface0) / 0.8); + } + + &.primary { + background: hsl(var(--accent)); + color: hsl(var(--on-accent)); + + &:hover, + &:focus-visible { + filter: brightness(1.1); + } + &:active { + background: hsla(var(--accent) / 0.8); + } + } + } + + .checkbox-label { + display: grid; + grid-template-columns: 1em auto; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + font-weight: 600; + color: hsl(var(--subtext0)); + user-select: none; + &:hover { + color: hsl(var(--subtext1)); + &:active { + color: hsla(var(--subtext0) / 0.8); + } + } + + &:has(:checked) { + color: hsl(var(--accent)); + &:hover, + &:focus-visible { + color: hsl(var(--accent)); + } + &:active { + color: hsla(var(--accent) / 0.8); + } + } + } + + input[type="checkbox"] { + -webkit-appearance: none; + appearance: none; + background-color: hsl(var(--surface0)); + margin: 0; + + min-width: 1.15em; + min-height: 1.15em; + border-radius: 0.25em; + corner-shape: squircle; + + display: grid; + place-content: center; + + &:hover, + &:focus-visible { + background: hsl(var(--surface1)); + color: hsl(var(--subtext0)); + } + &:active { + background: hsla(var(--surface0) / 0.8); + } + } + + input[type="checkbox"]::before { + content: ""; + aspect-ratio: 1 / 1; + width: 0.65em; + height: 0.65em; + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + transform-origin: center; + box-shadow: inset 1em 1em var(--form-control-color); + background-color: hsl(var(--on-accent) / 0); + } + + input[type="checkbox"]:checked { + background-color: hsla(var(--accent) / 0.8); + &:hover, + &:focus-visible { + background: hsl(var(--accent)); + } + &:active { + background: hsla(var(--accent) / 0.7); + } + } + + input[type="checkbox"]:checked::before { + transform: scale(1); + opacity: 1; + background-color: hsl(var(--on-accent)); + } + + input[type="checkbox"]:focus-visible { + } +</style>