Compare commits

...

6 Commits

7 changed files with 422 additions and 0 deletions

160
.gitignore vendored Normal file
View File

@ -0,0 +1,160 @@
# Created by https://www.toptal.com/developers/gitignore/api/git,node
# Edit at https://www.toptal.com/developers/gitignore?templates=git,node
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/git,node

View File

@ -0,0 +1 @@
<svg id="visual" viewBox="0 0 1920 720" width="1920" height="720" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><path d="M0 530L35.5 510.2C71 490.3 142 450.7 213.2 428.7C284.3 406.7 355.7 402.3 426.8 419C498 435.7 569 473.3 640 484.5C711 495.7 782 480.3 853.2 480.2C924.3 480 995.7 495 1066.8 504.2C1138 513.3 1209 516.7 1280 517.3C1351 518 1422 516 1493.2 497.8C1564.3 479.7 1635.7 445.3 1706.8 429.7C1778 414 1849 417 1884.5 418.5L1920 420L1920 721L1884.5 721C1849 721 1778 721 1706.8 721C1635.7 721 1564.3 721 1493.2 721C1422 721 1351 721 1280 721C1209 721 1138 721 1066.8 721C995.7 721 924.3 721 853.2 721C782 721 711 721 640 721C569 721 498 721 426.8 721C355.7 721 284.3 721 213.2 721C142 721 71 721 35.5 721L0 721Z" fill="#4facf7"></path><path d="M0 557L35.5 545C71 533 142 509 213.2 490.3C284.3 471.7 355.7 458.3 426.8 453.2C498 448 569 451 640 461C711 471 782 488 853.2 505C924.3 522 995.7 539 1066.8 551.3C1138 563.7 1209 571.3 1280 561.8C1351 552.3 1422 525.7 1493.2 510.2C1564.3 494.7 1635.7 490.3 1706.8 502.2C1778 514 1849 542 1884.5 556L1920 570L1920 721L1884.5 721C1849 721 1778 721 1706.8 721C1635.7 721 1564.3 721 1493.2 721C1422 721 1351 721 1280 721C1209 721 1138 721 1066.8 721C995.7 721 924.3 721 853.2 721C782 721 711 721 640 721C569 721 498 721 426.8 721C355.7 721 284.3 721 213.2 721C142 721 71 721 35.5 721L0 721Z" fill="#2b9dfc"></path><path d="M0 576L35.5 566.2C71 556.3 142 536.7 213.2 538.5C284.3 540.3 355.7 563.7 426.8 566.2C498 568.7 569 550.3 640 543.2C711 536 782 540 853.2 535C924.3 530 995.7 516 1066.8 524.7C1138 533.3 1209 564.7 1280 579.5C1351 594.3 1422 592.7 1493.2 589.2C1564.3 585.7 1635.7 580.3 1706.8 566.2C1778 552 1849 529 1884.5 517.5L1920 506L1920 721L1884.5 721C1849 721 1778 721 1706.8 721C1635.7 721 1564.3 721 1493.2 721C1422 721 1351 721 1280 721C1209 721 1138 721 1066.8 721C995.7 721 924.3 721 853.2 721C782 721 711 721 640 721C569 721 498 721 426.8 721C355.7 721 284.3 721 213.2 721C142 721 71 721 35.5 721L0 721Z" fill="#008cff"></path><path d="M0 575L35.5 580.2C71 585.3 142 595.7 213.2 602.3C284.3 609 355.7 612 426.8 610.8C498 609.7 569 604.3 640 605.3C711 606.3 782 613.7 853.2 617.5C924.3 621.3 995.7 621.7 1066.8 612.7C1138 603.7 1209 585.3 1280 581C1351 576.7 1422 586.3 1493.2 590.5C1564.3 594.7 1635.7 593.3 1706.8 585.7C1778 578 1849 564 1884.5 557L1920 550L1920 721L1884.5 721C1849 721 1778 721 1706.8 721C1635.7 721 1564.3 721 1493.2 721C1422 721 1351 721 1280 721C1209 721 1138 721 1066.8 721C995.7 721 924.3 721 853.2 721C782 721 711 721 640 721C569 721 498 721 426.8 721C355.7 721 284.3 721 213.2 721C142 721 71 721 35.5 721L0 721Z" fill="#007aff"></path><path d="M0 648L35.5 648.5C71 649 142 650 213.2 642.7C284.3 635.3 355.7 619.7 426.8 620.7C498 621.7 569 639.3 640 647.7C711 656 782 655 853.2 653.7C924.3 652.3 995.7 650.7 1066.8 644.2C1138 637.7 1209 626.3 1280 624.5C1351 622.7 1422 630.3 1493.2 633.3C1564.3 636.3 1635.7 634.7 1706.8 635.2C1778 635.7 1849 638.3 1884.5 639.7L1920 641L1920 721L1884.5 721C1849 721 1778 721 1706.8 721C1635.7 721 1564.3 721 1493.2 721C1422 721 1351 721 1280 721C1209 721 1138 721 1066.8 721C995.7 721 924.3 721 853.2 721C782 721 711 721 640 721C569 721 498 721 426.8 721C355.7 721 284.3 721 213.2 721C142 721 71 721 35.5 721L0 721Z" fill="#0066ff"></path></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

