commit 5265580472f3bf4f242eef90528a5ae18bb3fdf3 parent 9c0e4da0d9ed5b7e58d6795a487fdbe651a11bec Author: massi <mdsiboldi@gmail.com> Date: Mon, 5 Aug 2024 04:47:26 -0700 build changes, esbuild for js, pages added Diffstat:
22 files changed, 812 insertions(+), 136 deletions(-)
diff --git a/Makefile b/Makefile @@ -2,16 +2,34 @@ clean: rm -r build build: +# make dirs mkdir -p build/shared - cp -r shared/static/* build/shared + mkdir -p build/swatchbuckler.massi.world mkdir -p build/massi.world + +# copy static files for shared AND each subdomain + cp -r shared/static/* build/shared cp -r site/static/* build/massi.world +# cp -r swatchbuckler/static/* build/swatchbuckler.massi.world # nonconforning rn + +# copy over random shared things +# cp node_modules/d3-color/dist/d3-color.min.js shared/static/shared/js/ + +# build ts files + ./node_modules/esbuild/bin/esbuild \ + --splitting \ + --bundle \ + --sourcemap \ + --outdir=build/shared/shared/js \ + --format=esm \ + ts/* ; + +# link shared files to each subdomain cd build/massi.world && ln -s ../shared/* . - clj -X site/build + cd build/swatchbuckler.massi.world && ln -s ../shared/* . - mkdir -p build/swatchbuckler.massi.world/js - cp -r shared/static/* build/swatchbuckler.massi.world - cp swatchbuckler/node_modules/d3-color/dist/d3-color.min.js build/swatchbuckler.massi.world/js +# build + clj -X site/build clj -X swatchbuckler/build serve: diff --git a/package-lock.json b/package-lock.json @@ -8,12 +8,481 @@ "name": "massi-world", "version": "1.0.0", "license": "ISC", + "dependencies": { + "d3-color": "^3.1.0" + }, "devDependencies": { + "@types/d3-color": "^3.1.3", + "esbuild": "0.23.0", "marked": "^13.0.3", "marked-gfm-heading-id": "github:mdsib/marked-gfm-heading-id", "prettier": "^3.3.3" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", diff --git a/package.json b/package.json @@ -7,8 +7,13 @@ "license": "ISC", "description": "", "devDependencies": { + "@types/d3-color": "^3.1.3", + "esbuild": "0.23.0", "marked": "^13.0.3", "marked-gfm-heading-id": "github:mdsib/marked-gfm-heading-id", "prettier": "^3.3.3" + }, + "dependencies": { + "d3-color": "^3.1.0" } } diff --git a/shared/static/shared-assets/icon-16-16.png b/shared/static/shared/assets/icon-16-16.png Binary files differ. diff --git a/shared/static/shared-assets/icon-180-180.png b/shared/static/shared/assets/icon-180-180.png Binary files differ. diff --git a/shared/static/shared-assets/icon-192-192.png b/shared/static/shared/assets/icon-192-192.png Binary files differ. diff --git a/shared/static/shared-assets/icon-32-32.png b/shared/static/shared/assets/icon-32-32.png Binary files differ. diff --git a/shared/static/shared-assets/icon-48-48.png b/shared/static/shared/assets/icon-48-48.png Binary files differ. diff --git a/shared/static/shared-assets/spiral-for-dark-mode.svg b/shared/static/shared/assets/spiral-for-dark-mode.svg diff --git a/shared/static/shared-assets/spiral-for-light-mode.svg b/shared/static/shared/assets/spiral-for-light-mode.svg diff --git a/site/md/fretfret.templ.md b/site/md/fretfret.templ.md @@ -0,0 +1,52 @@ +Fretfret is an Android app for learning the notes on a guitar +fretboard. It shows a random note selected from the frets and strings +you want to be tested on. Name the note associated with the position +shown, press next, and see if you were right! Repeat until you're a +guitar wizard. + +# Design + + Similar trainer apps I've used introduce too much friction to get to + the next note, through having to select your answer from a bunch of + buttons. You have to first come up with an answer, then navigate + their UI to input the answer to get to the next one. + + To me, the purpose of these kinds of trainers is more to improve + your recall time than your accuracy. After trying a few options out, + I ended up liking the simplest one - showing which note the last + position was. This way, you have access to the answer if you're + not sure whether you named it correctly. + +# On learning the fretboard + + I recommend starting with a 3 to 5 frets on a single string, and + focusing on getting those down fairly automatically. + + Then, move onto the next 3-5 (NOT all the frets you've learned + combined). + + Once you've gotten all the frets on a string down, test yourself on + the entire string. + + If you just keep adding notes to the ones you already know, the + newer notes will appear less frequently so it takes longer to get + them down. + + It's important to space repititions of new material close together. + This approach follows that of learning systems such as + [Anki](https://apps.ankiweb.net/). + +# Roadmap + - _labels for strings_ + + - _customizeable strings_ - There's no reason why this can't be used + for other stringed instruments. I'm planning on add customization + for selecting the number of strings and their tunings. + + - _tempo tracker_ - Since this app prioritizes time to answer to + accuracy, it may be useful to show you how many positions you go + through per minute so you can gauge your progress over time. + +# Contributing + Email hello at this domain, with fretfret in the subject, with + patches or bug reports. diff --git a/site/md/swatchbuckler.templ.md b/site/md/swatchbuckler.templ.md @@ -0,0 +1,5 @@ +[Swatchbuckler](@(swatchbuckler-app-path)) is a simple tool for +generating monochromatic foreground/background color pairs. I needed a +basic color scheme for my personal site (the one you're on now) and +didn't know of a tool off the top of my head that would give me +what I wanted. So I made it! diff --git a/site/static/assets/fretfret-app-icon.webp b/site/static/assets/fretfret-app-icon.webp Binary files differ. diff --git a/site/static/assets/fretfret-scrot-dark.png b/site/static/assets/fretfret-scrot-dark.png Binary files differ. diff --git a/site/static/assets/fretfret-scrot-light.png b/site/static/assets/fretfret-scrot-light.png Binary files differ. diff --git a/site/style.templ.css b/site/style.templ.css @@ -25,10 +25,6 @@ header { line-height: 1.2; } - header > *:nth-child(2n)::after { - content: "\A"; - white-space: pre; - } } html { @@ -39,10 +35,19 @@ html { } body { + box-sizing: border-box; line-height: 1.5; margin: 0px auto; max-width: 35rem; - padding: 0.75rem; + min-height: 100vh; + padding: 0.75rem 0.75rem 2.5rem 0.75rem; + position: relative; +} + +footer { + position: absolute; + bottom: 0; + padding: 0.75rem 0; } a { @@ -78,17 +83,28 @@ h1 > a, h2 > a, h3 > a, h4 > a { header { font-size: 1.2rem; - padding-bottom: 0.8rem; + padding-bottom: 1.8rem; +} + +header > span:not(:last-child) { + display: inline-block; } header a { color: inherit; } -header a:last-child { +header span:last-child a { + font-size: 1.8rem; text-decoration: none; } +/* line break for last header */ +header > span:not(:first-child):last-child::before { + content: '\A'; + white-space: pre; +} + ul { margin-left: -1rem; } @@ -107,12 +123,12 @@ h4:hover > a::after { } ul > li { - list-style: url("@(web-root)/shared-assets/spiral-for-light-mode.svg") + list-style: url("@(web-root)/shared/assets/spiral-for-light-mode.svg") } @media (prefers-color-scheme: dark) { ul > li { - list-style: url("@(web-root)/shared-assets/spiral-for-dark-mode.svg") + list-style: url("@(web-root)/shared/assets/spiral-for-dark-mode.svg") } } @@ -147,3 +163,30 @@ ul > li { margin-left: 0; } } + +/* projects/swatchbuckler: */ + +.sb-gradient { + display: flex; + flex-direction: column; +} + +.sb-gradient > div { + display: flex; +} + +.gradient-swatch { + height: 50px; + width: 100px; +} + +picture, picture img { + max-width: 100%; +} + +header span:last-child img { + height: 2rem; + position: relative; + top: 0.3rem; + padding: 0 0.3rem; +} diff --git a/src/core.clj b/src/core.clj @@ -24,6 +24,11 @@ ([] (dev? (or (System/getenv "MW_ENV") ""))) ([mw-env] (= "DEV" (cstr/upper-case mw-env)))) +(defmacro if-dev [a b] + `(if (dev?) + ~a + ~b)) + (defn mk-path-fns [domain getspec] (letfn [(p ([] (p "" (dev?))) @@ -111,15 +116,17 @@ document.documentElement.classList.add('js');")] [:link {:rel "canonical" :href url}])) ;; todo figure out how to thread project-specific p stuff into this. -(defn massi-world-domain-stuff [url p {:or {img {:src "https://massi.world/shared-assets/icon-180-180.png" +(defn massi-world-domain-stuff [url p {:or {img {:src "https://massi.world/shared/assets/icon-180-180.png" :desc "an undiscovered planet."}} :as argmap}] - (concat (base-head-stuff url argmap) - (list [:link {:rel "shortcut icon" :href (p "/favicon.ico")}] - [:link {:rel "icon" :type "image/png" :sizes "16x16" :href (p "/shared-assets/icon-16-16.png")}] - [:link {:rel "icon" :type "image/png" :sizes "32x32" :href (p "/shared-assets/icon-32-32.png")}] - [:link {:rel "icon" :type "image/png" :sizes "48x48" :href (p "/shared-assets/icon-48-48.png")}] - [:link {:rel "apple-touch-icon" :href (p "/shared-assets/icon-180-180.png")}]))) + (concat + (base-head-stuff url argmap) + (list + [:link {:rel "shortcut icon" :href (p "/favicon.ico")}] + [:link {:rel "icon" :type "image/png" :sizes "16x16" :href (p "/shared/assets/icon-16-16.png")}] + [:link {:rel "icon" :type "image/png" :sizes "32x32" :href (p "/shared/assets/icon-32-32.png")}] + [:link {:rel "icon" :type "image/png" :sizes "48x48" :href (p "/shared/assets/icon-48-48.png")}] + [:link {:rel "apple-touch-icon" :href (p "/shared/assets/icon-180-180.png")}]))) (def linkback-footer (list [:footer "made by " [:a {:href "https://massi.world"} "massi"]])) diff --git a/src/site.clj b/src/site.clj @@ -68,32 +68,28 @@ [:svg {:height "1em" :width "1em" :viewBox "0 0 16 16" :id "svg-arrow"} [:path {:style "fill:var(--fg)" :d "M 9.3370298,4.2576328 8.9740248,4.8872198 13.221,7.329 H 0.72416487 V 8.032 H 13.221 l -4.2469782,2.442097 0.363005,0.629587 5.9300332,-3.4230256 z"}]]) +(defn mw-anchor + ([pgkey-or-spec] (mw-anchor pgkey-or-spec identity)) + ([pgkey-or-spec transform-title] + [:a {:href (p :path pgkey-or-spec)} (-> pgkey-or-spec getspec :title transform-title)])) + (defn header [& crumbs] - (letfn [(mkcrumb - ([pg] - (cond (page-identifier? pg) - [:a {:href (p :path pg)} - (:title (getspec pg))] - - (string? pg) - [:span pg] - - :else - (throw (Exception. "header got wrong stuff.")))))] - [:header - (interpose [:span {:class "crumb-interposer"} svg-arrow] (map mkcrumb (conj crumbs :home)))])) + [:header + (concat (map (fn [x] [:span x [:span {:class "crumb-interposer"} svg-arrow]]) + (conj (butlast crumbs) (mw-anchor :home))) + (list [:span (last crumbs)]))]) (defn footer [] - [:footer - "Last generated: " - (.format (java.text.SimpleDateFormat. "MM/dd/yyyy hh:mm:ssa z") - (new java.util.Date))]) + [:footer [:small + "Last generated: " + (.format (java.text.SimpleDateFormat. "MM/dd/yyyy hh:mm:ssa z") + (new java.util.Date))]]) (defn build-manifest [] (core/prnt 1 "🤖" "writing build/massi.world/manifest.json") (core/process-templ "site/manifest.templ.json" "build/massi.world/manifest.json" - {:icon-192 (p "/shared-assets/icon-192-192.png") + {:icon-192 (p "/shared/assets/icon-192-192.png") :index-url (p :url :home)})) (defn build-css [] @@ -118,20 +114,35 @@ (spit out-file out-str) (core/prnt 2 "✅" "done!"))) + (defpage :home {:path ["index.html"] :title "massi world" :desc "a website"} (header) [:section - [:h1 "projects"] [:ul - [:li [:a {:href (p :path :color-synth)} "color-synth"]]]] + [:li [:a {:href "https://git.massi.world"} "git"]] + [:li (mw-anchor :projects)]]] + (footer)) + +(defpage :projects {:path ["projects" "index.html"] + :title "projects" + :desc "a variety of programs made by massi"} + (header (mw-anchor :projects)) + [:ul + [:li [:p (mw-anchor :fretfret cstr/capitalize) + " is an android app for learning the notes on a guitar fretboard."]] + [:li [:p (mw-anchor :color-synth cstr/capitalize) + " is a modular, digital video synthesizer that runs on the web."]] + [:li (core/md "site/md/swatchbuckler.templ.md" + {:swatchbuckler-app-path (core/if-dev "http://localhost:8080/swatchbuckler.massi.world" + "https://swatchbuckler.massi.world")})]] (footer)) (defpage :color-synth {:path ["projects" "color-synth.html"] :title "color-synth" :desc "a modular digital video synthesizer on the web"} - (header "projects" :color-synth) + (header (mw-anchor :projects) (mw-anchor :color-synth)) [:main.color-synth [:div.md (core/md "site/md/color-synth.templ.md" {:samples [:div.samples @@ -145,6 +156,37 @@ :alt "mellow swampy waves"}]]})]] (footer)) +(defpage :swatchbuckler {:path ["projects" "swatchbuckler.html"] + :title "swatchbuckler" + :desc "generate fg/bg pairs from a midtone"} + (header (mw-anchor :projects) (mw-anchor :swatchbuckler)) + [:div.md + (core/md "site/md/swatchbuckler.templ.md" + {:swatchbuckler-app-path (core/if-dev "http://localhost:8080/swatchbuckler.massi.world" + "https://swatchbuckler.massi.world")} + ;; {:rgb-gradient [:div {:id "gradient-sample-1" :class "sb-gradient"}] + ;; :hsl-gradient [:div {:id "gradient-sample-2" :class "sb-gradient"}] + ;; :hcl-gradient [:div {:id "gradient-sample-3" :class "sb-gradient"}]} + )] + [:script {:type "module" :src (p "/shared/js/swatchbuckler-project.js")}] + (footer)) + +(defpage :fretfret {:path ["projects" "fretfret.html"] + :title "fretfret" + :desc "an Android app for learning the notes on a guitar fretboard"} + (header (mw-anchor :projects) + (list (mw-anchor :fretfret) + [:img {:src (p "/assets/fretfret-app-icon.webp")}])) + [:picture + [:source {:srcset (p "/assets/fretfret-scrot-dark.png") :media "(prefers-color-scheme:dark)"}] + [:img {:src (p "/assets/fretfret-scrot-light.png")}]] + [:ul + [:li [:a {:href "https://git.massi.world/fretfret"} "source code"]] + [:li [:a {:href "https://releases.massi.world/fretfret/fretfret-2024-08-05.apk"} "download apk"] [:small " 2024.08.05"]]] + [:div.md + (core/md "site/md/fretfret.templ.md")] + (footer)) + ;; _ arg is so we can call this with clj -X massi-world/build ;; _ is expected to be a map, and clojure complains if we don't accept it (defn build [_] diff --git a/src/swatchbuckler.clj b/src/swatchbuckler.clj @@ -35,7 +35,7 @@ (core/process-templ "swatchbuckler/manifest.templ.json" "build/swatchbuckler.massi.world/manifest.json" - {:icon-192 (p "/shared-assets/icon-192-192.png") + {:icon-192 (p "/shared/assets/icon-192-192.png") :index-url (p :url spec)}) (core/prnt 1 "🤖" "wrote build/massi.world/manifest.json") (print "\t 🕜\t"))) diff --git a/swatchbuckler/index.templ.html b/swatchbuckler/index.templ.html @@ -141,99 +141,7 @@ } } </style> - <script type="module" src="js/d3-color.min.js"></script> - <script> - const INC = 0.4; - const RANGE = 4.1; - - // copied from https://gist.github.com/jfsiii/5641126 - //// from http://www.w3.org/TR/WCAG20/#relativeluminancedef - function luminance({ r, g, b }) { - var RsRGB = r / 255; - var GsRGB = g / 255; - var BsRGB = b / 255; - - var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4); - var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4); - var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4); - - // For the sRGB colorspace, the relative luminance of a color is defined as: - var L = 0.2126 * R + 0.7152 * G + 0.0722 * B; - return L; - } - function contrast(lumA, lumB) { - if (lumA < lumB) { - let tmp = lumA; - lumA = lumB; - lumB = tmp; - } - return (lumA + 0.05) / (lumB + 0.05); - } - function makeColors() { - const parentEl = document.getElementById("renderzone"); - const colorEl = document.getElementById("midcolor") - const midColor = d3.lch(colorEl.value); - const minContrast = Number(document.getElementById("mincontrast").value); - const heading = document.getElementById("msg").value; - - console.log(midColor); - if (Number.isNaN(midColor.h)) { - colorEl.style.background = "salmon"; - function handlePress() { - colorEl.style.background = "white"; - } - colorEl.addEventListener("keydown", handlePress, { once: true }); - return; - } - - parentEl.innerHTML = ""; - for (let i = -1 * RANGE; i <= RANGE; i += INC) { - const bg = d3.lch(midColor).brighter(i).formatHex(); - if (bg == "#000000" || bg == "#ffffff") { - continue; - } - const bgLum = luminance(d3.rgb(bg)); - const sign = bgLum > 0.5 ? 1 : -1 - for (let j = sign * -1 * RANGE; sign * j <= RANGE; j += sign * INC) { - const fg = d3.lch(midColor).brighter(j).formatHex(); - const swatchContrast = contrast(luminance(d3.rgb(fg)), bgLum); - if (fg == "#000000" || fg == "#ffffff" || swatchContrast < minContrast) { - continue; - } - const el = document.createElement("div"); - el.classList.add("renderblock", "rounded"); - el.style.backgroundColor = bg; - el.style.color = fg; - el.innerHTML = `<h1>${heading}</h1> - <h2>bg: ${bg}</h2> - <h3>fg: ${fg}</h3> - <p>contrast ratio: ${swatchContrast.toPrecision(4)}</p>` - const b = document.createElement('button'); - b.innerText = "copy css"; - b.style.color = bg; - b.style.backgroundColor = fg; - b.addEventListener('click', () => { - navigator.clipboard.writeText(`background-color: ${bg};\ncolor: ${fg};`) - }) - el.appendChild(b); - parentEl.appendChild(el); - } - } - } - - document.addEventListener("DOMContentLoaded", () => { - makeColors(); - document.addEventListener("keypress", (e) => { - if (e.keyCode === 13) { - makeColors(); - } - }) - document.getElementById("thebutton").addEventListener("click", () => { - makeColors(); - }) - }) - - </script> + <script type="module" src="shared/js/swatchbuckler.js"></script> </head> <body> diff --git a/ts/swatchbuckler-project.ts b/ts/swatchbuckler-project.ts @@ -0,0 +1,35 @@ +import * as d3 from 'd3-color'; +document.addEventListener("DOMContentLoaded", () => { + const el = document.getElementById("gradient-sample-1") as HTMLDivElement; + const drawSwatches = (tgtEl: HTMLDivElement, lightenColorFn: (colorspace, lightness: number) => string) => { + const colorspaceSwatch = (colorspace, swatchEl = document.createElement("div")) => { + const nodes: Array<HTMLDivElement> = []; + let lastColor = ""; + swatchEl.classList.add("gradient"); + for (let i = -10; i <= 10; i += 0.2) { + const color = lightenColorFn(colorspace, i); + console.log(color); + if (color === "rgb(0, 0, 0)" || color === lastColor) { + continue; + } + const swatchEl = document.createElement("div"); + swatchEl.classList.add("gradient-swatch") + swatchEl.style.backgroundColor = color; + nodes.push(swatchEl); + lastColor = color; + } + swatchEl.replaceChildren(...nodes); + return swatchEl; + } + tgtEl.appendChild(colorspaceSwatch(d3.rgb)); + tgtEl.appendChild(colorspaceSwatch(d3.hsl)); + tgtEl.appendChild(colorspaceSwatch(d3.lch)); + } + const mkLighten = (color) => (colorspace, lightness: number) => { + return colorspace(color).brighter(lightness).toString(); + + } + drawSwatches(document.getElementById("gradient-sample-1") as HTMLDivElement, mkLighten("#ff1111")); + drawSwatches(document.getElementById("gradient-sample-2") as HTMLDivElement, mkLighten("#11ff11")); + drawSwatches(document.getElementById("gradient-sample-3") as HTMLDivElement, mkLighten("#1111ff")); +}); diff --git a/ts/swatchbuckler.ts b/ts/swatchbuckler.ts @@ -0,0 +1,92 @@ +import * as d3 from 'd3-color'; + +const INC = 0.4; +const RANGE = 4.1; + +// copied from https://gist.github.com/jfsiii/5641126 +//// from http://www.w3.org/TR/WCAG20/#relativeluminancedef +function luminance({ r, g, b }) { + var RsRGB = r / 255; + var GsRGB = g / 255; + var BsRGB = b / 255; + + var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4); + var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4); + var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4); + + // For the sRGB colorspace, the relative luminance of a color is defined as: + var L = 0.2126 * R + 0.7152 * G + 0.0722 * B; + return L; +} +function contrast(lumA: number, lumB: number) { + if (lumA < lumB) { + let tmp = lumA; + lumA = lumB; + lumB = tmp; + } + return (lumA + 0.05) / (lumB + 0.05); +} +function makeColors() { + const parentEl = document.getElementById("renderzone") as HTMLDivElement; + const colorEl = document.getElementById("midcolor") as HTMLInputElement; + const midColor = d3.lch(colorEl.value); + const minContrast = Number((document.getElementById("mincontrast") as HTMLInputElement).value); + const heading = (document.getElementById("msg") as HTMLInputElement).value; + + if (Number.isNaN(midColor.h)) { + colorEl.style.background = "salmon"; + function handlePress() { + colorEl.style.background = "white"; + } + colorEl.addEventListener("keydown", handlePress, { once: true }); + return; + } + + parentEl.innerHTML = ""; + for (let i = -1 * RANGE; i <= RANGE; i += INC) { + const bg = d3.lch(midColor).brighter(i).formatHex(); + if (bg == "#000000" || bg == "#ffffff") { + continue; + } + const bgLum = luminance(d3.rgb(bg)); + const sign = bgLum > 0.5 ? 1 : -1 + for (let j = sign * -1 * RANGE; sign * j <= RANGE; j += sign * INC) { + const fg = d3.lch(midColor).brighter(j).formatHex(); + const swatchContrast = contrast(luminance(d3.rgb(fg)), bgLum); + if (fg == "#000000" || fg == "#ffffff" || swatchContrast < minContrast) { + continue; + } + const el = document.createElement("div"); + el.classList.add("renderblock", "rounded"); + el.style.backgroundColor = bg; + el.style.color = fg; + el.innerHTML = `<h1>${heading}</h1> + <h2>bg: ${bg}</h2> + <h3>fg: ${fg}</h3> + <p>contrast ratio: ${swatchContrast.toPrecision(4)}</p>` + const b = document.createElement('button'); + b.innerText = "copy css"; + b.style.color = bg; + b.style.backgroundColor = fg; + b.addEventListener('click', () => { + navigator.clipboard.writeText(`background-color: ${bg};\ncolor: ${fg};`) + }) + el.appendChild(b); + parentEl.appendChild(el); + } + } +} + +document.addEventListener("DOMContentLoaded", () => { + makeColors(); + document.addEventListener("keypress", (e) => { + if (e.keyCode === 13) { + makeColors(); + } + }) + const buttonEl = document.getElementById("thebutton") as HTMLButtonElement; + buttonEl.addEventListener("click", () => { + makeColors(); + }) +}) +