style overhaul, change descriptions a little

This commit is contained in:
osmarks 2021-04-12 14:15:44 +01:00
parent 1902999735
commit c62da80799
9 changed files with 223 additions and 26 deletions

View File

@ -1,6 +1,7 @@
--- ---
title: New site design! title: New site design!
created: 25/01/2020 created: 25/01/2020
description: It's slightly different now!
--- ---
If you visit this frequently (why would you?) you may have noticed that since late last year there's been a page notifying you of service disruption and no actual content any more. If you visit this frequently (why would you?) you may have noticed that since late last year there's been a page notifying you of service disruption and no actual content any more.
This is because the old code used to generate the HTML files making up the site was a several-year-old and somewhat flaky Haskell program, and I've been meaning to replace it for a while, but finally got round to it recently after quite a while of just leaving the service disruption page up. This is because the old code used to generate the HTML files making up the site was a several-year-old and somewhat flaky Haskell program, and I've been meaning to replace it for a while, but finally got round to it recently after quite a while of just leaving the service disruption page up.

View File

@ -37,7 +37,7 @@ Obviously this is just stuff *I* like; you might not like it, which isn't really
* [The Combat Codes](https://www.goodreads.com/book/show/27790093-the-combat-codes) by Alexander Darwin. Similar to Ender's Game, but with MMA, basically, and I am bad at describing things. * [The Combat Codes](https://www.goodreads.com/book/show/27790093-the-combat-codes) by Alexander Darwin. Similar to Ender's Game, but with MMA, basically, and I am bad at describing things.
* [Schlock Mercenary](https://www.schlockmercenary.com/), a *very* long-running space opera webcomic. It's been running for something like 20 years, and the art and such improve over time. * [Schlock Mercenary](https://www.schlockmercenary.com/), a *very* long-running space opera webcomic. It's been running for something like 20 years, and the art and such improve over time.
* [Freefall](http://freefall.purrsia.com/), a hard-science-fiction webcomic. * [Freefall](http://freefall.purrsia.com/), a hard-science-fiction webcomic.
* [Mage Errant](https://www.goodreads.com/series/252085-mage-errant) - a moderately-long-by-now fantasy series with a really interesting world. * [Mage Errant](https://www.goodreads.com/series/252085-mage-errant) - a moderately-long-by-now fantasy series with a very vibrant world, and which actually considers the geopolitical implications of there being beings around ("Great Powers") able to act as one-man armies.
* [Arcane Ascension](https://www.goodreads.com/series/201441-arcane-ascension) - fun progression fantasy series with (... like most of these, actually) worldbuilding I like and good characters. I have only read the first two, since I'm writing this just as the third came out * [Arcane Ascension](https://www.goodreads.com/series/201441-arcane-ascension) - fun progression fantasy series with (... like most of these, actually) worldbuilding I like and good characters. I have only read the first two, since I'm writing this just as the third came out
Special mentions (i.e. "I haven't gotten around to reading these but they are well-reviewed and sound interesting") to: Special mentions (i.e. "I haven't gotten around to reading these but they are well-reviewed and sound interesting") to:

View File

@ -0,0 +1,158 @@
---
title: FractalArt
slug: fractalart
description: A somewhat unperformant generator for pleasant watercolor-y "fractalart" images. Ported from a Haskell implementation by "TomSmeets".
---
<style>
.controls input, .controls button { border: 1px solid black; padding: 0.2em; }
#fractalart { display: flex; align-items: flex-start; flex-wrap: wrap; }
#status { font-style: italic; }
#info { padding-top: 0.5em; font-size: 0.8em; }
#out { image-rendering: crisp-edges; }
</style>
<div id="fractalart">
<canvas id="out" width=512 height=512></canvas>
<table class="controls">
<tr><td>Lightness:</td><td><input id="lightness" type="number" max="1" min="0" step="0.01" value="0.6"></td></tr>
<tr><td>Saturation:</td><td><input id="saturation" type="number" max="1" min="0" step="0.01" value="1.0"></td></tr>
<tr><td>Hue:</td><td><input id="hue" type="number" max="360" min="0" step="1" value="0"></td><td>(or random: <input type="checkbox" checked id="hue-random">)</td></tr>
<tr><td>Variance:</td><td><input id="variance" type="number" min="0" step="0.25" value="4"></td></tr>
<tr><td>Bias:</td><td><input id="bias" type="number" step="0.25" value="0"></td></td></tr>
<tr><td>Render Size:</td><td><input id="width" type="number" step="16" value="512"></td></td></tr>
<tr><td><button id="generate">Generate</button></td></tr>
<tr><td colspan="4" id="status"></td></tr>
<tr><td colspan="4"><div id="info">
<a href="https://github.com/TomSmeets/FractalArt/">FractalArt</a> for JS.<br>
If this is too slow try my <a href="https://github.com/osmarks/random-stuff/tree/master/fractalart-rs">Rust port</a>.<br>
This is all contained in one HTML file, so you can save it locally if you want.
</div></td></tr>
</table>
</div>
<script id="fractalart-code">
// harvested from github somewhere
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [ r * 255, g * 255, b * 255 ];
}
const randRange = x => Math.floor(Math.random() * x)
// main fractalart logic
// todo: WASM (there is already a perfectly good Rust version...) or just actually optimize this
const run = (im, { hue, saturation, lightness, variance, bias }) => {
const set = (x, y, offset, value) => { im.data[(y * im.width + x) * 4 + offset] = value }
const get = (x, y, offset) => im.data[(y * im.width + x) * 4 + offset]
const boundcheck = (x, y) => x >= 0 && y >= 0 && x < im.width && y < im.height
const ringAt = (x, y, l) => {
let out = []
for (let n = -l; n <= l; n++) {
out.push([n + x, l + y])
out.push([n + x, -l + y])
}
for (let n = 1 - l; n <= l; n++) {
out.push([l + x, n + y])
out.push([-l + x, n + y])
}
return out.filter(([x, y]) => boundcheck(x, y))
}
const modColorChannel = x => Math.min(Math.max(Math.floor(x + (Math.random() * (variance * 2 + 1)) - variance + bias), 0), 255)
const startX = randRange(im.width)
const startY = randRange(im.height)
const iterations = Math.max(startX, im.width - 1 - startX, startY, im.height - 1 - startY)
const [sR, sG, sB] = hslToRgb(hue, saturation, lightness)
set(startX, startY, 0, Math.floor(sR))
set(startX, startY, 1, Math.floor(sG))
set(startX, startY, 2, Math.floor(sB))
set(startX, startY, 3, 255)
for (let iteration = 1; iteration <= iterations; iteration++) {
for (const [x, y] of ringAt(startX, startY, iteration)) {
const possibleColors = []
for (const [ax, ay] of ringAt(x, y, 1)) {
if (get(ax, ay, 3)) {
possibleColors.push([get(ax, ay, 0), get(ax, ay, 1), get(ax, ay, 2)])
}
}
const [chosenR, chosenG, chosenB] = possibleColors[randRange(possibleColors.length)]
set(x, y, 0, modColorChannel(chosenR))
set(x, y, 1, modColorChannel(chosenG))
set(x, y, 2, modColorChannel(chosenB))
set(x, y, 3, 255)
}
}
postMessage(im)
}
const readInput = name => document.getElementById(name).value
// detect if not running on webworker
if ("document" in self) {
const statusDisplay = document.querySelector("#status")
let status = "Idle"
let startTime = null
const updateStatus = s => {
if (statusDisplay.firstChild) { statusDisplay.removeChild(statusDisplay.firstChild) }
statusDisplay.appendChild(document.createTextNode(s))
status = s
}
const canv = document.querySelector("#out")
// TODO things
canv.style.width = canv.style.height = Math.min(512, window.innerWidth - 16) + "px"
const ctx = canv.getContext("2d")
const source = `
${document.querySelector("#fractalart-code").innerHTML}
onmessage = e => run(e.data[0], e.data[1])
`
// be nice to foolish single threaded browsers
// offload to worker thread
const worker = new Worker(URL.createObjectURL(new Blob([source], {type: 'application/javascript'})))
worker.onmessage = e => {
const im = e.data
canv.width = canv.height = im.width
ctx.putImageData(im, 0, 0)
updateStatus(`Idle (${Date.now() - startTime}ms)`)
}
worker.onerror = e => {
console.warn(e)
updateStatus(e.message)
}
const generate = () => {
if (status !== "Working") {
updateStatus("Working")
startTime = Date.now()
const size = parseInt(readInput("width"))
worker.postMessage([ctx.createImageData(size, size), {
hue: document.getElementById("hue-random").checked ? Math.random() : parseFloat(readInput("hue")),
saturation: parseFloat(readInput("saturation")),
lightness: parseFloat(readInput("lightness")),
variance: parseFloat(readInput("variance")),
bias: parseFloat(readInput("bias"))
}])
}
}
document.querySelector("#generate").addEventListener("click", generate)
generate()
}
</script>

View File

@ -1,6 +1,6 @@
--- ---
title: IncDec title: IncDec
slug: incdec slug: incdec
description: The exciting game of incrementing and decrementing! description: The exciting multiplayer game of incrementing and decrementing! No cheating.
--- ---
If you're reading this, then this is not linking to actual IncDec as it should due to a server misconfiguration. If you're reading this, then this is not linking to actual IncDec as it should due to a server misconfiguration.

View File

@ -1,7 +1,7 @@
--- ---
title: Lorem Ipsum title: Lorem Ipsum
slug: lorem slug: lorem
description: Lorem Ipsum (latin-like placeholder text)... FOREVER. description: Lorem Ipsum (latin-like placeholder text), eternally. Somehow people have left comments at the bottom anyway.
--- ---
<style> <style>

View File

@ -1,7 +1,7 @@
--- ---
title: RPNCalc v2 title: RPNCalc v2
slug: rpncalc2 slug: rpncalc2
description: A Reverse Polish Notation (check wikipedia) calculator, version 2. Buggy and kind of unreliable. This updated version implements subtraction. description: A Reverse Polish Notation (check wikipedia) calculator, version 2. Buggy and kind of unreliable. This updated version implements advanced features such as subtraction.
--- ---
<link rel="stylesheet" href="calc.css"> <link rel="stylesheet" href="calc.css">

View File

@ -16,6 +16,7 @@ const terser = require("terser")
const util = require("util") const util = require("util")
const childProcess = require("child_process") const childProcess = require("child_process")
const chalk = require("chalk") const chalk = require("chalk")
const crypto = require("crypto")
dayjs.extend(customParseFormat) dayjs.extend(customParseFormat)
@ -30,6 +31,38 @@ const outDir = path.join(root, "out")
const buildID = nanoid() const buildID = nanoid()
globalData.buildID = buildID globalData.buildID = buildID
const hexPad = x => Math.round(x).toString(16).padStart(2, "0")
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return `#${hexPad(r * 255)}${hexPad(g * 255)}${hexPad(b * 255)}`
}
const hashColor = (x, s, l) => {
const buf = crypto.createHash("md5").update(x).digest()
const hue = (buf[0] + 256 * buf[1]) % 360
return hslToRgb(hue / 360, s, l)
}
const removeExtension = x => x.replace(/\.[^/.]+$/, "") const removeExtension = x => x.replace(/\.[^/.]+$/, "")
const readFile = path => fsp.readFile(path, { encoding: "utf8" }) const readFile = path => fsp.readFile(path, { encoding: "utf8" })
@ -100,6 +133,8 @@ const applyTemplate = async (template, input, getOutput, options = {}) => {
return page.data return page.data
} }
const addColors = R.map(x => ({ ...x, bgcol: hashColor(x.title, 0.5, 0.85), bordercol: hashColor(x.title, 0.7, 0.6) }))
const processExperiments = async () => { const processExperiments = async () => {
const templates = globalData.templates const templates = globalData.templates
const experiments = await loadDir(experimentDir, (subdirectory, basename) => { const experiments = await loadDir(experimentDir, (subdirectory, basename) => {
@ -120,7 +155,7 @@ const processExperiments = async () => {
{ processMeta: meta => { meta.slug = meta.slug || basename }}) { processMeta: meta => { meta.slug = meta.slug || basename }})
}) })
console.log(chalk.yellow(`${Object.keys(experiments).length} experiments`)) console.log(chalk.yellow(`${Object.keys(experiments).length} experiments`))
globalData.experiments = R.sortBy(x => x.title, R.values(experiments)) globalData.experiments = addColors(R.sortBy(x => x.title, R.values(experiments)))
} }
const processBlog = async () => { const processBlog = async () => {
@ -133,7 +168,7 @@ const processBlog = async () => {
}, { processMeta: meta => { meta.slug = meta.slug || removeExtension(basename) }, processContent: renderMarkdown }) }, { processMeta: meta => { meta.slug = meta.slug || removeExtension(basename) }, processContent: renderMarkdown })
}) })
console.log(chalk.yellow(`${Object.keys(blog).length} blog entries`)) console.log(chalk.yellow(`${Object.keys(blog).length} blog entries`))
globalData.blog = R.sortBy(x => x.updated ? -x.updated.valueOf() : 0, R.values(blog)) globalData.blog = addColors(R.sortBy(x => x.updated ? -x.updated.valueOf() : 0, R.values(blog)))
} }
const processErrorPages = () => { const processErrorPages = () => {

View File

@ -37,6 +37,18 @@ main.blog-post
max-width: 40em max-width: 40em
text-align: justify text-align: justify
.blog, .experiments, .atl
margin: -0.5em
margin-bottom: 0
display: flex
flex-wrap: wrap
> div
min-width: 20em
background: #eee
margin: 0.5em
padding: 0.5em
flex: 1 1 20%
main main
margin-top: 1em margin-top: 1em
@ -71,19 +83,9 @@ ul
margin-bottom: 0.5em margin-bottom: 0.5em
.ring .ring
.atl
display: flex
flex-wrap: wrap
margin: -0.5rem
.art .art
flex: 1 1 0
display: flex
flex-direction: column flex-direction: column
margin: 0.5rem flex: 1 1 25%
padding: 0.5rem
background: #eee
min-width: 20em
.sum .sum
font-size: 0.8rem font-size: 0.8rem
flex: 1 1 0 flex: 1 1 0

View File

@ -5,21 +5,22 @@ block content
h2 Blog h2 Blog
p. p.
Stuff I say, conveniently accessible on the internet. Stuff I say, conveniently accessible on the internet.
ul.blog div.blog
each post in posts each post in posts
li div(style=`background: ${post.bgcol}; border: 1px solid ${post.bordercol}`)
a.title(href=`/${post.slug}/`)= post.title div
span= ` ` a.title(href=`/${post.slug}/`)= post.title
span.description!= post.description small= renderDate(post.updated)
div.description!= post.description
h2 Experiments h2 Experiments
p. p.
Various random somewhat useless web projects I have put together over many years. Made with at least four different JS frameworks. Various random somewhat useless web projects I have put together over many years. Made with at least four different JS frameworks.
ul.experiments div.experiments
each experiment in experiments each experiment in experiments
li div(style=`background: ${experiment.bgcol}; border: 1px solid ${experiment.bordercol}`)
a.title(href=`/${experiment.slug}/`)= experiment.title div
span= ` ` a.title(href=`/${experiment.slug}/`)= experiment.title
span.description!= experiment.description span.description!= experiment.description
p Get updates to the blog (not experiments) in your favourite RSS reader using the <a href="/rss.xml">RSS feed</a>. p Get updates to the blog (not experiments) in your favourite RSS reader using the <a href="/rss.xml">RSS feed</a>.