89
css/theme.css Normal file
View File

@ -0,0 +1,89 @@
:root {
--bg: #bbdbf6;
--bg2: #0066ff;
--accent: #333333;
--accent-alt: #f4d48c;
--selected: hsl(330, 64%, 53%);
}
html, body { font-size: 100%; }
body {
margin: 0; padding: 0;
display: flex;
flex-direction: column;
font-family: "Ubuntu", sans-serif;
text-align: center;
font-size: 150%;
min-height: 100vh;
background: var(--bg);
color: var(--accent);
}
#input-line {
display: flex;
justify-content: center;
align-items: center;
}
#starting-regen {
background: transparent;
border: 0;
font-size: 200%;
cursor: pointer;
margin-left: 0.5em;
}
#header {
/* Ref https://app.haikei.app/ */
background: url("./layered-waves-haikei.svg") bottom/cover;
}
input[type=text] {
color: var(--accent);
font-family: "Ubuntu", sans-serif;
text-align: center;
font-size: 150%;
max-width: 80%;
background: transparent;
border: 0;
border-bottom: 0.2em solid var(--accent-alt);
}
input[type=text]:focus {
outline: 0.2em solid var(--selected);
}
#mutations {
padding-top: 1em;
padding-bottom: 10vh;
background: #0066ff;
flex: 1;
}
.mutation-step {
display: flex;
justify-content: space-evenly;
padding: 1em 0;
}
hr {
width: 60%;
border-bottom: 0.2em solid hsla(0, 0%, 10%, 0.5);
}
.mutation-option {
cursor: pointer;
background: var(--accent-alt);
border-radius: 0.25em;
padding: 0.5em 0.75em;
}
.mutation-option.selected {
color: white;
background: var(--selected);
}

27
index.html Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>mutate-a-word!</title>
</head>
<body>
<section id="header">
<h1>mutate-a-word!</h1>
<h2>Enter a starting word</h2>
<div id="input-line">
<input type="text" id="starting-word" value="rockets" placeholder="e.g. rockets" />
<button id="starting-regen">🔄</button>
</div>
</section>
<section id="mutations">
<em>Mutations will appear here</em>
</section>
<!---------------->
<link rel="stylesheet" href="./css/theme.css" />
<script src="./js/index.mjs" charset="utf-8" type="module"></script>
</body>
</html>

81
js/index.mjs Normal file
View File

@ -0,0 +1,81 @@
"use strict";
import mutate from './mutate.mjs';
let display_options = 3;
window.addEventListener("load", (_event) => {
const el_starting_word = document.querySelector("#starting-word");
const el_starting_regen = document.querySelector("#starting-regen");
if(el_starting_word.value.length > 0)
handle_starting_keyup();
el_starting_word.addEventListener("keyup", handle_starting_keyup);
el_starting_regen.addEventListener("click", handle_starting_keyup);
el_starting_regen.addEventListener("mouseup", handle_starting_keyup);
el_starting_regen.addEventListener("touchend", handle_starting_keyup);
});
function handle_starting_keyup(_event) {
const el_starting_word = document.querySelector("#starting-word");
const el_mutations = document.querySelector("#mutations");
el_mutations.replaceChildren(); // Empty node of all children
mutation_step(el_starting_word.value);
}
function handle_mutation_word_keyup(event) {
// 1: Find DOM elements
const el_mutations = document.querySelector("#mutations");
const el_step = event.target.closest(".mutation-step");
const el_word = event.target.closest(".mutation-option");
const els_word = el_step.querySelectorAll(".mutation-option");
// 2: Update UI - Change classes, Remove all subsequent mutation steps
for(let i = 0; i < els_word.length; i++)
els_word[i].classList.remove("selected");
el_word.classList.add("selected");
while(el_step.nextElementSibling !== null)
el_step.parentNode.removeChild(el_step.nextElementSibling);
// 3: Do a new mutation step
el_mutations.appendChild(document.createElement("hr"))
mutation_step(el_word.dataset.word);
}
function mutation_step(word) {
// 0: Validate input
if(typeof word !== "string")
throw new Error(`Error: Expected argument 1 (word) to be of type string, but got value of type ${typeof word}.`);
// 1: Find DOM elements
const el_mutations = document.querySelector("#mutations");
// 2: Generate mutations
// TODO: Eliminate duplicates
const words_new = Array(display_options).fill(null)
.map(() => mutate(word));
// 3: Update DOM
let el_step = document.createElement("div");
el_step.classList.add("mutation-step");
for(let word_item of words_new) {
let el_word = document.createElement("span");
el_word.classList.add("mutation-option");
el_word.appendChild(document.createTextNode(word_item));
el_word.dataset.word = word_item;
el_step.appendChild(el_word);
el_word.addEventListener("click", handle_mutation_word_keyup);
el_word.addEventListener("touchend", handle_mutation_word_keyup);
el_word.addEventListener("mouseup", handle_mutation_word_keyup);
}
el_mutations.appendChild(el_step);
window.scrollTo(0,document.body.scrollHeight);
}

49
js/mutate.mjs Normal file
View File

@ -0,0 +1,49 @@
"use strict";
const vowels = "aeiou".split("");
const consonants = "bcdfghjklmnpqrstvwxyz".split("");
function random_item(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
export default function(word) {
const chars = word.toLowerCase().split("");
const targetpos = Math.floor(Math.random() * word.length);
let mode = "replace";
if(Math.random() < 0.1) mode = "add";
if(Math.random() > 0.9) mode = "remove";
switch(mode) {
case "replace":
const targetchar = chars[targetpos];
console.log("TARGET", targetchar, "POS", targetpos);
let pool = consonants.concat(vowels);
if(vowels.includes(targetchar))
pool = vowels;
if(consonants.includes(targetchar))
pool = consonants;
pool = [...pool]; // Shallow copy to avoid splice mutating the original array
console.log("POOL BEFORE", pool);
if(pool.includes(targetchar)) {
console.log("REMOVING TARGET");
pool.splice(pool.indexOf(targetchar), 1);
}
console.log("POOL AFTER", pool);
let newchar = random_item(pool);
chars.splice(targetpos, 1, newchar);
break;
case "add":
chars.splice(targetpos, 0, random_item(consonants.concat(vowels)));
break;
case "remove":
chars.splice(targetpos, 1);
break;
}
return chars.join("");
}

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "mutate-a-word",
"version": "1.0.0",
"description": "Mutate a word to find a cool name!",
"main": "js/index.mjs",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://git.starbeamrainbowlabs.com/sbrl/mutate-a-word.git"
},
"author": "Starbeamrainbowlabs",
"license": "MPL-2.0"
}