Compare commits
9 commits
main
...
photos-car
Author | SHA1 | Date | |
---|---|---|---|
6dbf3da09b | |||
afb59e7862 | |||
da994bd6b7 | |||
ad9f83ed39 | |||
bb655bcf30 | |||
af957c0150 | |||
00d2e9263d | |||
baad04e7a4 | |||
81a5507e8f |
2
.gitattributes
vendored
|
@ -1,5 +1,3 @@
|
||||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.pxm filter=lfs diff=lfs merge=lfs -text
|
*.pxm filter=lfs diff=lfs merge=lfs -text
|
||||||
*.mov filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
||||||
|
|
5
.gitignore
vendored
|
@ -1,11 +1,6 @@
|
||||||
node_modules/
|
|
||||||
public/
|
public/
|
||||||
/documentation/mirrors/
|
|
||||||
/resources/
|
/resources/
|
||||||
.hugo_build.lock
|
.hugo_build.lock
|
||||||
*.log
|
*.log
|
||||||
*.orig
|
*.orig
|
||||||
*~
|
*~
|
||||||
|
|
||||||
# Backup files for Markdown files processed in-place with sed
|
|
||||||
*.md-e
|
|
||||||
|
|
18
.gitmodules
vendored
|
@ -1,18 +0,0 @@
|
||||||
[submodule "themes/platters"]
|
|
||||||
path = themes/platters
|
|
||||||
url = nutmeg:git/hugo-theme-platters.git
|
|
||||||
[submodule "themes/termlite"]
|
|
||||||
path = themes/termlite
|
|
||||||
url = git@github.com:erynofwales/hugo-theme-termlite.git
|
|
||||||
[submodule "themes/resource-builders"]
|
|
||||||
path = themes/resource-builders
|
|
||||||
url = git@github.com:erynofwales/hugo-resource-builders.git
|
|
||||||
[submodule "themes/image-utils"]
|
|
||||||
path = themes/image-utils
|
|
||||||
url = git@github.com:erynofwales/hugo-image-utilities.git
|
|
||||||
[submodule "themes/photostream"]
|
|
||||||
path = themes/photostream
|
|
||||||
url = git@github.com:erynofwales/hugo-theme-photostream.git
|
|
||||||
[submodule "themes/feeds"]
|
|
||||||
path = themes/feeds
|
|
||||||
url = git@github.com:erynofwales/hugo-theme-feeds.git
|
|
|
@ -1,15 +0,0 @@
|
||||||
snippet jp "lang jp shortcode" w
|
|
||||||
{{< lang jp >}}$1{{< /lang >}}
|
|
||||||
endsnippet
|
|
||||||
|
|
||||||
snippet jpp "lang jp shortcode with expansion" w
|
|
||||||
{{% lang jp %}}$1{{% /lang %}}
|
|
||||||
endsnippet
|
|
||||||
|
|
||||||
snippet tess "tess shortcode" w
|
|
||||||
{{< tess >}}
|
|
||||||
endsnippet
|
|
||||||
|
|
||||||
snippet ruby "ruby shortcode" w
|
|
||||||
{{< ruby "$1" >}}$2{{< /ruby >}}
|
|
||||||
endsnippet
|
|
|
@ -1,4 +0,0 @@
|
||||||
-- Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
vim.bo.shiftwidth = 2
|
|
||||||
vim.bo.softtabstop = 2
|
|
|
@ -1,6 +0,0 @@
|
||||||
-- Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
local root = gitTopLevelDirectory()
|
|
||||||
vim.opt_local.path:prepend(root .. "/assets/scripts/**")
|
|
||||||
vim.opt_local.path:prepend(root .. "/assets/styles/**")
|
|
||||||
vim.opt_local.path:prepend(root .. "/layouts/**")
|
|
|
@ -1,4 +0,0 @@
|
||||||
-- Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
local root = gitTopLevelDirectory()
|
|
||||||
vim.opt_local.path:prepend(root .. "/assets/scripts/**")
|
|
|
@ -1,11 +0,0 @@
|
||||||
-- Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
vim.bo.textwidth = 80
|
|
||||||
|
|
||||||
vim.cmd [[
|
|
||||||
iabbrev tokyo Tōkyō
|
|
||||||
iabbrev Tokyo Tōkyō
|
|
||||||
iabbrev kyoto Kyōto
|
|
||||||
iabbrev Kyoto Kyōto
|
|
||||||
iabbrev xx ×
|
|
||||||
]]
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
local filetypedetectGroup = vim.api.nvim_create_augroup("HugoHTMLTemplates", {clear = true})
|
local filetypedetectGroup = vim.api.nvim_create_augroup("HugoHTMLTemplates", {clear = true})
|
||||||
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
|
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
|
||||||
pattern = {"**/layouts/**/*.html"},
|
pattern = {"*/layouts/*.html"},
|
||||||
group = filetypedetectGroup,
|
group = filetypedetectGroup,
|
||||||
command = "set ft=gohtmltmpl",
|
command = "set ft=gohtmltmpl",
|
||||||
})
|
})
|
||||||
|
|
15
Makefile
|
@ -1,9 +1,5 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
BUILD_DIR=public
|
|
||||||
|
|
||||||
CONTENT_PATH=content
|
|
||||||
|
|
||||||
DEPLOY_USER=eryn
|
DEPLOY_USER=eryn
|
||||||
DEPLOY_HOSTNAME=nutmeg.erynwells.me
|
DEPLOY_HOSTNAME=nutmeg.erynwells.me
|
||||||
DEPLOY_PATH=/srv/www/erynwells.me/html
|
DEPLOY_PATH=/srv/www/erynwells.me/html
|
||||||
|
@ -16,16 +12,13 @@ NETHACK_LOGFILE_DATA_FILE=data/nethack/logfile/$(HOSTNAME).json
|
||||||
|
|
||||||
.PHONY: site deploy clean
|
.PHONY: site deploy clean
|
||||||
|
|
||||||
site:
|
site: nethack
|
||||||
@echo "Building site"
|
@echo "Building site"
|
||||||
hugo --buildFuture --enableGitInfo --destination "$(BUILD_DIR)"
|
hugo
|
||||||
|
|
||||||
deploy: site
|
deploy: site
|
||||||
@echo "Deploying to $(DEPLOY_LOCATION)"
|
@echo "Deploying to $(DEPLOY_LOCATION)"
|
||||||
rsync -avz --no-times --no-perms --delete "$(BUILD_DIR)/" "$(DEPLOY_LOCATION)"
|
rsync -avz --no-times --no-perms --delete public/ $(DEPLOY_LOCATION)
|
||||||
git tag -f deploy-$(shell date +%Y-%m-%d)
|
|
||||||
|
|
||||||
deployall: nethack deploy
|
|
||||||
|
|
||||||
nethack: nethack-logfile nethack-commit
|
nethack: nethack-logfile nethack-commit
|
||||||
|
|
||||||
|
@ -39,4 +32,4 @@ nethack-commit: $(NETHACK_LOGFILE_DATA_FILE)
|
||||||
if ! git diff --quiet $<; then git commit -m "Update Nethack logfile for $(HOSTNAME)" -- $<; fi
|
if ! git diff --quiet $<; then git commit -m "Update Nethack logfile for $(HOSTNAME)" -- $<; fi
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf "$(BUILD_DIR)/"
|
rm -rf public/
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
title: "{{ replace .Name "-" " " | title }}"
|
|
||||||
slug: link-{{ .Name }}
|
|
||||||
date: {{ .Date }}
|
|
||||||
categories: links
|
|
||||||
draft: true
|
|
||||||
tags: []
|
|
||||||
---
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
title: "Notes on {{ time.Now.Format "2006" }}W%%WEEK_NUMBER%%"
|
|
||||||
slug: weeknotes-{{ time.Now.Format "2006" }}w%%WEEK_NUMBER%%
|
|
||||||
date: {{ .Date | time.Format "2006-01-02" }}
|
|
||||||
categories: weeknotes
|
|
||||||
tags:
|
|
||||||
- Weeknotes
|
|
||||||
draft: true
|
|
||||||
---
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/************************
|
|
||||||
* PARAGRAPH-SPACED LIST
|
|
||||||
************************/
|
|
||||||
|
|
||||||
p + .paragraph-spaced-list {
|
|
||||||
margin-block-start: var(--space-paragraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
.paragraph-spaced-list {
|
|
||||||
li + li {
|
|
||||||
margin-block-start: var(--space-paragraph);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
.home-latest {
|
|
||||||
display: grid;
|
|
||||||
grid-column: main-start / main-end;
|
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
|
||||||
grid-template-rows: min-content min-content;
|
|
||||||
grid-template-areas:
|
|
||||||
"blog1 blog1 blog2 blog2"
|
|
||||||
"photo1 photo2 photo3 photo4";
|
|
||||||
|
|
||||||
.home-latest__blog {
|
|
||||||
margin-block-end: var(--space-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-latest__blog:nth-of-type(1) {
|
|
||||||
grid-area: blog1;
|
|
||||||
border-right: 2px dashed var(--gray6);
|
|
||||||
padding-inline-end: var(--space-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-latest__blog:nth-of-type(2) {
|
|
||||||
grid-area: blog2;
|
|
||||||
padding-inline-start: var(--space-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-latest__photo {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 480px) {
|
|
||||||
.home-latest {
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
grid-template-rows: repeat(min-content, 4);
|
|
||||||
grid-template-areas:
|
|
||||||
"blog1 blog2"
|
|
||||||
"photo1 photo2"
|
|
||||||
"photo3 photo4";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p + .home-latest {
|
|
||||||
margin-block-start: var(--space-paragraph);
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************
|
|
||||||
* NETHACK LOGFILE
|
|
||||||
******************/
|
|
||||||
|
|
||||||
#dungeon-background {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: -1;
|
|
||||||
filter: brightness(0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logfile {
|
|
||||||
margin-inline-start: 0;
|
|
||||||
padding-inline-start: 0;
|
|
||||||
|
|
||||||
.nethack-logentry {
|
|
||||||
align-items: first baseline;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content min-content auto min-content;
|
|
||||||
grid-template-areas:
|
|
||||||
"list-marker entry-marker entry-date entry-character-descriptor"
|
|
||||||
". . entry-description entry-description"
|
|
||||||
". . entry-stats entry-stats";
|
|
||||||
gap: var(--space-xs);
|
|
||||||
margin-inline-start: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.nethack-logentry {
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-block-end: var(--space-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
grid-area: list-marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__marker {
|
|
||||||
grid-area: entry-marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__date {
|
|
||||||
grid-area: entry-date;
|
|
||||||
line-height: 1;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__character-descriptor {
|
|
||||||
font-family: var(--font-family-monospace);
|
|
||||||
font-size: var(--text-s);
|
|
||||||
grid-area: entry-character-descriptor;
|
|
||||||
line-height: 1;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__description {
|
|
||||||
grid-area: entry-description;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__stats {
|
|
||||||
border: 0;
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
font-family: var(--font-family-monospace);
|
|
||||||
font-size: var(--text-s);
|
|
||||||
grid-area: entry-stats;
|
|
||||||
margin-block: 0;
|
|
||||||
width: 100%;
|
|
||||||
-webkit-border-horizontal-spacing: 0;
|
|
||||||
-webkit-border-vertical-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__stats {
|
|
||||||
padding: 0;
|
|
||||||
text-transform: uppercase;
|
|
||||||
vertical-align: bottom;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
thead {
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nethack-logentry__score,
|
|
||||||
.nethack-logentry__hp,
|
|
||||||
.nethack-logentry__level {
|
|
||||||
width: 16rem;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 464 KiB |
|
@ -402,11 +402,11 @@ class TunnelGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dig a corridor from fromPoint to toPoint, assuming that both points are
|
* Dig a corridor from fromPoint to toPoint, assuming that both points are adjacent to valid locations for doors on
|
||||||
* adjacent to valid locations for doors on the map.
|
* the map.
|
||||||
*
|
*
|
||||||
* This is as close a copy of dig_corridor in the Nethack source as I could
|
* This is as close a copy of dig_corridor in the Nethack source as I could muster. It's not exactly pretty. This
|
||||||
* muster. It's not exactly pretty. This method assumed
|
* method assumed
|
||||||
*/
|
*/
|
||||||
#digCorridorFromPointToPoint(p, fromPoint, toPoint) {
|
#digCorridorFromPointToPoint(p, fromPoint, toPoint) {
|
||||||
const MAX_STEPS = 500;
|
const MAX_STEPS = 500;
|
||||||
|
@ -711,8 +711,6 @@ new p5(p => {
|
||||||
|
|
||||||
p.setup = () => {
|
p.setup = () => {
|
||||||
const container = document.querySelector('#dungeon-background');
|
const container = document.querySelector('#dungeon-background');
|
||||||
console.assert(container, "Missing #dungeon-background element");
|
|
||||||
|
|
||||||
canvasWidth = parseFloat(getComputedStyle(container).width);
|
canvasWidth = parseFloat(getComputedStyle(container).width);
|
||||||
canvasHeight = parseFloat(getComputedStyle(container).height);
|
canvasHeight = parseFloat(getComputedStyle(container).height);
|
||||||
|
|
||||||
|
@ -725,19 +723,16 @@ new p5(p => {
|
||||||
|
|
||||||
const gridBounds = Rect.fromCoordinates(
|
const gridBounds = Rect.fromCoordinates(
|
||||||
0, 0,
|
0, 0,
|
||||||
Math.max(80, Math.ceil(canvasWidth / CELL_WIDTH) - 1),
|
Math.ceil(canvasWidth / CELL_WIDTH) - 1, Math.ceil(canvasHeight / CELL_HEIGHT) - 1
|
||||||
Math.max(24, Math.ceil(canvasHeight / CELL_HEIGHT) - 1)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`Generating grid with size ${gridBounds.size.width} x ${gridBounds.size.height}`);
|
console.log(`Generating grid with size ${gridBounds.size.width} x ${gridBounds.size.height}`);
|
||||||
|
|
||||||
grid = new Grid(gridBounds.size.width, gridBounds.size.height);
|
grid = new Grid(gridBounds.size.width, gridBounds.size.height);
|
||||||
grid.generate(p, NRandomRoomsGenerator, TunnelGenerator);
|
grid.generate(p, NRandomRoomsGenerator, TunnelGenerator);
|
||||||
};
|
}
|
||||||
|
|
||||||
p.draw = () => {
|
p.draw = () => {
|
||||||
console.log("Drawing");
|
|
||||||
|
|
||||||
p.textSize(CELL_HEIGHT);
|
p.textSize(CELL_HEIGHT);
|
||||||
|
|
||||||
for (let y = 0; y < grid.height; y++) {
|
for (let y = 0; y < grid.height; y++) {
|
||||||
|
|
304
assets/scripts/photos/carousel.js
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
class Carousel {
|
||||||
|
carousel = null;
|
||||||
|
#items = null;
|
||||||
|
#caption = null;
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
this.carousel = element;
|
||||||
|
this.#caption = element.querySelector("figcaption");
|
||||||
|
}
|
||||||
|
|
||||||
|
get items() {
|
||||||
|
if (this.#items === null) {
|
||||||
|
const boundingRect = this.carousel.getBoundingClientRect();
|
||||||
|
this.#items = Array.from(this.carousel.querySelectorAll("ul > li")).map(item => {
|
||||||
|
let itemRect = item.getClientRects()[0];
|
||||||
|
let relativeX = itemRect.x - boundingRect.x;
|
||||||
|
return new CarouselItem(item, relativeX);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#items;
|
||||||
|
}
|
||||||
|
|
||||||
|
get scrollContainer() {
|
||||||
|
return this.carousel.querySelector("ul");
|
||||||
|
}
|
||||||
|
|
||||||
|
setCaption(caption) {
|
||||||
|
if (!this.#caption) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#caption.innerHTML = caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(index) {
|
||||||
|
this.scrollContainer.scrollTo(this.items[index].relativeX, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpScrollEventListener(photoParametersTable) {
|
||||||
|
let previousScrollLeft = null;
|
||||||
|
let isScrollingLeft = true;
|
||||||
|
|
||||||
|
this.scrollContainer.addEventListener("scroll", event => {
|
||||||
|
const target = event.target;
|
||||||
|
const scrollLeft = target.scrollLeft;
|
||||||
|
|
||||||
|
const carouselRect = target.getBoundingClientRect();
|
||||||
|
const carouselRectHorizontalCenter = carouselRect.width / 2.0;
|
||||||
|
|
||||||
|
if (previousScrollLeft !== null) {
|
||||||
|
isScrollingLeft = scrollLeft > previousScrollLeft;
|
||||||
|
}
|
||||||
|
previousScrollLeft = scrollLeft;
|
||||||
|
|
||||||
|
this.items.forEach(item => {
|
||||||
|
const itemRelativeXCoordinate = isScrollingLeft ? item.relativeX - scrollLeft : item.relativeMaxX - scrollLeft;
|
||||||
|
|
||||||
|
const itemWasLeftOfContainerCenter = item.isLeftOfContainerCenter;
|
||||||
|
const itemIsLeftOfContainerCenter = itemRelativeXCoordinate < carouselRectHorizontalCenter;
|
||||||
|
item.isLeftOfContainerCenter = itemIsLeftOfContainerCenter;
|
||||||
|
|
||||||
|
if ( (isScrollingLeft && (!itemWasLeftOfContainerCenter && itemIsLeftOfContainerCenter))
|
||||||
|
|| (!isScrollingLeft && (itemWasLeftOfContainerCenter && !itemIsLeftOfContainerCenter))) {
|
||||||
|
this.setCaption(item.title);
|
||||||
|
photoParametersTable.setMakeModel(item.make, item.model);
|
||||||
|
photoParametersTable.setLocation(item.latitude, item.longitude);
|
||||||
|
photoParametersTable.setMegapixels(item.megapixels);
|
||||||
|
photoParametersTable.setSize(item.width, item.height);
|
||||||
|
photoParametersTable.setISO(item.iso);
|
||||||
|
photoParametersTable.setFocalLength(item.focalLength);
|
||||||
|
photoParametersTable.setFNumber(item.fNumber);
|
||||||
|
photoParametersTable.setExposureTime(item.exposureTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CarouselItem {
|
||||||
|
element = null;
|
||||||
|
relativeX = 0;
|
||||||
|
isLeftOfContainerCenter = false;
|
||||||
|
|
||||||
|
constructor(element, relativeX) {
|
||||||
|
this.element = element;
|
||||||
|
this.relativeX = relativeX
|
||||||
|
}
|
||||||
|
|
||||||
|
get relativeMaxX() {
|
||||||
|
const rect = this.element.getBoundingClientRect();
|
||||||
|
return this.relativeX + rect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() { return this.element.dataset.title; }
|
||||||
|
get latitude() { return this.element.dataset.latitude; }
|
||||||
|
get longitude() { return this.element.dataset.longitude; }
|
||||||
|
get megapixels() { return this.element.dataset.megapixels; }
|
||||||
|
get width() { return this.element.dataset.width; }
|
||||||
|
get height() { return this.element.dataset.height; }
|
||||||
|
get make() { return this.element.dataset.make; }
|
||||||
|
get model() { return this.element.dataset.model; }
|
||||||
|
get iso() { return this.element.dataset.iso; }
|
||||||
|
get focalLength() { return this.element.dataset.focalLength; }
|
||||||
|
get fNumber() { return this.element.dataset.fNumber; }
|
||||||
|
get exposureTime() { return this.element.dataset.exposureTime; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class PhotoParametersTable {
|
||||||
|
tableElement;
|
||||||
|
|
||||||
|
#makeModelElement;
|
||||||
|
#latitudeElement;
|
||||||
|
#longitudeElement;
|
||||||
|
#megapixelsElement;
|
||||||
|
#widthElement;
|
||||||
|
#heightElement;
|
||||||
|
#isoElement;
|
||||||
|
#focalLengthElement;
|
||||||
|
#fNumberElement;
|
||||||
|
#exposureTimeElement;
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
this.tableElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMakeModel(make, model) {
|
||||||
|
if (!this.#makeModelElement) {
|
||||||
|
this.#makeModelElement = this.#getElementWithQuerySelector("td.make-model");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.#makeModelElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeModel = "";
|
||||||
|
if (make && model) {
|
||||||
|
return `${make} ${model}`;
|
||||||
|
} else if (make) {
|
||||||
|
return make;
|
||||||
|
} else if (model) {
|
||||||
|
return model;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#makeModelElement.innerHTML = makeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocation(latitude, longitude) {
|
||||||
|
let latitudeElement = this.#latitudeElement;
|
||||||
|
if (!latitudeElement) {
|
||||||
|
latitudeElement = this.#getElementWithQuerySelector("td.location > data.latitude");
|
||||||
|
this.#latitudeElement = latitudeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let longitudeElement = this.#longitudeElement;
|
||||||
|
if (!longitudeElement) {
|
||||||
|
longitudeElement = this.#getElementWithQuerySelector("td.location > data.longitude");
|
||||||
|
this.#longitudeElement = longitudeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latitudeElement || !longitudeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
latitudeElement.innerHTML = latitude;
|
||||||
|
longitudeElement.innerHTML = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMegapixels(megapixels) {
|
||||||
|
let megapixelsElement = this.#megapixelsElement;
|
||||||
|
if (!megapixelsElement) {
|
||||||
|
megapixelsElement = this.#getElementWithQuerySelector("td.size > data.megapixels");
|
||||||
|
this.#megapixelsElement = megapixelsElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!megapixelsElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
megapixelsElement.innerHTML = megapixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(width, height) {
|
||||||
|
let widthElement = this.#widthElement;
|
||||||
|
if (!widthElement) {
|
||||||
|
widthElement = this.#getElementWithQuerySelector("td.size > data.width");
|
||||||
|
this.#widthElement = widthElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let heightElement = this.#heightElement;
|
||||||
|
if (heightElement) {
|
||||||
|
heightElement = this.#getElementWithQuerySelector("td.size > data.height");
|
||||||
|
this.#heightElement = heightElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!widthElement || !heightElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
widthElement.innerHTML = width;
|
||||||
|
heightElement.innerHTML = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
setISO(iso) {
|
||||||
|
let isoElement = this.#isoElement;
|
||||||
|
if (!isoElement) {
|
||||||
|
isoElement = this.#getElementWithQuerySelector("td.iso");
|
||||||
|
this.#isoElement = isoElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isoElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isoElement.innerHTML = `ISO ${iso}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocalLength(focalLength) {
|
||||||
|
let focalLengthElement = this.#focalLengthElement;
|
||||||
|
if (!focalLengthElement) {
|
||||||
|
focalLengthElement = this.#getElementWithQuerySelector("td.focal-length");
|
||||||
|
this.#focalLengthElement = focalLengthElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!focalLengthElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
focalLengthElement.innerHTML = `${focalLength} mm`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFNumber(f) {
|
||||||
|
let fNumberElement = this.#fNumberElement;
|
||||||
|
if (!fNumberElement) {
|
||||||
|
fNumberElement = this.#getElementWithQuerySelector("td.f-number");
|
||||||
|
this.#fNumberElement = fNumberElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fNumberElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fNumberElement.innerHTML = `ƒ${f}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setExposureTime(exposureTime) {
|
||||||
|
let exposureTimeElement = this.#exposureTimeElement;
|
||||||
|
if (!exposureTimeElement) {
|
||||||
|
exposureTimeElement = this.#getElementWithQuerySelector("td.exposure-time");
|
||||||
|
this.#exposureTimeElement = exposureTimeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exposureTimeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exposureTimeElement.innerHTML = `${exposureTime} s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getElementWithQuerySelector(query) {
|
||||||
|
const element = this.tableElement.querySelector(query);
|
||||||
|
if (!element) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
const carouselElements = document.querySelectorAll("figure.carousel");
|
||||||
|
if (carouselElements.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allCarousels = Array.from(carouselElements).sort((a, b) => {
|
||||||
|
const aRect = a.getBoundingClientRect();
|
||||||
|
const bRect = a.getBoundingClientRect();
|
||||||
|
return aRect.top - bRect.top;
|
||||||
|
}).map(elem => new Carousel(elem));
|
||||||
|
|
||||||
|
let photoParametersTable = null;
|
||||||
|
const photoParamsTableElement = document.querySelector(".photo-params table");
|
||||||
|
if (photoParamsTableElement) {
|
||||||
|
photoParametersTable = new PhotoParametersTable(photoParamsTableElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
allCarousels.forEach(carousel => {
|
||||||
|
carousel.setUpScrollEventListener(photoParametersTable);
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = new URL(window.location);
|
||||||
|
let photoGetParameter = url.searchParams.get("photo");
|
||||||
|
if (photoGetParameter) {
|
||||||
|
try {
|
||||||
|
photoGetParameter = parseInt(photoGetParameter);
|
||||||
|
allCarousels[0].scrollTo(photoGetParameter);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Unable to parse 'photo' GET parameter as integer:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
111
assets/scripts/photos/list.js
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
class Item {
|
||||||
|
static allItems = [];
|
||||||
|
|
||||||
|
element;
|
||||||
|
|
||||||
|
#mouseoverRegionOffsets;
|
||||||
|
#currentRegion = 0;
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
this.element = element;
|
||||||
|
this.setUpEventHandlers();
|
||||||
|
|
||||||
|
element._item = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this.element.getAttribute("title");
|
||||||
|
}
|
||||||
|
|
||||||
|
get numberOfImages() {
|
||||||
|
const numberOfImages = this.element.dataset.numberOfImages;
|
||||||
|
if (typeof numberOfImages === "undefined" || numberOfImages === null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return parseInt(numberOfImages);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Unable to parse data-number-of-images attribute:", numberOfImages);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get mouseoverRegionOffsets() {
|
||||||
|
let offsets = this.#mouseoverRegionOffsets;
|
||||||
|
|
||||||
|
if (!offsets) {
|
||||||
|
const numberOfImages = this.numberOfImages;
|
||||||
|
offsets = new Array(numberOfImages);
|
||||||
|
|
||||||
|
const rect = this.element.getBoundingClientRect();
|
||||||
|
const widthOfRegion = rect.width / numberOfImages;
|
||||||
|
for (let i = 0; i < numberOfImages; i++) {
|
||||||
|
offsets[i] = i * widthOfRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#mouseoverRegionOffsets = offsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpEventHandlers() {
|
||||||
|
if (this.numberOfImages <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.addEventListener("mousemove", event => {
|
||||||
|
const offsetX = event.offsetX;
|
||||||
|
|
||||||
|
let indexOfRegion = -1;
|
||||||
|
for (let regionOffset of this.mouseoverRegionOffsets) {
|
||||||
|
if (regionOffset > offsetX) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
indexOfRegion += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indexOfRegion !== this.#currentRegion) {
|
||||||
|
console.debug(`Mouse moved at ${offsetX}, ${event.offsetY} in cell for '${this.title}' in region ${indexOfRegion}`);
|
||||||
|
// TODO: Set image to images[indexOfRegion]
|
||||||
|
this.#currentRegion = indexOfRegion;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.element.addEventListener("mouseout", event => {
|
||||||
|
console.debug(`Mouse left cell for '${this.title}'`);
|
||||||
|
// TODO: Reset to the first image.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addToIntersectionObserver(intersectionObserver) {
|
||||||
|
intersectionObserver.observe(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleIntersectionObservation(entry, observer) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
console.debug(`Cell for ${this.title} entered the viewport`);
|
||||||
|
} else {
|
||||||
|
console.debug(`Cell for ${this.title} left the viewport`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let intersectionObserver;
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", event => {
|
||||||
|
intersectionObserver = new IntersectionObserver((entries, observer) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
entry.target._item.handleIntersectionObservation(entry, observer);
|
||||||
|
});
|
||||||
|
}, { threshold: 0.5 });
|
||||||
|
|
||||||
|
Item.allItems = Array.from(document.querySelectorAll(".photos.list > a")).map(cell => {
|
||||||
|
const item = new Item(cell)
|
||||||
|
item.addToIntersectionObserver(intersectionObserver);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,4 +1,8 @@
|
||||||
import rr from "scripts/lib/railroad.js";
|
{{ with resources.Get "scripts/lib/railroad.js" | fingerprint "sha512" }}
|
||||||
|
import rr from "{{ .RelPermalink }}";
|
||||||
|
{{ else }}
|
||||||
|
{{ errorf "Unable to get railroad.js resource" }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
class RailroadDiagramManager {
|
class RailroadDiagramManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
class RubiksCubeScrambler extends HTMLElement {
|
|
||||||
static #RandomMoveHysteresisMaxLength = 2;
|
|
||||||
|
|
||||||
#shadowRoot;
|
|
||||||
#movesListElement;
|
|
||||||
|
|
||||||
#numberOfMovesToGenerate = 25;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.#shadowRoot = this.attachShadow({ mode: "open" });
|
|
||||||
}
|
|
||||||
|
|
||||||
scramble() {
|
|
||||||
console.log("Randomizing Rubik's cube...");
|
|
||||||
|
|
||||||
const movesList = this.#movesListElement;
|
|
||||||
|
|
||||||
while (movesList.childElementCount > this.#numberOfMovesToGenerate) {
|
|
||||||
movesList.removeChild(movesList.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
let randomMoveHysteresis = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < this.#numberOfMovesToGenerate; i++) {
|
|
||||||
const randomMove = this.#randomMove(randomMoveHysteresis);
|
|
||||||
|
|
||||||
let moveItem;
|
|
||||||
if (i < movesList.childElementCount) {
|
|
||||||
moveItem = movesList.children[i];
|
|
||||||
} else {
|
|
||||||
moveItem = document.createElement("li");
|
|
||||||
movesList.appendChild(moveItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
moveItem.classList.add("scrambler__move");
|
|
||||||
moveItem.classList.remove("scrambler__move--start", "scrambler__move--end");
|
|
||||||
|
|
||||||
if (randomMove.includes("2")) {
|
|
||||||
moveItem.classList.add("scrambler__move--start");
|
|
||||||
} else if (randomMove.includes("'")) {
|
|
||||||
moveItem.classList.add("scrambler__move--end");
|
|
||||||
}
|
|
||||||
|
|
||||||
moveItem.innerText = randomMove;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#randomMove(hysteresis) {
|
|
||||||
const faces = "FBLRUD";
|
|
||||||
|
|
||||||
let move;
|
|
||||||
do {
|
|
||||||
move = faces.charAt(Math.floor(Math.random() * faces.length));
|
|
||||||
} while (hysteresis && hysteresis.includes(move));
|
|
||||||
|
|
||||||
if (hysteresis) {
|
|
||||||
hysteresis.unshift(move);
|
|
||||||
while (hysteresis.length > RubiksCubeScrambler.#RandomMoveHysteresisMaxLength) {
|
|
||||||
hysteresis.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const modifierFactor = Math.random();
|
|
||||||
if (modifierFactor < 0.33333) {
|
|
||||||
move = "2" + move;
|
|
||||||
} else if (modifierFactor < 0.666666) {
|
|
||||||
move = move + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
|
|
||||||
#removeAllMoves() {
|
|
||||||
const element = this.#movesListElement;
|
|
||||||
while (element.hasChildNodes()) {
|
|
||||||
element.removeChild(element.lastChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Custom Element
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
let template = document.getElementById("rubiks-cube-scrambler-template");
|
|
||||||
console.assert(template, "Couldn't find RubiksCubeScrambler component template in the document");
|
|
||||||
|
|
||||||
const shadowRoot = this.#shadowRoot;
|
|
||||||
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
||||||
|
|
||||||
this.#movesListElement = shadowRoot.querySelector(".scrambler__move-list");
|
|
||||||
|
|
||||||
shadowRoot
|
|
||||||
.querySelector("button[name='scramble']")
|
|
||||||
.addEventListener("click", () => this.scramble());
|
|
||||||
|
|
||||||
const patternLengthInputElement = shadowRoot.querySelector(".scrambler__pattern-length > input");
|
|
||||||
patternLengthInputElement.value = this.#numberOfMovesToGenerate;
|
|
||||||
patternLengthInputElement.addEventListener("input", event => {
|
|
||||||
try {
|
|
||||||
const integerValue = parseInt(event.target.value);
|
|
||||||
this.#numberOfMovesToGenerate = integerValue;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Non-integer value of pattern length field", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.scramble();
|
|
||||||
}
|
|
||||||
|
|
||||||
attributeChangedCallback(name, oldValue, newValue) {
|
|
||||||
console.debug("RubiksCubeScrambler attribute changed", name, oldValue, newValue);
|
|
||||||
if (name === "count") {
|
|
||||||
try {
|
|
||||||
let newIntValue = parseInt(newValue);
|
|
||||||
this.#numberOfMovesToGenerate = newIntValue;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("`count` attribute should have an integer value.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.customElements.define("rubiks-cube-scrambler", RubiksCubeScrambler);
|
|
|
@ -1,169 +0,0 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
class RubySwitch extends HTMLElement {
|
|
||||||
static controlSizeInPixels = 32;
|
|
||||||
static thumbTransitionDuration = 0.1;
|
|
||||||
|
|
||||||
static settings = [
|
|
||||||
{
|
|
||||||
id: "ruby-switch-none",
|
|
||||||
value: "none",
|
|
||||||
label: "あ"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ruby-switch-both",
|
|
||||||
value: "both",
|
|
||||||
label: "<ruby>あ<rt>a</rt></ruby>",
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ruby-switch-hidden",
|
|
||||||
value: "hidden",
|
|
||||||
label: "ab"
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
#root;
|
|
||||||
#thumb;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.#updateValue(RubySwitch.settings.find(obj => obj.default).value);
|
|
||||||
this.addEventListener("RubyStyleChanged", event => {
|
|
||||||
this.#updateValue(event.detail.style);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.#root = this.attachShadow({ mode: "closed" });
|
|
||||||
this.#buildShadowDOM();
|
|
||||||
|
|
||||||
this.#updateThumbPosition(this.#root.querySelector(".control[data-default]"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#updateValue(style) {
|
|
||||||
this.setAttribute("value", style);
|
|
||||||
}
|
|
||||||
|
|
||||||
get #stylesheet() {
|
|
||||||
const controlSize = RubySwitch.controlSizeInPixels;
|
|
||||||
const halfControlSize = controlSize / 2;
|
|
||||||
|
|
||||||
return `
|
|
||||||
#ruby-controls {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls {
|
|
||||||
border: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: inline grid;
|
|
||||||
grid-template-columns: repeat(3, ${controlSize}px);
|
|
||||||
margin: 0;
|
|
||||||
overflow: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#thumb {
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 2px 2px 6px #ccc;
|
|
||||||
border: 0.5px solid #aaa;
|
|
||||||
border-radius: ${halfControlSize}px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
height: ${controlSize}px;
|
|
||||||
width: ${controlSize}px;
|
|
||||||
z-index: 50;
|
|
||||||
transition: left ${RubySwitch.thumbTransitionDuration}s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control {
|
|
||||||
aspect-ratio: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: ${controlSize}px;
|
|
||||||
width: ${controlSize}px;
|
|
||||||
}
|
|
||||||
|
|
||||||
b {
|
|
||||||
font-weight: normal;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
z-index: 2;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ruby-switch-both {
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
#buildShadowDOM() {
|
|
||||||
const root = this.#root;
|
|
||||||
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.textContent = this.#stylesheet;
|
|
||||||
root.appendChild(style);
|
|
||||||
|
|
||||||
let container = document.createElement("div");
|
|
||||||
container.id = "ruby-controls";
|
|
||||||
root.appendChild(container);
|
|
||||||
|
|
||||||
let controls = document.createElement("div");
|
|
||||||
controls.id = "controls";
|
|
||||||
container.appendChild(controls);
|
|
||||||
|
|
||||||
for (const desc of RubySwitch.settings) {
|
|
||||||
let control = document.createElement("div");
|
|
||||||
control.classList.add("control")
|
|
||||||
control.id = desc.id;
|
|
||||||
|
|
||||||
control.dataset.value = desc.value;
|
|
||||||
if (desc.default) {
|
|
||||||
control.dataset.default = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
controls.appendChild(control);
|
|
||||||
|
|
||||||
control.addEventListener("click", event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.#updateThumbPosition(event.currentTarget);
|
|
||||||
|
|
||||||
this.#root.dispatchEvent(new CustomEvent("RubyStyleChanged", {
|
|
||||||
bubbles: true,
|
|
||||||
composed: true,
|
|
||||||
detail: {
|
|
||||||
style: control.dataset.value,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}, { capture: true });
|
|
||||||
|
|
||||||
const label = document.createElement("b");
|
|
||||||
label.innerHTML = desc.label;
|
|
||||||
control.appendChild(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
const thumb = document.createElement("div");
|
|
||||||
this.#thumb = thumb;
|
|
||||||
thumb.id = "thumb";
|
|
||||||
container.appendChild(thumb);
|
|
||||||
}
|
|
||||||
|
|
||||||
#updateThumbPosition(selectedControl) {
|
|
||||||
const controls = this.#root.querySelector("#controls");
|
|
||||||
|
|
||||||
const trackBoundingRect = controls.getBoundingClientRect();
|
|
||||||
const controlBoundingRect = selectedControl.getBoundingClientRect();
|
|
||||||
|
|
||||||
const offset = controlBoundingRect.left - trackBoundingRect.left;
|
|
||||||
this.#thumb.style.left = `${offset}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ruby-switch", RubySwitch);
|
|
79
assets/styles/blog.css
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
:root {
|
||||||
|
--post-item-highlight-color: #efefef;
|
||||||
|
|
||||||
|
--blog-list-grid-template-columns: minmax(min-content, 10vh) minmax(min-content, 3vh) auto max-content max-content;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--post-item-highlight-color: #121212;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog > .highlight { margin-block-end: var(--body-item-spacing); }
|
||||||
|
|
||||||
|
.post-nav {
|
||||||
|
align-items: baseline;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-nav .next {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-single footer {
|
||||||
|
margin-block-start: var(--body-item-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog.list > ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.blog.list > ul > li {
|
||||||
|
align-items: baseline;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: var(--blog-list-grid-template-columns);
|
||||||
|
margin-block-end: 0.25rem;
|
||||||
|
transition: background-color 0.25s;
|
||||||
|
}
|
||||||
|
.blog.list > ul > li:hover {
|
||||||
|
background-color: var(--post-item-highlight-color);
|
||||||
|
}
|
||||||
|
.blog.list > ul > li > a { color: inherit; }
|
||||||
|
.blog.list > ul > li > a:hover { text-decoration: none; }
|
||||||
|
.blog.list > ul > li > time:nth-child(2) { text-align: end; }
|
||||||
|
.blog.list > ul > li > .draft { align-self: center; }
|
||||||
|
|
||||||
|
@supports (grid-template-columns: subgrid) {
|
||||||
|
.blog.list {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: var(--blog-list-grid-template-columns);
|
||||||
|
}
|
||||||
|
.blog.list > ul {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 0.25rem;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-template-columns: subgrid;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.blog.list > ul > li {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-template-columns: subgrid;
|
||||||
|
margin-block-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog.list > h5 {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
margin-block: 1rem 0;
|
||||||
|
}
|
||||||
|
.blog.list > h5:first-child {
|
||||||
|
margin-block-start: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog.list > ul > li > :first-child {
|
||||||
|
text-align: end;
|
||||||
|
}
|
78
assets/styles/development.css
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/* development.css
|
||||||
|
* Some styles for development UI.
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
|
||||||
|
details:has(.photo-params.debug) {
|
||||||
|
margin-block-end: var(--body-item-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
.draft {
|
||||||
|
color: red;
|
||||||
|
display: inline-block;
|
||||||
|
font-family: 'SF Pro', sans-serif;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draft:before {
|
||||||
|
content: "[";
|
||||||
|
}
|
||||||
|
|
||||||
|
.draft:after {
|
||||||
|
content: "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border: 1px solid var(--separator-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 4px 5px 5px var(--box-shadow-color);
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
position: fixed;
|
||||||
|
left: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > details {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > summary {
|
||||||
|
font-family: var(--font-family-heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > details > table {
|
||||||
|
border: 0;
|
||||||
|
border-color: rgb(var(--dk-gray));
|
||||||
|
display: block;
|
||||||
|
margin-block-start: 0.5em;
|
||||||
|
margin-inline-start: 1em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > details > table > tbody {
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > details > table > tbody + tbody {
|
||||||
|
border-top: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > details > table > tbody > tr > td {
|
||||||
|
border: 1px dotted;
|
||||||
|
border-left: 1px solid;
|
||||||
|
padding: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-page-info > details > table > tbody > tr:nth-child(even) {
|
||||||
|
background-color: var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params.debug {
|
||||||
|
width: 100%;
|
||||||
|
}
|
166
assets/styles/home.css
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
:root {
|
||||||
|
--animation-offset: 6px;
|
||||||
|
--font-size-max: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 2rem;
|
||||||
|
justify-content: center;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 5rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
h1 { font-size: 4.25rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
html {
|
||||||
|
--font-size-scale-factor: 1.33vmax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin-block-start: 10vh;
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .grid {
|
||||||
|
align-items: baseline;
|
||||||
|
display: grid;
|
||||||
|
gap: 0 2rem;
|
||||||
|
grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 2fr);
|
||||||
|
grid-template-rows: repeat(2, max-content);
|
||||||
|
grid-template-areas:
|
||||||
|
"title blurb"
|
||||||
|
"title nav";
|
||||||
|
padding-inline: 1em;
|
||||||
|
width: min-content;
|
||||||
|
}
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
main .grid {
|
||||||
|
gap: 1rem 0;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: repeat(3, max-content);
|
||||||
|
grid-template-areas:
|
||||||
|
"title"
|
||||||
|
"blurb"
|
||||||
|
"nav";
|
||||||
|
justify-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main h1 {
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
main h1 { text-align: center; }
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
align-items: baseline;
|
||||||
|
align-self: center;
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { grid-area: title; }
|
||||||
|
#content { grid-area: blurb }
|
||||||
|
nav { grid-area: nav }
|
||||||
|
|
||||||
|
h1, #content > p, nav { position: relative; }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
animation: left-fade-in var(--transition-duration) ease-in-out;
|
||||||
|
}
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
h1 {
|
||||||
|
animation: top-fade-in var(--transition-duration) ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#content > p, nav {
|
||||||
|
animation: right-fade-in var(--transition-duration) ease-in-out;
|
||||||
|
}
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
#content > p, nav {
|
||||||
|
animation: bottom-fade-in var(--transition-duration) ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes left-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0%;
|
||||||
|
left: var(--animation-offset);
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
opacity: 0%;
|
||||||
|
left: var(--animation-offset);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes right-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0%;
|
||||||
|
left: calc(-1 * var(--animation-offset));
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
opacity: 0%;
|
||||||
|
left: calc(-1 * var(--animation-offset));
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes top-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0%;
|
||||||
|
bottom: calc(-1 * var(--animation-offset));
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
opacity: 0%;
|
||||||
|
bottom: calc(-1 * var(--animation-offset));
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bottom-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0%;
|
||||||
|
bottom: var(--animation-offset);
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
opacity: 0%;
|
||||||
|
bottom: var(--animation-offset);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.platter {
|
||||||
|
padding: 1.5rem 3rem;
|
||||||
|
}
|
192
assets/styles/monokai.css
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
:root {
|
||||||
|
--highlight-background-color: rgb(var(--super-lt-gray));
|
||||||
|
--highlight-foreground-color: var(--foreground-body-color);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chroma {
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
color: var(--highlight-foreground-color);
|
||||||
|
background-color: var(--highlight-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chroma {
|
||||||
|
color: var(--highlight-foreground-color);
|
||||||
|
background-color: var(--highlight-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Other */ .chroma .x { }
|
||||||
|
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
|
||||||
|
/* CodeLine */ .chroma .cl { }
|
||||||
|
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
|
||||||
|
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
|
||||||
|
/* LineHighlight */ .chroma .hl { background-color: #ffffcc }
|
||||||
|
/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||||
|
/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||||
|
/* Keyword */ .chroma .k { color: #00a8c8 }
|
||||||
|
/* KeywordConstant */ .chroma .kc { color: #00a8c8 }
|
||||||
|
/* KeywordDeclaration */ .chroma .kd { color: #00a8c8 }
|
||||||
|
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
|
||||||
|
/* KeywordPseudo */ .chroma .kp { color: #00a8c8 }
|
||||||
|
/* KeywordReserved */ .chroma .kr { color: #00a8c8 }
|
||||||
|
/* KeywordType */ .chroma .kt { color: #00a8c8 }
|
||||||
|
/* Name */ .chroma .n { color: #111111 }
|
||||||
|
/* NameAttribute */ .chroma .na { color: #75af00 }
|
||||||
|
/* NameBuiltin */ .chroma .nb { color: #111111 }
|
||||||
|
/* NameBuiltinPseudo */ .chroma .bp { color: #111111 }
|
||||||
|
/* NameClass */ .chroma .nc { color: #75af00 }
|
||||||
|
/* NameConstant */ .chroma .no { color: #00a8c8 }
|
||||||
|
/* NameDecorator */ .chroma .nd { color: #75af00 }
|
||||||
|
/* NameEntity */ .chroma .ni { color: #111111 }
|
||||||
|
/* NameException */ .chroma .ne { color: #75af00 }
|
||||||
|
/* NameFunction */ .chroma .nf { color: #75af00 }
|
||||||
|
/* NameFunctionMagic */ .chroma .fm { color: #111111 }
|
||||||
|
/* NameLabel */ .chroma .nl { color: #111111 }
|
||||||
|
/* NameNamespace */ .chroma .nn { color: #111111 }
|
||||||
|
/* NameOther */ .chroma .nx { color: #75af00 }
|
||||||
|
/* NameProperty */ .chroma .py { color: #111111 }
|
||||||
|
/* NameTag */ .chroma .nt { color: #f92672 }
|
||||||
|
/* NameVariable */ .chroma .nv { color: #111111 }
|
||||||
|
/* NameVariableClass */ .chroma .vc { color: #111111 }
|
||||||
|
/* NameVariableGlobal */ .chroma .vg { color: #111111 }
|
||||||
|
/* NameVariableInstance */ .chroma .vi { color: #111111 }
|
||||||
|
/* NameVariableMagic */ .chroma .vm { color: #111111 }
|
||||||
|
/* Literal */ .chroma .l { color: #ae81ff }
|
||||||
|
/* LiteralDate */ .chroma .ld { color: #d88200 }
|
||||||
|
/* LiteralString */ .chroma .s { color: #d88200 }
|
||||||
|
/* LiteralStringAffix */ .chroma .sa { color: #d88200 }
|
||||||
|
/* LiteralStringBacktick */ .chroma .sb { color: #d88200 }
|
||||||
|
/* LiteralStringChar */ .chroma .sc { color: #d88200 }
|
||||||
|
/* LiteralStringDelimiter */ .chroma .dl { color: #d88200 }
|
||||||
|
/* LiteralStringDoc */ .chroma .sd { color: #d88200 }
|
||||||
|
/* LiteralStringDouble */ .chroma .s2 { color: #d88200 }
|
||||||
|
/* LiteralStringEscape */ .chroma .se { color: #8045ff }
|
||||||
|
/* LiteralStringHeredoc */ .chroma .sh { color: #d88200 }
|
||||||
|
/* LiteralStringInterpol */ .chroma .si { color: #d88200 }
|
||||||
|
/* LiteralStringOther */ .chroma .sx { color: #d88200 }
|
||||||
|
/* LiteralStringRegex */ .chroma .sr { color: #d88200 }
|
||||||
|
/* LiteralStringSingle */ .chroma .s1 { color: #d88200 }
|
||||||
|
/* LiteralStringSymbol */ .chroma .ss { color: #d88200 }
|
||||||
|
/* LiteralNumber */ .chroma .m { color: #ae81ff }
|
||||||
|
/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
|
||||||
|
/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
|
||||||
|
/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
|
||||||
|
/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
|
||||||
|
/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
|
||||||
|
/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
|
||||||
|
/* Operator */ .chroma .o { color: #f92672 }
|
||||||
|
/* OperatorWord */ .chroma .ow { color: #f92672 }
|
||||||
|
/* Punctuation */ .chroma .p { color: #111111 }
|
||||||
|
/* Comment */ .chroma .c { color: #75715e }
|
||||||
|
/* CommentHashbang */ .chroma .ch { color: #75715e }
|
||||||
|
/* CommentMultiline */ .chroma .cm { color: #75715e }
|
||||||
|
/* CommentSingle */ .chroma .c1 { color: #75715e }
|
||||||
|
/* CommentSpecial */ .chroma .cs { color: #75715e }
|
||||||
|
/* CommentPreproc */ .chroma .cp { color: #75715e }
|
||||||
|
/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
|
||||||
|
/* Generic */ .chroma .g { }
|
||||||
|
/* GenericDeleted */ .chroma .gd { }
|
||||||
|
/* GenericEmph */ .chroma .ge { font-style: italic }
|
||||||
|
/* GenericError */ .chroma .gr { }
|
||||||
|
/* GenericHeading */ .chroma .gh { }
|
||||||
|
/* GenericInserted */ .chroma .gi { }
|
||||||
|
/* GenericOutput */ .chroma .go { }
|
||||||
|
/* GenericPrompt */ .chroma .gp { }
|
||||||
|
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
||||||
|
/* GenericSubheading */ .chroma .gu { }
|
||||||
|
/* GenericTraceback */ .chroma .gt { }
|
||||||
|
/* GenericUnderline */ .chroma .gl { }
|
||||||
|
/* TextWhitespace */ .chroma .w { }
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
/* Background */ .bg { color: #f8f8f2; background-color: #272822 }
|
||||||
|
/* PreWrapper */ .chroma { color: #f8f8f2; background-color: #272822; }
|
||||||
|
/* Other */ .chroma .x { }
|
||||||
|
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
|
||||||
|
/* CodeLine */ .chroma .cl { }
|
||||||
|
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
|
||||||
|
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
|
||||||
|
/* LineHighlight */ .chroma .hl { background-color: #ffffcc }
|
||||||
|
/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||||
|
/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||||
|
/* Keyword */ .chroma .k { color: #66d9ef }
|
||||||
|
/* KeywordConstant */ .chroma .kc { color: #66d9ef }
|
||||||
|
/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
|
||||||
|
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
|
||||||
|
/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
|
||||||
|
/* KeywordReserved */ .chroma .kr { color: #66d9ef }
|
||||||
|
/* KeywordType */ .chroma .kt { color: #66d9ef }
|
||||||
|
/* Name */ .chroma .n { }
|
||||||
|
/* NameAttribute */ .chroma .na { color: #a6e22e }
|
||||||
|
/* NameBuiltin */ .chroma .nb { color: inherit; }
|
||||||
|
/* NameBuiltinPseudo */ .chroma .bp { }
|
||||||
|
/* NameClass */ .chroma .nc { color: #a6e22e }
|
||||||
|
/* NameConstant */ .chroma .no { color: #66d9ef }
|
||||||
|
/* NameDecorator */ .chroma .nd { color: #a6e22e }
|
||||||
|
/* NameEntity */ .chroma .ni { }
|
||||||
|
/* NameException */ .chroma .ne { color: #a6e22e }
|
||||||
|
/* NameFunction */ .chroma .nf { color: #a6e22e }
|
||||||
|
/* NameFunctionMagic */ .chroma .fm { }
|
||||||
|
/* NameLabel */ .chroma .nl { }
|
||||||
|
/* NameNamespace */ .chroma .nn { }
|
||||||
|
/* NameOther */ .chroma .nx { color: #a6e22e }
|
||||||
|
/* NameProperty */ .chroma .py { }
|
||||||
|
/* NameTag */ .chroma .nt { color: #f92672 }
|
||||||
|
/* NameVariable */ .chroma .nv { color: inherit; }
|
||||||
|
/* NameVariableClass */ .chroma .vc { }
|
||||||
|
/* NameVariableGlobal */ .chroma .vg { }
|
||||||
|
/* NameVariableInstance */ .chroma .vi { }
|
||||||
|
/* NameVariableMagic */ .chroma .vm { }
|
||||||
|
/* Literal */ .chroma .l { color: #ae81ff }
|
||||||
|
/* LiteralDate */ .chroma .ld { color: #e6db74 }
|
||||||
|
/* LiteralString */ .chroma .s { color: #e6db74 }
|
||||||
|
/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
|
||||||
|
/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
|
||||||
|
/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
|
||||||
|
/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
|
||||||
|
/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
|
||||||
|
/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
|
||||||
|
/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
|
||||||
|
/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
|
||||||
|
/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
|
||||||
|
/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
|
||||||
|
/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
|
||||||
|
/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
|
||||||
|
/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
|
||||||
|
/* LiteralNumber */ .chroma .m { color: #ae81ff }
|
||||||
|
/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
|
||||||
|
/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
|
||||||
|
/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
|
||||||
|
/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
|
||||||
|
/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
|
||||||
|
/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
|
||||||
|
/* Operator */ .chroma .o { color: #f92672 }
|
||||||
|
/* OperatorWord */ .chroma .ow { color: #f92672 }
|
||||||
|
/* Punctuation */ .chroma .p { color: inherit; }
|
||||||
|
/* Comment */ .chroma .c { color: #75715e }
|
||||||
|
/* CommentHashbang */ .chroma .ch { color: #75715e }
|
||||||
|
/* CommentMultiline */ .chroma .cm { color: #75715e }
|
||||||
|
/* CommentSingle */ .chroma .c1 { color: #75715e }
|
||||||
|
/* CommentSpecial */ .chroma .cs { color: #75715e }
|
||||||
|
/* CommentPreproc */ .chroma .cp { color: #75715e }
|
||||||
|
/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
|
||||||
|
/* Generic */ .chroma .g { }
|
||||||
|
/* GenericDeleted */ .chroma .gd { color: #f92672 }
|
||||||
|
/* GenericEmph */ .chroma .ge { font-style: italic }
|
||||||
|
/* GenericError */ .chroma .gr { }
|
||||||
|
/* GenericHeading */ .chroma .gh { }
|
||||||
|
/* GenericInserted */ .chroma .gi { color: #a6e22e }
|
||||||
|
/* GenericOutput */ .chroma .go { }
|
||||||
|
/* GenericPrompt */ .chroma .gp { }
|
||||||
|
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
||||||
|
/* GenericSubheading */ .chroma .gu { color: #75715e }
|
||||||
|
/* GenericTraceback */ .chroma .gt { }
|
||||||
|
/* GenericUnderline */ .chroma .gl { }
|
||||||
|
/* TextWhitespace */ .chroma .w { }
|
||||||
|
}
|
120
assets/styles/nethack.css
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/* Eryn Wells <eryn@erynwells.me> */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--logentry-background-color: var(--tag-background-color);
|
||||||
|
--logentry-foreground-color: var(--tag-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--separator-color: rgb(var(--dk-gray));
|
||||||
|
--box-shadow-color: rgba(var(--dk-gray), 0.8);
|
||||||
|
--body-code-background-color: rgb(var(--dk-gray));
|
||||||
|
|
||||||
|
--heading-color: rgb(var(--white));
|
||||||
|
--header-series-arrow-foreground-color: rgb(var(--super-dk-gray));
|
||||||
|
|
||||||
|
--html-background-color: rgb(var(--black));
|
||||||
|
--html-color: rgba(var(--white), 0.8);
|
||||||
|
|
||||||
|
--platter-background-color: rgba(var(--black), var(--platter-background-opacity));
|
||||||
|
--platter-backdrop-filter: brightness(0.66) blur(10px);
|
||||||
|
|
||||||
|
--tag-foreground-color: rgb(var(--sub-lt-gray));
|
||||||
|
--tag-background-color: rgb(var(--dk-gray));
|
||||||
|
--tag-spacer-foreground-color: rgb(var(--super-dk-gray));
|
||||||
|
--tag-hover-background-color: rgb(var(--super-dk-gray));
|
||||||
|
--tag-hover-foreground-color: rgb(var(--mid-gray));
|
||||||
|
|
||||||
|
--twitter-icon: url(/icons/twitter-dark.svg);
|
||||||
|
--github-icon: url(/icons/github-dark.svg);
|
||||||
|
--instagram-icon: url(/icons/instagram-dark.svg);
|
||||||
|
--feed-icon: url(/icons/rss-dark.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dungeon-background {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: -1;
|
||||||
|
filter: brightness(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile {
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-inline-start: 0;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li {
|
||||||
|
align-items: first baseline;
|
||||||
|
color: var(--logentry-foreground-color);
|
||||||
|
background-color: var(--logentry-background-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 32px auto auto;
|
||||||
|
row-gap: 1rem;
|
||||||
|
margin-block-start: 2rem;
|
||||||
|
margin-inline-start: 0;
|
||||||
|
padding: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > .date {
|
||||||
|
grid-column-start: 2;
|
||||||
|
grid-row-start: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > h4.date {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > .character-descriptor {
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
grid-column-start: 3;
|
||||||
|
grid-row-start: 1;
|
||||||
|
text-align: right;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > p {
|
||||||
|
grid-column-start: 2;
|
||||||
|
grid-column-end: 4;
|
||||||
|
grid-row-start: 2;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > table.stats {
|
||||||
|
border: 0;
|
||||||
|
grid-column-start: 2;
|
||||||
|
grid-column-end: 4;
|
||||||
|
grid-row-start: 3;
|
||||||
|
margin-block-end: 0;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-border-horizontal-spacing: 0;
|
||||||
|
-webkit-border-vertical-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > table.stats > tbody > tr > td {
|
||||||
|
border: 0;
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile > li > table.stats > tbody > tr > td.score,
|
||||||
|
.logfile > li > table.stats > tbody > tr > td.hp,
|
||||||
|
.logfile > li > table.stats > tbody > tr > td.level {
|
||||||
|
width: 16rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
261
assets/styles/photos.css
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
:root {
|
||||||
|
--date-item-background-color: rgb(var(--lt-gray));
|
||||||
|
|
||||||
|
--photo-grid-item-size: 200px;
|
||||||
|
|
||||||
|
--photo-params-background-color: rgb(var(--lt-gray));
|
||||||
|
--photo-params-container-background-color: rgb(var(--super-lt-gray));
|
||||||
|
--photo-params-color: rgb(var(--sub-dk-gray));
|
||||||
|
--photo-params-border-color: rgb(var(--super-lt-gray));
|
||||||
|
|
||||||
|
--carousel-gradient-full-opaque: var(--background);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--date-item-background-color: rgb(var(--dk-gray));
|
||||||
|
|
||||||
|
--photo-params-background-color: rgb(var(--dk-gray));
|
||||||
|
--photo-params-container-background-color: rgb(var(--sub-dk-gray));
|
||||||
|
--photo-params-color: rgb(var(--super-lt-gray));
|
||||||
|
--photo-params-border-color: rgb(var(--sub-dk-gray));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: none;
|
||||||
|
padding: 0 var(--body-item-spacing);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(var(--photo-grid-item-size), 1fr));
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > * {
|
||||||
|
margin-block-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > :has(img) {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > nav {
|
||||||
|
margin-block-end: var(--body-item-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > a {
|
||||||
|
display: block;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.photos.list > a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > a > img {
|
||||||
|
border-radius: 3px;
|
||||||
|
image-orientation: from-image;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > a > .badge {
|
||||||
|
align-items: center;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background: rgba(255, 255, 255, 0.6);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: rgb(var(--mid-gray));
|
||||||
|
display: flex;
|
||||||
|
left: 4px;
|
||||||
|
line-height: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
position: relative;
|
||||||
|
top: 4px;
|
||||||
|
width: max-content;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.photos.list > a > .badge:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > div {
|
||||||
|
background-color: var(--date-item-background-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > div > h6 {
|
||||||
|
display: block;
|
||||||
|
font-size: 5rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > div > h6 > span {
|
||||||
|
text-align: end;
|
||||||
|
width: min-content;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
padding-inline-end: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.list > div > h6 > span::after {
|
||||||
|
top: 6px;
|
||||||
|
right: 0px;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
content: "⏵︎";
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
@media (max-width: calc(24px + 400px + 9px)) {
|
||||||
|
.photos.list > div > h6 > span {
|
||||||
|
width: max-content;
|
||||||
|
padding-block: calc(0.25 * var(--body-item-spacing));
|
||||||
|
}
|
||||||
|
.photos.list > div > h6 > span::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel {
|
||||||
|
position: relative;
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > ul {
|
||||||
|
align-items: stretch;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
gap: 0 calc(0.5 * var(--body-item-spacing));
|
||||||
|
margin: 0;
|
||||||
|
overflow-x: scroll;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
padding: 0;
|
||||||
|
padding-inline: var(--body-item-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > ul::-webkit-scrollbar {
|
||||||
|
background: transparent;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > .shadow {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: var(--body-item-spacing);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > .shadow.left {
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 1),
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 0));
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > .shadow.right {
|
||||||
|
background: linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 1),
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 0));
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > ul > li {
|
||||||
|
scroll-snap-align: center;
|
||||||
|
margin: 0;
|
||||||
|
width: var(--content-width);
|
||||||
|
flex-shrink: 0;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params > .container {
|
||||||
|
display: block;
|
||||||
|
background-color: var(--photo-params-container-background-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: var(--body-item-spacing) auto;
|
||||||
|
padding: calc(var(--body-item-spacing) / 2);
|
||||||
|
width: 66%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params > .container > table {
|
||||||
|
background-color: var(--photo-params-background-color);
|
||||||
|
color: var(--photo-params-color);
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params table > thead > tr > td {
|
||||||
|
border-bottom: 1px solid var(--photo-params-border-color);
|
||||||
|
font-size: 80%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params tr.exposure-attributes > td {
|
||||||
|
border-top: 1px solid var(--photo-params-border-color);
|
||||||
|
border-left: 1px solid var(--photo-params-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params tr.exposure-attributes > td:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params td {
|
||||||
|
font-size: 75%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params td:last-child {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params td:first-child {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params .make-model {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params .size {
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params .location {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params.debug thead {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 2px solid rgb(var(--dk-gray));
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params.debug {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-params.debug td {
|
||||||
|
border: 1px solid var(--photo-params-border-color);
|
||||||
|
overflow: scroll;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
64
assets/styles/railroad.css
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
:root {
|
||||||
|
--rect-fill: rgba(var(--mid-blue), 0.1);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--rect-fill: rgb(var(--mid-blue), 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram path {
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke: var(--html-color);
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram text {
|
||||||
|
font-family: var(--font-family-monospace);
|
||||||
|
text-anchor: middle;
|
||||||
|
white-space: pre;
|
||||||
|
fill: var(--html-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram text.diagram-text {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram text.diagram-arrow {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram text.label {
|
||||||
|
text-anchor: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram text.comment {
|
||||||
|
font: italic 12px var(--font-family-monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram g.non-terminal text {
|
||||||
|
/*font-style: italic;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram rect {
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke: var(--html-color);
|
||||||
|
fill: var(--rect-fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram rect.group-box {
|
||||||
|
stroke: gray;
|
||||||
|
stroke-dasharray: 10 5;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram path.diagram-text {
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke: var(--html-color);
|
||||||
|
fill: var(--html-background-color);
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.railroad-diagram g.diagram-text:hover path.diagram-text {
|
||||||
|
fill: var(--rect-fill);
|
||||||
|
}
|
647
assets/styles/root.css
Normal file
|
@ -0,0 +1,647 @@
|
||||||
|
@layer reset, root, template, page;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--black: 0, 0, 0;
|
||||||
|
--sub-dk-gray: 16, 16, 16;
|
||||||
|
--dk-gray: 32, 32, 32;
|
||||||
|
--super-dk-gray: 80, 80, 80;
|
||||||
|
--mid-gray: 128, 128, 128;
|
||||||
|
--sub-lt-gray: 175, 175, 175;
|
||||||
|
--lt-gray: 223, 223, 223;
|
||||||
|
--super-lt-gray: 240, 240, 240;
|
||||||
|
--white: 255, 255, 255;
|
||||||
|
|
||||||
|
--lt-blue: 69, 212, 243;
|
||||||
|
--mid-blue: 26, 169, 239;
|
||||||
|
--dk-blue: 63, 46, 231;
|
||||||
|
--purple: 161, 49, 232;
|
||||||
|
--lilac: 187, 121, 245;
|
||||||
|
|
||||||
|
--background: var(--white);
|
||||||
|
|
||||||
|
--site-nav-link-color: rgb(var(--mid-blue));
|
||||||
|
|
||||||
|
--separator-color: rgb(var(--lt-gray));
|
||||||
|
--header-border-color: var(--separator-color);
|
||||||
|
--footer-border-color: var(--separator-color);
|
||||||
|
|
||||||
|
--box-shadow-color: rgba(var(--lt-gray), 0.8);
|
||||||
|
--header-box-shadow-color: var(--box-shadow-color);
|
||||||
|
|
||||||
|
--font-family-body: Verdana, Helvetica, sans-serif;
|
||||||
|
--font-family-monospace: "Courier New", Courier, monospace;
|
||||||
|
--font-family-heading: Museo_Slab, Tahoma, sans-serif;
|
||||||
|
--font-family-site-heading: Museo_Slab, Tahoma, sans-serif;
|
||||||
|
--font-size-min: 6px;
|
||||||
|
--font-size-max: 8px;
|
||||||
|
|
||||||
|
--body-font-size: 2rem;
|
||||||
|
--body-item-spacing: 1em;
|
||||||
|
--body-line-height: 1.5;
|
||||||
|
--body-header-line-height: 1.1;
|
||||||
|
--body-code-background-color: rgb(var(--super-lt-gray));
|
||||||
|
|
||||||
|
--heading-color: rgb(var(--black));
|
||||||
|
--header-series-arrow-foreground-color: rgb(var(--sub-dk-gray));
|
||||||
|
|
||||||
|
--html-background-color: rgb(var(--background));
|
||||||
|
--html-color: rgba(var(--black), 0.8);
|
||||||
|
|
||||||
|
--nav-bulleted-spacing: 0.75rem;
|
||||||
|
|
||||||
|
--platter-background-color: rgba(var(--white), var(--platter-background-opacity));
|
||||||
|
--platter-background-opacity: 0.6;
|
||||||
|
--platter-backdrop-filter: blur(10px);
|
||||||
|
|
||||||
|
--content-width: 80rem;
|
||||||
|
|
||||||
|
--tag-foreground-color: rgb(var(--super-dk-gray));
|
||||||
|
--tag-background-color: rgb(var(--super-lt-gray));
|
||||||
|
--tag-spacer-foreground-color: rgb(var(--super-dk-gray));
|
||||||
|
--tag-hover-background-color: rgb(var(--sub-lt-gray));
|
||||||
|
|
||||||
|
--transition-duration: 0.7s;
|
||||||
|
|
||||||
|
--social-menu-padding: 1rem;
|
||||||
|
--menu-icon-size: 20px;
|
||||||
|
--twitter-icon: url(/icons/twitter.svg);
|
||||||
|
--github-icon: url(/icons/github.svg);
|
||||||
|
--instagram-icon: url(/icons/instagram.svg);
|
||||||
|
--feed-icon: url(/icons/rss.svg);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: var(--black);
|
||||||
|
|
||||||
|
--separator-color: rgb(var(--dk-gray));
|
||||||
|
--box-shadow-color: rgba(var(--dk-gray), 0.8);
|
||||||
|
--body-code-background-color: rgb(var(--dk-gray));
|
||||||
|
|
||||||
|
--heading-color: rgb(var(--white));
|
||||||
|
--header-series-arrow-foreground-color: rgb(var(--super-dk-gray));
|
||||||
|
|
||||||
|
--html-background-color: rgb(var(--background));
|
||||||
|
--html-color: rgba(var(--white), 0.8);
|
||||||
|
|
||||||
|
--platter-background-color: rgba(var(--black), var(--platter-background-opacity));
|
||||||
|
--platter-backdrop-filter: brightness(0.66) blur(10px);
|
||||||
|
|
||||||
|
--tag-foreground-color: rgb(var(--sub-lt-gray));
|
||||||
|
--tag-background-color: rgb(var(--dk-gray));
|
||||||
|
--tag-spacer-foreground-color: rgb(var(--super-dk-gray));
|
||||||
|
--tag-hover-background-color: rgb(var(--super-dk-gray));
|
||||||
|
--tag-hover-foreground-color: rgb(var(--mid-gray));
|
||||||
|
|
||||||
|
--twitter-icon: url(/icons/twitter-dark.svg);
|
||||||
|
--github-icon: url(/icons/github-dark.svg);
|
||||||
|
--instagram-icon: url(/icons/instagram-dark.svg);
|
||||||
|
--feed-icon: url(/icons/rss-dark.svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Museo_Slab";
|
||||||
|
src: url("{{ `/fonts/Museo_Slab_500.woff2` | relURL }}") format("woff2"),
|
||||||
|
url("{{ `/fonts/Museo_Slab_500.woff` | relURL }}") format("woff");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer reset {
|
||||||
|
body, button, h1, h2, h3, h4, h5, h6, input, ol, ul, p, pre, textarea {
|
||||||
|
padding: 0; margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgb(var(--mid-blue));
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 0.12em;
|
||||||
|
}
|
||||||
|
a:visited { color: rgb(var(--mid-blue)); }
|
||||||
|
|
||||||
|
a.hover-only {
|
||||||
|
color: var(--html-color);
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
a.hover-only:hover {
|
||||||
|
color: rgb(var(--mid-blue));
|
||||||
|
text-decoration: underline;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
article.two-column {
|
||||||
|
columns: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-inline-start: 4px solid var(--separator-color);
|
||||||
|
padding-inline-start: 2rem;
|
||||||
|
margin-inline-start: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: var(--body-font-size);
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: var(--body-code-background-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-family: var(--font-family-monospace);
|
||||||
|
display: inline-block;
|
||||||
|
padding-inline: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.nobg {
|
||||||
|
background: none;
|
||||||
|
border-radius: 0;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
margin-block-start: 0.2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
border-radius: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
margin-inline: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: var(--content-width);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure.bordered {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 2px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure a,
|
||||||
|
figure a:hover {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure img {
|
||||||
|
border-radius: 6px;
|
||||||
|
height: auto;
|
||||||
|
max-width: var(--content-width);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure svg {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure .youtube {
|
||||||
|
border-radius: 6px;
|
||||||
|
line-height: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: var(--content-width);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
margin-block: var(--body-item-spacing);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site > ul {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site > .slogans {
|
||||||
|
margin-block-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site > .slogans > li {
|
||||||
|
margin-block-end: 0;
|
||||||
|
margin-inline-start: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site > p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site p + p {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 740px) {
|
||||||
|
footer.site .slogans span {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { font-size: 1.6em; }
|
||||||
|
h2 { font-size: 1.5em; }
|
||||||
|
h3 { font-size: 1.4em; }
|
||||||
|
h4 { font-size: 1.3em; }
|
||||||
|
h5 { font-size: 1.3em; }
|
||||||
|
h6 { font-size: var(--body-font-size); }
|
||||||
|
h5, h6 { font-family: var(--font-family-body); }
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: var(--heading-color);
|
||||||
|
font-family: var(--font-family-heading);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-block: 3.5rem 1rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
line-height: var(--body-header-line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.site {
|
||||||
|
color: rgb(var(--mid-blue));
|
||||||
|
font-size: 2.5em;
|
||||||
|
}
|
||||||
|
@supports (background-clip: text) {
|
||||||
|
h1.site {
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 20% 70%, rgb(var(--purple)), transparent 40%),
|
||||||
|
radial-gradient(circle at 30% 30%, rgb(var(--lt-blue)), rgb(var(--mid-blue)) 20%, transparent 80%),
|
||||||
|
radial-gradient(ellipse at 95% 20%, rgb(var(--dk-blue)), rgb(var(--mid-blue)) 70%, transparent 80%),
|
||||||
|
radial-gradient(circle at 100% 100%, rgb(var(--purple)), rgb(var(--lilac)) 100%),
|
||||||
|
radial-gradient(circle at 45% 100%, rgb(var(--lilac)), rgb(var(--purple)) 60%),
|
||||||
|
radial-gradient(ellipse at 50% 50%, rgb(var(--dk-blue)), transparent 80%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.site > a { color: inherit; }
|
||||||
|
h1.site > a:hover { text-decoration: none; }
|
||||||
|
|
||||||
|
header.site {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.site > .grid > h1 {
|
||||||
|
font-family: var(--font-family-site-heading);
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0;
|
||||||
|
order: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.site > .grid {
|
||||||
|
align-items: baseline;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0 0.5em;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: var(--content-width);
|
||||||
|
padding: 1.5rem 3rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.site > .grid > nav {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
font-size: max(1.5rem, 80%);
|
||||||
|
justify-content: start;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
list-style: none;
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.site > .grid > nav > li {
|
||||||
|
color: var(--site-nav-link-color);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.site > .grid > nav > .active { font-weight: bold; }
|
||||||
|
|
||||||
|
header.site > .grid > nav:first-of-type {
|
||||||
|
justify-content: start;
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.site > .grid > nav:last-of-type {
|
||||||
|
justify-content: end;
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 516px) {
|
||||||
|
header.site > .platter {
|
||||||
|
border-left: none;
|
||||||
|
border-radius: 0;
|
||||||
|
border-right: none;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
header.site > .grid {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 435px) {
|
||||||
|
header.site > .grid > nav:first-of-type {
|
||||||
|
order: 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
--font-size-scale-factor: 1vmax;
|
||||||
|
|
||||||
|
background-color: var(--html-background-color);
|
||||||
|
color: var(--html-color);
|
||||||
|
font-family: var(--font-family-body);
|
||||||
|
font-size: clamp(var(--font-size-min), var(--font-size-scale-factor), var(--font-size-max));
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.circular {
|
||||||
|
shape-outside: circle(50%);
|
||||||
|
-webkit-clip-path: circle(50%);
|
||||||
|
clip-path: circle(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: calc(var(--content-width) + 2 * var(--body-item-spacing));
|
||||||
|
margin: var(--body-item-spacing) auto;
|
||||||
|
padding-inline: var(--body-item-spacing);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > :first-child,
|
||||||
|
main > article > :first-child { margin-block-start: 0; }
|
||||||
|
main > :not(:last-child),
|
||||||
|
main > article > :not(:last-child) { margin-block-end: var(--body-item-spacing); }
|
||||||
|
main > :last-child,
|
||||||
|
main > article > :last-child { margin-block-end: 0; }
|
||||||
|
|
||||||
|
nav.bulleted > li:first-child::before {
|
||||||
|
color: var(--html-color);
|
||||||
|
content: "";
|
||||||
|
margin-inline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.bulleted > li::before {
|
||||||
|
color: var(--heading-color);
|
||||||
|
content: "•";
|
||||||
|
font-size: 60%;
|
||||||
|
font-weight: normal;
|
||||||
|
opacity: 80%;
|
||||||
|
margin-inline: var(--nav-bulleted-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.social > li { margin-inline-start: var(--nav-bulleted-spacing); }
|
||||||
|
nav.social > li:first-child { margin-inline-start: 0; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
dl,
|
||||||
|
ul > li,
|
||||||
|
ol > li,
|
||||||
|
dl > dt,
|
||||||
|
ul > li > ul,
|
||||||
|
ul > li > ol,
|
||||||
|
ol > li > ul,
|
||||||
|
ol > li > ol {
|
||||||
|
margin-inline-start: calc(var(--body-item-spacing) + 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
header > span.series {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-inline-start: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > span.series::before {
|
||||||
|
color: var(--header-series-arrow-foreground-color);
|
||||||
|
content: "↳";
|
||||||
|
margin-inline-end: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
@supports (display: subgrid) {
|
||||||
|
.post-list {
|
||||||
|
align-items: baseline;
|
||||||
|
display: grid;
|
||||||
|
gap: 1em;
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(150px, max-content) minmax(20px, max-content) 3px auto;
|
||||||
|
grid-template-areas: "month day line title";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list li {
|
||||||
|
align-items: baseline;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: grid;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title {
|
||||||
|
align-items: baseline;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title h1 {
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: 1.8em;
|
||||||
|
margin-block: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title > .post-date {
|
||||||
|
color: var(--light-dim);
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
inline-size: min-content;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-block-start: 0.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p5-sketch {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social {
|
||||||
|
display: block;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
margin-left: auto;
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social > li > a {
|
||||||
|
background-image: var(--url);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: var(--menu-icon-size);
|
||||||
|
display: block;
|
||||||
|
height: var(--menu-icon-size);
|
||||||
|
width: var(--menu-icon-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.social > li > a > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social > li + li:before {
|
||||||
|
color: var(--dark);
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags {
|
||||||
|
display: flex;
|
||||||
|
margin-inline-start: 0;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > li {
|
||||||
|
background-color: var(--tag-background-color);
|
||||||
|
color: var(--tag-foreground-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 75%;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > li:first-child {
|
||||||
|
margin-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > li:hover {
|
||||||
|
background-color: var(--tag-hover-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > li > a {
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > li > a:hover {
|
||||||
|
color: var(--tag-hover-foreground-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > li + li { margin-inline-start: 1rem; }
|
||||||
|
footer > .tags > li.chevron + li { margin-inline-start: 0 }
|
||||||
|
|
||||||
|
footer > .tags > .chevron {
|
||||||
|
align-items: center;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
color: var(--tag-spacer-foreground-color);
|
||||||
|
background-color: inherit;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-inline-start: 0;
|
||||||
|
padding-inline-start: 1px;
|
||||||
|
width: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > .tags > .chevron:hover {
|
||||||
|
color: var(--tag-spacer-foreground-color);
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border: 1px solid var(--separator-color);
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-inline: auto;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border: 1px solid var(--separator-color);
|
||||||
|
padding-inline: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.youtube iframe {
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
margin-bottom: -3px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** SYNTAX HIGHLIGHTING **/
|
||||||
|
|
||||||
|
.highlight code {
|
||||||
|
background-color: inherit;
|
||||||
|
margin-block: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight .line {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: max-content auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight .ln a {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-use-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight .cl {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** HELPER CLASSES **/
|
||||||
|
|
||||||
|
.centered { text-align: center; }
|
||||||
|
.float-right { float: right; }
|
||||||
|
|
||||||
|
.nobreak { white-space: nowrap; }
|
||||||
|
.noselect {
|
||||||
|
cursor: default;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-use-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.platter {
|
||||||
|
-webkit-backdrop-filter: var(--platter-backdrop-filter);
|
||||||
|
backdrop-filter: var(--platter-backdrop-filter);
|
||||||
|
background-color: var(--platter-background-color);
|
||||||
|
border: 1px solid var(--header-border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 3px 4px 4px var(--header-box-shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.visible { visibility: visible; }
|
2
config/_default/author.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
name: Eryn Wells
|
||||||
|
email: eryn@erynwells.me
|
|
@ -1,6 +1,5 @@
|
||||||
baseURL: https://erynwells.me/
|
baseURL: https://erynwells.me/
|
||||||
languageCode: en-US
|
languageCode: en-US
|
||||||
title: ~eryn
|
title: Erynwells.me
|
||||||
copyright: Copyright © 2020—2024 Eryn Wells
|
copyright: Copyright © 2020—2022 Eryn Wells
|
||||||
defaultContentLanguage: en
|
defaultContentLanguage: en
|
||||||
enableEmoji: true
|
|
||||||
|
|
|
@ -7,6 +7,3 @@ es:
|
||||||
jp:
|
jp:
|
||||||
languageName: 日本語
|
languageName: 日本語
|
||||||
weight: 3
|
weight: 3
|
||||||
tok:
|
|
||||||
languageName: toki pona
|
|
||||||
weight: 4
|
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
goldmark:
|
|
||||||
renderer:
|
|
||||||
unsafe: true
|
|
||||||
parser:
|
|
||||||
attribute:
|
|
||||||
block: true
|
|
||||||
title: true
|
|
||||||
highlight:
|
highlight:
|
||||||
anchorLineNos: true
|
anchorLineNos: true
|
||||||
lineNos: false
|
lineNos: true
|
||||||
lineNumbersInTable: false
|
lineNumbersInTable: false
|
||||||
noClasses: false
|
noClasses: false
|
||||||
|
|
|
@ -11,19 +11,13 @@ main:
|
||||||
name: About
|
name: About
|
||||||
url: /about/
|
url: /about/
|
||||||
weight: 30
|
weight: 30
|
||||||
- identifier: feed
|
|
||||||
name: feed
|
|
||||||
url: /feed.atom
|
|
||||||
weight: 40
|
|
||||||
params:
|
|
||||||
style: file
|
|
||||||
social:
|
social:
|
||||||
- identifier: mastodon
|
- identifier: twitter
|
||||||
name: Mastodon
|
name: Twitter
|
||||||
url: https://mastodon.social/@erynofwales
|
url: https://twitter.com/erynofwales
|
||||||
weight: 10
|
weight: 10
|
||||||
params:
|
params:
|
||||||
shortName: mst
|
shortName: tw
|
||||||
- identifier: github
|
- identifier: github
|
||||||
name: Github
|
name: Github
|
||||||
url: https://github.com/erynofwales
|
url: https://github.com/erynofwales
|
||||||
|
@ -37,7 +31,7 @@ social:
|
||||||
params:
|
params:
|
||||||
shortName: ig
|
shortName: ig
|
||||||
- identifier: feed
|
- identifier: feed
|
||||||
name: feed
|
name: Feed
|
||||||
url: /feed.atom
|
url: /feed.atom
|
||||||
weight: 40
|
weight: 40
|
||||||
params:
|
params:
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
hugoVersion:
|
|
||||||
extended: false
|
|
||||||
min: "0.116.0"
|
|
||||||
replacements: >-
|
|
||||||
github.com/erynofwales/hugo-theme-feeds/v2 -> feeds,
|
|
||||||
github.com/erynofwales/hugo-theme-termlite/v2 -> termlite,
|
|
||||||
github.com/erynofwales/hugo-theme-photostream/v2 -> photostream,
|
|
||||||
github.com/erynofwales/hugo-resource-builders/v2 -> resource-builders,
|
|
||||||
github.com/erynofwales/hugo-image-utilities/v2 -> image-utils
|
|
||||||
imports:
|
|
||||||
- path: github.com/erynofwales/hugo-theme-termlite/v2
|
|
||||||
- path: github.com/erynofwales/hugo-theme-feeds/v2
|
|
||||||
- path: github.com/erynofwales/hugo-theme-photostream/v2
|
|
||||||
- path: github.com/erynofwales/hugo-resource-builders/v2
|
|
||||||
- path: github.com/erynofwales/hugo-image-utilities/v2
|
|
|
@ -1,4 +1,3 @@
|
||||||
home: [HTML, Atom]
|
home: [HTML, Atom, RSS]
|
||||||
section: [HTML, Atom]
|
page: [HTML, JSON]
|
||||||
taxonomy: [HTML]
|
section: [HTML, Atom, RSS]
|
||||||
term: [HTML]
|
|
||||||
|
|
|
@ -1,20 +1,4 @@
|
||||||
author:
|
|
||||||
name: Eryn Wells
|
|
||||||
email: eryn@erynwells.me
|
|
||||||
|
|
||||||
shortTitle: Eryn Wells
|
|
||||||
|
|
||||||
twitter: erynofwales
|
twitter: erynofwales
|
||||||
github: erynofwales
|
github: erynofwales
|
||||||
instagram: erynofwales
|
instagram: erynofwales
|
||||||
description: Home page of Eryn Rachel Wells
|
description: Home page of Eryn Rachel Wells
|
||||||
|
|
||||||
blog:
|
|
||||||
yearLimit: 3
|
|
||||||
|
|
||||||
photostream:
|
|
||||||
yearLimit: 3
|
|
||||||
|
|
||||||
photos:
|
|
||||||
gridSize: 200
|
|
||||||
thumbnailSize: 600
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
x:
|
twitter:
|
||||||
enableDNT: true
|
enableDNT: true
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
x:
|
twitter:
|
||||||
disableInlineCSS: true
|
disableInlineCSS: true
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
title: Eryn Rachel Wells
|
title: Eryn Rachel Wells
|
||||||
layout: single
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{{< nobreak >}}Ingeniera de software,{{< /nobreak >}}
|
{{< nobreak >}}Ingeniera de software,{{< /nobreak >}}
|
||||||
|
|
|
@ -1,58 +1,5 @@
|
||||||
---
|
---
|
||||||
layout: single
|
title: Eryn Rachel Wells
|
||||||
params:
|
|
||||||
renderHeadingAnchors: false
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Hi, I'm Eryn Wells. This is my website. Welcome!
|
Software engineer, potter, musician, and overall nerd.
|
||||||
|
|
||||||
## Latest
|
|
||||||
|
|
||||||
Here are some of my most recent posts.
|
|
||||||
|
|
||||||
{{< home/latest >}}
|
|
||||||
|
|
||||||
## Personal
|
|
||||||
|
|
||||||
I'm a queer trans woman, {{< tess >}}' partner, and mom of [two cats][cats]. I
|
|
||||||
was born in Seattle, {{< abbr Washington >}}WA{{< /abbr >}} and grew up in
|
|
||||||
Phoenix, {{< abbr Arizona >}}AZ{{< /abbr >}}. I attended [Oberlin College][ob]
|
|
||||||
where I got a degree in Computer Science. My pronouns are [she/her][pronouns].
|
|
||||||
|
|
||||||
You can read more about me on my [about][ab] page, or [get in touch][where-am-i].
|
|
||||||
|
|
||||||
## Professional
|
|
||||||
|
|
||||||
I've worked as a software engineer since 2011 for a variety of companies around
|
|
||||||
the San Francisco Bay Area. I joined [Apple][a] in 2016, where I currently work
|
|
||||||
on password management and authentication technologies.
|
|
||||||
|
|
||||||
My [résumé][r] has all the details.
|
|
||||||
|
|
||||||
## Hobbies
|
|
||||||
|
|
||||||
When I'm not working, you can reliably find me hacking on this website or [some
|
|
||||||
coding other project][gh]. I'm also a musician, and play piano, Irish tin
|
|
||||||
whistle, and modular synthesizer. Occasionally I [record][bc] [things][sc]. I
|
|
||||||
love outer space and astronomy; I will always get excited to look at the moon
|
|
||||||
with you, or check out anything through a telescope. I enjoy [photograhy][p],
|
|
||||||
mostly as a travel hobby. And I've been practicing iaido, a traditional Japanese
|
|
||||||
sword art, since early 2024. Other things I've been into include: bread baking,
|
|
||||||
bicycling, calligraphy, ceramics, and knitting.
|
|
||||||
|
|
||||||
[a]: https://apple.com
|
|
||||||
[ab]: {{< ref "/about" >}}
|
|
||||||
[b]: {{< ref "/blog" >}}
|
|
||||||
[bc]: https://erynwells.bandcamp.com/releases
|
|
||||||
[cats]: {{< ref "/cats" >}}
|
|
||||||
[eml]: mailto:Eryn%20Wells<eryn@erynwells.me>
|
|
||||||
[gh]: https://github.com/erynofwales
|
|
||||||
[ig]: https://www.instagram.com/erynofwales
|
|
||||||
[m]: https://mastodon.social/@erynofwales
|
|
||||||
[n]: {{< ref "/now" >}}
|
|
||||||
[ob]: https://www.oberlin.edu
|
|
||||||
[p]: {{< ref "/photos" >}}
|
|
||||||
[pronouns]: http://pronoun.is/she
|
|
||||||
[r]: {{< ref "/resume" >}}
|
|
||||||
[sc]: https://soundcloud.com/purlsnbeeps
|
|
||||||
[where-am-i]: {{< ref "/about/where-am-i" >}}
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
---
|
---
|
||||||
title: "Hi! 👋🏻"
|
title: "Hi! 👋🏻"
|
||||||
layout: single
|
date: 2022-09-03T12:14:32-07:00
|
||||||
|
draft: false
|
||||||
resources:
|
resources:
|
||||||
- name: me
|
- name: me
|
||||||
src: me.jpeg
|
src: me.jpeg
|
||||||
params:
|
|
||||||
alt: >
|
|
||||||
Me, wearing a hat and smiling slightly, standing in front of a stone
|
|
||||||
background.
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{{% section class=content--small-right-column %}}
|
{{< circular_image id=me name=me class="float-right" width=200
|
||||||
|
alt="A photo of me, wearing a hat, standing in front of a stone background">}}
|
||||||
|
|
||||||
I'm Eryn. My pronouns are [she/her][p]. I'm a queer trans woman. I live in San
|
I'm Eryn. My pronouns are [she/her][p]. I'm a queer trans woman. I live in San
|
||||||
Francisco, CA, on the unceded ancestral lands of the Ramaytush Ohlone people,
|
Francisco, CA, on the unceded ancestral lands of the Ramaytush Ohlone people,
|
||||||
|
@ -20,10 +18,6 @@ I attended [Oberlin College][ob] where I got a degree in Computer Science.
|
||||||
|
|
||||||
I speak English natively, and Spanish too, though I always need more practice.
|
I speak English natively, and Spanish too, though I always need more practice.
|
||||||
|
|
||||||
{{< circular_image id=me name=me class="content--right-column" width=200 >}}
|
|
||||||
|
|
||||||
{{% /section %}}
|
|
||||||
|
|
||||||
## Hobbies
|
## Hobbies
|
||||||
|
|
||||||
I've been a musician for most of my life. I started on the piano at age seven,
|
I've been a musician for most of my life. I started on the piano at age seven,
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
@layer page {
|
|
||||||
main > section > p:not(:last-child) {
|
|
||||||
margin-bottom: var(--body-item-spacing);
|
|
||||||
}
|
|
||||||
|
|
||||||
p:has(img#me) {
|
img#me {
|
||||||
display: inline;
|
|
||||||
grid-column: unset;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
img#me {
|
|
||||||
margin: 0;
|
|
||||||
shape-outside: circle(55%);
|
|
||||||
width: min(200px, 25%);
|
width: min(200px, 25%);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,11 @@ title: "Where to Find Me"
|
||||||
date: 2022-11-11T08:35:26-08:00
|
date: 2022-11-11T08:35:26-08:00
|
||||||
---
|
---
|
||||||
|
|
||||||
Here's a list of places you can find me online. You can often find me on
|
Here's a list of places you can find me online.
|
||||||
services not listed here with the `erynofwales` or `erynrwells` handles.
|
|
||||||
|
|
||||||
## Social Media
|
## Social Media
|
||||||
|
|
||||||
I'm really only on Instagram and Mastodon these days. My Twitter account is
|
- Cohost: [@eryn](https://cohost.org/eryn)
|
||||||
still live, as an archive, but I don't post on it or look at it. Ditto for
|
|
||||||
Facebook.
|
|
||||||
|
|
||||||
- Facebook: [erynofwales](https://www.facebook.com/erynofwales)
|
|
||||||
- Instagram: [@erynofwales](https://instagram.com/erynofwales)
|
- Instagram: [@erynofwales](https://instagram.com/erynofwales)
|
||||||
- Mastodon: [@erynofwales](https://mastodon.social/@erynofwales)
|
- Mastodon: [@erynofwales](https://mastodon.social/@erynofwales)
|
||||||
- Twitter: [@erynofwales](https://twitter.com/erynofwales)
|
- Twitter: [@erynofwales](https://twitter.com/erynofwales)
|
||||||
|
@ -20,10 +15,9 @@ Facebook.
|
||||||
## Content
|
## Content
|
||||||
|
|
||||||
- Bandcamp: [erynwells](https://erynwells.bandcamp.com/releases)
|
- Bandcamp: [erynwells](https://erynwells.bandcamp.com/releases)
|
||||||
- GitHub: [erynofwales](https://github.com/erynofwales)
|
|
||||||
- Soundcloud: [purlsnbeeps](https://soundcloud.com/purlsnbeeps)
|
- Soundcloud: [purlsnbeeps](https://soundcloud.com/purlsnbeeps)
|
||||||
- StoryGraph: [erynrwells](https://app.thestorygraph.com/profile/erynrwells)
|
|
||||||
- YouTube: [Eryn Wells](https://www.youtube.com/channel/UCWb2pTDlC27R1PucyUPrypA)
|
- YouTube: [Eryn Wells](https://www.youtube.com/channel/UCWb2pTDlC27R1PucyUPrypA)
|
||||||
|
- GitHub: [erynofwales](https://github.com/erynofwales)
|
||||||
|
|
||||||
## The Old Fashioned Way
|
## The Old Fashioned Way
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Booting a Raspberry Pi Over TFTP"
|
title: "Booting a Raspberry Pi Over TFTP"
|
||||||
date: 2020-10-13T08:31:52-07:00
|
date: 2020-10-13T08:31:52-07:00
|
||||||
description: A writeup of how I set up a Raspberry Pi to boot over TFTP to facilitate an operating system development project.
|
draft: false
|
||||||
series: ["Raspberry Pi OS Development"]
|
series: ["Raspberry Pi OS Development"]
|
||||||
categories: ["Tech"]
|
categories: ["Tech"]
|
||||||
tags: ["Raspberry Pi", "Networking"]
|
tags: ["Raspberry Pi", "Networking"]
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
title: 2020
|
|
||||||
date: 2020-01-01
|
|
||||||
---
|
|
|
@ -7,7 +7,7 @@ categories: ["Music"]
|
||||||
tags: ["Synthesizers", "Electronics", "DIY", "Compositions"]
|
tags: ["Synthesizers", "Electronics", "DIY", "Compositions"]
|
||||||
---
|
---
|
||||||
|
|
||||||
{{< youtube id="gCSwWsxzy_c" title="A timelapse video of me building an Oskitone Scout, set to music produced using the Scout itself" >}}
|
{{< figures/youtube id="gCSwWsxzy_c" title="A timelapse video of me building an Oskitone Scout, set to music produced using the Scout itself">}}
|
||||||
|
|
||||||
[Oskitone][oskitone] recently released a new synthesizer: the [Scout][scout].
|
[Oskitone][oskitone] recently released a new synthesizer: the [Scout][scout].
|
||||||
It's a small monophonic keyboard synth built around an Arduino. It was a quick
|
It's a small monophonic keyboard synth built around an Arduino. It was a quick
|
||||||
|
|
|
@ -41,4 +41,4 @@ I'm so grateful for every one of these people. We've been friends for years and
|
||||||
even though our lives have taken us in so many different directions, we've found
|
even though our lives have taken us in so many different directions, we've found
|
||||||
each other again and that is so wonderful.
|
each other again and that is so wonderful.
|
||||||
|
|
||||||
{{< twitter user=erynofwales id=1447951049076056071 >}}
|
{{< twitter erynofwales 1447951049076056071 >}}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
title: 2021
|
|
||||||
date: 2021-01-01
|
|
||||||
---
|
|
|
@ -7,7 +7,7 @@ categories: ["Music"]
|
||||||
tags: ["Eurorack", "Synthesizers", "Recordings", "Performances", "Compositions"]
|
tags: ["Eurorack", "Synthesizers", "Recordings", "Performances", "Compositions"]
|
||||||
---
|
---
|
||||||
|
|
||||||
{{< youtube id="sqr7g4P85aM" title="A top-down video of me operating a small Eurorack system made of only three modules. Lights flash, an incorporeal hand turns knobs to sculpt the sound." >}}
|
{{< figures/youtube id="sqr7g4P85aM" title="A top-down video of me operating a small Eurorack system made of only three modules. Lights flash, an incorporeal hand turns knobs to sculpt the sound." >}}
|
||||||
|
|
||||||
This is my submission to the [Three Module Challenge][3mc] show put on by
|
This is my submission to the [Three Module Challenge][3mc] show put on by
|
||||||
Colorado Modular Synth Society in late January 2022. This is my first time
|
Colorado Modular Synth Society in late January 2022. This is my first time
|
||||||
|
|
|
@ -18,7 +18,7 @@ estuve hecho.
|
||||||
|
|
||||||
Es posible que me entusiasme un poco.
|
Es posible que me entusiasme un poco.
|
||||||
|
|
||||||
{{< twitter user=erynofwales id=1510763278691016705 >}}
|
{{< twitter erynofwales 1510763278691016705 >}}
|
||||||
|
|
||||||
He mejorado mucho en las últimas semanas. Mis puntajes han crecidos desde 1,000
|
He mejorado mucho en las últimas semanas. Mis puntajes han crecidos desde 1,000
|
||||||
hasta el mejor juego hasta ahora en que [obtuve 9401 puntos][over9000]. Quién
|
hasta el mejor juego hasta ahora en que [obtuve 9401 puntos][over9000]. Quién
|
||||||
|
|
|
@ -23,7 +23,7 @@ packed with [NetHackWiki][nethackwiki] tabs too, including a pinned one for the
|
||||||
|
|
||||||
I may have gotten a little carried away a time or two.
|
I may have gotten a little carried away a time or two.
|
||||||
|
|
||||||
{{< twitter user=erynofwales id=1510763278691016705 >}}
|
{{< twitter erynofwales 1510763278691016705 >}}
|
||||||
|
|
||||||
I've gotten much better in that time. My scores have increased from the
|
I've gotten much better in that time. My scores have increased from the
|
||||||
1000-2000 range to my best game so far in which [I scored 9401
|
1000-2000 range to my best game so far in which [I scored 9401
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Roguelikes I Like"
|
title: "Roguelikes I Like"
|
||||||
date: 2022-05-09T08:37:23-07:00
|
date: 2022-05-09T08:37:23-07:00
|
||||||
description: Some roguelikes I’ve enjoyed recently.
|
|
||||||
draft: false
|
draft: false
|
||||||
resources:
|
resources:
|
||||||
- name: nethack
|
- name: nethack
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
<svg class="railroad-diagram" width="408" height="62" viewBox="0 0 408 62">
|
|
||||||
<g transform="translate(.5 .5)">
|
|
||||||
<g>
|
|
||||||
<path d="M20 21v20m10 -20v20m-10 -10h20"></path>
|
|
||||||
</g>
|
|
||||||
<path d="M40 31h10"></path>
|
|
||||||
<g>
|
|
||||||
<path d="M50 31h0"></path>
|
|
||||||
<path d="M358 31h0"></path>
|
|
||||||
<g class="terminal ">
|
|
||||||
<path d="M50 31h0"></path>
|
|
||||||
<path d="M126 31h0"></path>
|
|
||||||
<rect x="50" y="20" width="76" height="22" rx="10" ry="10"></rect>
|
|
||||||
<text x="88" y="35"><audio></text>
|
|
||||||
</g>
|
|
||||||
<path d="M126 31h10"></path>
|
|
||||||
<path d="M136 31h10"></path>
|
|
||||||
<g class="terminal ">
|
|
||||||
<path d="M146 31h0"></path>
|
|
||||||
<path d="M230 31h0"></path>
|
|
||||||
<rect x="146" y="20" width="84" height="22" rx="10" ry="10"></rect>
|
|
||||||
<text x="188" y="35">Analyzer</text>
|
|
||||||
</g>
|
|
||||||
<path d="M230 31h10"></path>
|
|
||||||
<path d="M240 31h10"></path>
|
|
||||||
<g class="terminal ">
|
|
||||||
<path d="M250 31h0"></path>
|
|
||||||
<path d="M358 31h0"></path>
|
|
||||||
<rect x="250" y="20" width="108" height="22" rx="10" ry="10"></rect>
|
|
||||||
<text x="304" y="35">destination</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="M358 31h10"></path>
|
|
||||||
<path d="M 368 31 h 20 m -10 -10 v 20 m 10 -20 v 20"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1 KiB |
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Making an Audio Scope with P5.js"
|
title: "Making an Audio Scope with P5.js"
|
||||||
date: 2022-08-18T20:48:37-07:00
|
date: 2022-08-18T20:48:37-07:00
|
||||||
description: A writeup of a small JavaScript waveform visualizer I made with P5.js.
|
draft: false
|
||||||
categories: ["Tech"]
|
categories: ["Tech"]
|
||||||
tags: ["P5.js", "Programming", "Web", "Art"]
|
tags: ["P5.js", "Programming", "Web", "Art"]
|
||||||
resources:
|
resources:
|
||||||
|
@ -88,7 +88,22 @@ node to the input of the analyzer node, and the output of the analyzer node to
|
||||||
the audio context's `destination` node that routes to the computer's speakers.
|
the audio context's `destination` node that routes to the computer's speakers.
|
||||||
Our audio processing graph looks like this:
|
Our audio processing graph looks like this:
|
||||||
|
|
||||||

|
{{< figures/railroad id="audioContextDiagram" >}}
|
||||||
|
{{< scripts/railroad >}}
|
||||||
|
return rr.Diagram(
|
||||||
|
rr.Sequence(
|
||||||
|
rr.Terminal("<audio>"),
|
||||||
|
rr.Terminal("Analyzer"),
|
||||||
|
rr.Terminal("destination")));
|
||||||
|
{{< /scripts/railroad >}}
|
||||||
|
{{< scripts/railroad narrow=1 >}}
|
||||||
|
return rr.Diagram(
|
||||||
|
rr.Stack(
|
||||||
|
rr.Terminal("<audio>"),
|
||||||
|
rr.Terminal("Analyzer"),
|
||||||
|
rr.Terminal("destination")));
|
||||||
|
{{< /scripts/railroad >}}
|
||||||
|
{{< /figures/railroad >}}
|
||||||
|
|
||||||
By itself the AudioContext doesn't actually play any audio. I'll tackle that
|
By itself the AudioContext doesn't actually play any audio. I'll tackle that
|
||||||
next.
|
next.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Hugo's Dictionary API"
|
title: "Hugo's Dictionary API"
|
||||||
date: 2022-10-13T10:19:02-07:00
|
date: 2022-10-13T10:19:02-07:00
|
||||||
description: I’ve found Hugo’s API for collections to be difficult to understand. Here’s my attempt to summarize it’s quirks.
|
|
||||||
categories: ["Tech"]
|
categories: ["Tech"]
|
||||||
tags: ["Hugo", "Web", "API Design"]
|
tags: ["Hugo", "Web", "API Design"]
|
||||||
series: "Erynwells.me Development"
|
series: "Erynwells.me Development"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Lunar Eclipse 🌝"
|
title: "Lunar Eclipse 🌝"
|
||||||
date: 2022-11-07T08:37:45-08:00
|
date: 2022-11-07T08:37:45-08:00
|
||||||
description: A quick note about the upcoming lunar eclipse in the morning of 2022-11-08.
|
|
||||||
categories: Space
|
categories: Space
|
||||||
tags: [Moon, Lunar Eclipse]
|
tags: [Moon, Lunar Eclipse]
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "My Best Nethack Game (So Far)"
|
title: "My Best Nethack Game (So Far)"
|
||||||
date: 2022-11-24T09:13:15-05:00
|
date: 2022-11-24T09:13:15-05:00
|
||||||
description: A summary of my best-to-date game of Nethack.
|
|
||||||
categories: ["Games"]
|
categories: ["Games"]
|
||||||
tags: ["Nethack", "Roguelikes", "Video Games"]
|
tags: ["Nethack", "Roguelikes", "Video Games"]
|
||||||
resources:
|
resources:
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
title: 2022
|
|
||||||
date: 2022-01-01
|
|
||||||
---
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
title: 2023
|
|
||||||
date: 2023-01-01
|
|
||||||
---
|
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
title: "Atom Feed Bug Fixes"
|
|
||||||
date: 2023-08-09T08:43:26-07:00
|
|
||||||
categories: ["Tech"]
|
|
||||||
tags: ["Erynwells.me", "Meta", "Atom"]
|
|
||||||
---
|
|
||||||
|
|
||||||
A kind reader pointed out to me that my Atom feed was incorrect. There were two
|
|
||||||
problems. First, I was specifying an incorrect URL in the feed's `<link
|
|
||||||
rel="self">` -- it was pointing to a nonexistant feed.xml file. Second, I was
|
|
||||||
omitting a `<link>` tag from the entries entirely.
|
|
||||||
|
|
||||||
Thunderbird didn't like this. With no `<link>` for an entry, it would show the
|
|
||||||
feed's `<link>` in it's UI. And that link left users at a 404 page.
|
|
||||||
|
|
||||||
I pushed a fix this morning. You might have to refresh or resubscribe to pick up
|
|
||||||
the changes.
|
|
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
title: "Chess"
|
|
||||||
date: 2023-11-20T14:58:56-08:00
|
|
||||||
categories: Chess
|
|
||||||
tags: ["Games", "Hobbies"]
|
|
||||||
---
|
|
||||||
|
|
||||||
I've been playing a lot of chess lately. {{< tess >}} and I have been watching
|
|
||||||
[Slow Horses][slow-horses] on Apple TV+, and there was a recent episode in which
|
|
||||||
a chess game between two characters is a key plot beat. That got me thinking
|
|
||||||
about playing again.
|
|
||||||
|
|
||||||
I learned chess as a kid. My dad taught me. I played in chess clubs in
|
|
||||||
elementary and middle school. I was really into it for a while!
|
|
||||||
|
|
||||||
I have a [Chess.com](chess.com) account now: [erynrwells][chess-com-profile].
|
|
||||||
I'm also on [lichess.org](lichess.org): I'm [erynrwells][lichess-profile] there too.
|
|
||||||
Send me a friend request or challenge? :)
|
|
||||||
|
|
||||||
[slow-horses]: https://tv.apple.com/us/show/slow-horses/umc.cmc.2szz3fdt71tl1ulnbp8utgq5o
|
|
||||||
[chess-com-profile]: https://www.chess.com/member/erynrwells
|
|
||||||
[lichess-profile]: https://lichess.org/@/erynrwells
|
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
title: "Guide to Computing"
|
|
||||||
date: 2023-09-23T10:24:35-07:00
|
|
||||||
categories: "Tech"
|
|
||||||
tags: ["Retro Computing", "Design"]
|
|
||||||
---
|
|
||||||
|
|
||||||
I really enjoyed looking through the images on [Docubyte's Guide to
|
|
||||||
Computing][link]. It depicts machines from the early days of modern computing --
|
|
||||||
think IBM mainframes, PDP-1's, and lots of midcentury modern design -- in a way
|
|
||||||
I found really intriguing.
|
|
||||||
|
|
||||||
[link]: https://www.docubyte.com/projects/guide-to-computing/
|
|
|
@ -1,38 +0,0 @@
|
||||||
---
|
|
||||||
title: "Hello Chess Friend"
|
|
||||||
description: I started building a chess engine in Rust. Here it is.
|
|
||||||
date: 2023-12-29T08:29:00-08:00
|
|
||||||
series: chess-friend
|
|
||||||
categories: Tech
|
|
||||||
tags: [Programming, Chess]
|
|
||||||
---
|
|
||||||
|
|
||||||
I started [playing a lot of chess][chess-post] recently. As often happens with
|
|
||||||
me, it wasn't very long until I started wondering how I could Do Programming To
|
|
||||||
It.
|
|
||||||
|
|
||||||
I found the mostly excellent, occasionally vague and confusing [Chess
|
|
||||||
Programming Wiki][cpwiki] and have been using that as a guide. It helpfully says
|
|
||||||
this on it's [Getting Started][cpgs] page:
|
|
||||||
|
|
||||||
> The **very first step** to writing a chess engine is to write a complete, bug
|
|
||||||
> free board representation that knows every rule of chess.
|
|
||||||
|
|
||||||
As a software engineer, the "bug free" bit cracks me up.
|
|
||||||
|
|
||||||
My engine is called ChessFriend. It uses [bitboards][cpbb] for its board
|
|
||||||
representation. As of this post, I've managed to write a board representation
|
|
||||||
that allows me to place pieces of both colors on any square, and I'm hacking
|
|
||||||
away at the move generator. I've also written a small command line "board
|
|
||||||
explorer" utility that can interact with my board representation. Of course, it
|
|
||||||
has a pile of unit tests, helping me inch ever-so-slowly toward that blissful
|
|
||||||
bug-free state.
|
|
||||||
|
|
||||||
It's written in Rust. I've [_mostly_][rust-bc-toot] avoided fighting with the
|
|
||||||
borrow checker.
|
|
||||||
|
|
||||||
[chess-post]: {{< ref "chess" >}}
|
|
||||||
[cpwiki]: https://www.chessprogramming.org/Main_Page
|
|
||||||
[cpgs]: https://www.chessprogramming.org/Getting_Started
|
|
||||||
[cpbb]: https://www.chessprogramming.org/Bitboards
|
|
||||||
[rust-bc-toot]: https://mastodon.social/@erynofwales/111637122773195611
|
|
|
@ -1,36 +0,0 @@
|
||||||
---
|
|
||||||
title: "Less Instagram, More Blog"
|
|
||||||
description: Resolving, yet again, to blog more and social media less.
|
|
||||||
date: 2023-12-27T08:56:44-07:00
|
|
||||||
categories: Meta
|
|
||||||
tags: [Writing, Resolutions, Habits]
|
|
||||||
---
|
|
||||||
|
|
||||||
I've been thinking the last few days about how to make use of my blog in 2024. I
|
|
||||||
made some vague noises in this general direction a few days ago in my [What
|
|
||||||
Should I Blog About?][what-to-blog-about] post too.
|
|
||||||
|
|
||||||
My vision since I started posting more here has been to use it as a place to
|
|
||||||
share all sorts of things: stuff I'm working on or thinking about; photos; and
|
|
||||||
stories from travel and life.
|
|
||||||
|
|
||||||
I often fall into a trap when I sit down to write something in which I feel like
|
|
||||||
I must first invent the Universe. The need to explain everything from first
|
|
||||||
principles seriously hampers my ability (and frankly, desire) to write anything.
|
|
||||||
I don't want to only post carefully thought out, highly edited and polished
|
|
||||||
pieces, though I certainly hope _some_ of my posts reach that bar. I hope to
|
|
||||||
also post quick notes and sketches of ideas. I've enjoyed reading some quicker
|
|
||||||
posts from {{< tess >}} and [Elaine][e] this past year, and I'd like to follow
|
|
||||||
their example.
|
|
||||||
|
|
||||||
{{< youtube zSgiXGELjbc >}}
|
|
||||||
|
|
||||||
I'm not setting myself a specific goal here. The idea is just "more" in a
|
|
||||||
certain general direction. I don't want to commit to a specific frequency or
|
|
||||||
quality. Instead, I'm hoping this post sets a foundation on which to build a
|
|
||||||
sustainable thinking-writing-sharing habit.
|
|
||||||
|
|
||||||
Thanks for coming along. :)
|
|
||||||
|
|
||||||
[what-to-blog-about]: {{< ref "what-to-blog-about" >}}
|
|
||||||
[e]: https://diplograph.net
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:562f6c157c43ef11ae55274b0bd1403c2cbb9a64b82443a2d9f4fb58b32606fd
|
|
||||||
size 148873
|
|
|
@ -1,15 +0,0 @@
|
||||||
---
|
|
||||||
title: "Mastodon Icon"
|
|
||||||
date: 2023-08-11T08:23:25-07:00
|
|
||||||
categories: ["Tech"]
|
|
||||||
tags: ["Meta", "Erynwells.me", "Web"]
|
|
||||||
---
|
|
||||||
|
|
||||||
I finally got around to replacing the Twitter icon in the site's header with a
|
|
||||||
link to my Mastodon page. It was surprisingly tricky because of how I styled and
|
|
||||||
layed out those icons. I was able to clean up the SVGs a little bit too.
|
|
||||||
|
|
||||||
These days I have [way too many social media accounts][where-to-find-me]. I'm
|
|
||||||
mostly on Mastodon and Instagram.
|
|
||||||
|
|
||||||
[where-to-find-me]: {{< ref "/about/where-am-i" >}}
|
|
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 197 KiB |
Before Width: | Height: | Size: 429 KiB |
|
@ -1,47 +0,0 @@
|
||||||
---
|
|
||||||
title: "Netscape Meteors: Retrospective"
|
|
||||||
date: 2023-08-05T17:14:40-07:00
|
|
||||||
description: Someone shared my Netscape Meteors post on the Orange Website, causing it to be moderately viral for a few days. Here’s an update on the web traffic my server received.
|
|
||||||
categories: ["Tech"]
|
|
||||||
tags:
|
|
||||||
- History
|
|
||||||
- Meta
|
|
||||||
- Netscape
|
|
||||||
- Web
|
|
||||||
- Web Browsers
|
|
||||||
---
|
|
||||||
|
|
||||||
Last week, I published a small blog post about trying to find the original
|
|
||||||
[Netscape "meteors"][meteors] loading animation. At some point that night,
|
|
||||||
someone posted it on the orange website, and ... it did some numbers.
|
|
||||||
|
|
||||||
As of today, five days later, my server has registered just over 200,000 page
|
|
||||||
accesses.
|
|
||||||
|
|
||||||
{{< figures/code >}}
|
|
||||||
```txt
|
|
||||||
% grep "GET /blog/2023/08/netscape-meteors/" \
|
|
||||||
/var/log/nginx/access.log | wc -l
|
|
||||||
200201
|
|
||||||
```
|
|
||||||
{{< /figures/code >}}
|
|
||||||
|
|
||||||
Bandwidth saw a bit of a spike too.
|
|
||||||
|
|
||||||
{{< figures/image name=bandwidth-2023-08-05.png shouldShowTitle=false >}}
|
|
||||||
|
|
||||||
To my knowledge this is the first time anything I've published on the internet
|
|
||||||
has been picked up by Hacker News. It's jarring to realize so many people have
|
|
||||||
visited my website in the last several days, reading this and other things I've
|
|
||||||
written, listening to music I've published, and looking through photos I've
|
|
||||||
posted. It's a little like having surprise house guests and realizing you
|
|
||||||
haven't tidied up in a little while.
|
|
||||||
|
|
||||||
I only took a brief look at the comments. I was pleased to see they were civil,
|
|
||||||
and mostly reminiscing about the days of Netscape and the early web. I had a few
|
|
||||||
people reach out to tell me they enjoyed my post too.
|
|
||||||
|
|
||||||
Thanks, y'all, for reading my little corner of the web, and for your kind
|
|
||||||
words.
|
|
||||||
|
|
||||||
[meteors]: {{< ref "blog/2023/netscape-meteors" >}}
|
|
|
@ -1,97 +0,0 @@
|
||||||
---
|
|
||||||
title: "Netscape Meteors"
|
|
||||||
date: 2023-08-01T18:23:33-07:00
|
|
||||||
description: I went on a hunt to find the "Meteors" loading animation from Netscape back in the 90s, and wrote up my adventure.
|
|
||||||
resources:
|
|
||||||
- name: netscape60
|
|
||||||
title: Netscape Meteor Loading Animation
|
|
||||||
src: netscape-meteors.gif
|
|
||||||
- name: netscape-modified60
|
|
||||||
title: Modified Netscape Meteor Loading Animation, Small
|
|
||||||
src: netscape-meteors-modified-60.gif
|
|
||||||
- name: netscape-modified240
|
|
||||||
title: Modified Netscape Meteor Loading Animation, Large
|
|
||||||
src: netscape-meteors-modified-240.gif
|
|
||||||
- name: rectangular-pixels
|
|
||||||
title: Rectangular Pixels
|
|
||||||
src: rectangular-pixels.png
|
|
||||||
alt: "A zoomed in screenshot of an animation frame with pixel grid enabled,
|
|
||||||
showing rectangular pixels"
|
|
||||||
categories: Tech
|
|
||||||
tags: ["Netscape", "History", "Web Browsers", "Web"]
|
|
||||||
---
|
|
||||||
|
|
||||||
I went on a small journey the last couple days to find the original Netscape
|
|
||||||
Navigator "meteors" animation. This one has a special place in my head and
|
|
||||||
heart because it is so clearly connected to my memories of discovering the
|
|
||||||
web as a kid. Here it is in its original 60×60 px glory:
|
|
||||||
|
|
||||||
{{< figures/image name=netscape60 shouldShowTitle=false size=small >}}
|
|
||||||
|
|
||||||
I started out doing some web searches that turned up several versions. One was
|
|
||||||
promising but far too big: 400×400 px. Worse, after some shoddy resize
|
|
||||||
attempts, the "pixels" had become rectangular.
|
|
||||||
|
|
||||||
{{< figures/image name=rectangular-pixels shouldShowTitle=false size=small >}}
|
|
||||||
|
|
||||||
This would not do.
|
|
||||||
|
|
||||||
I continued searching, hoping to find the original animations. I found someone's
|
|
||||||
[mirror of Netscape 5.0 on Github][gh-netscape]. Then I found some [very old
|
|
||||||
versions of Mozilla][moz-netscape] on a Mozilla FTP server. Sadly, the
|
|
||||||
animations had been stripped out of these archives. :(
|
|
||||||
|
|
||||||
Frustrated with hitting several deadends, I complained to {{< tess >}} and
|
|
||||||
wondered aloud if anyone might have the original images stashed away somewhere.
|
|
||||||
She quipped that if anyone did, it would be Jamie Zawinski.
|
|
||||||
|
|
||||||
A little later, I posted about it on Mastodon.
|
|
||||||
|
|
||||||
<iframe
|
|
||||||
src="https://mastodon.social/@erynofwales/110817133916254596/embed"
|
|
||||||
class="mastodon-embed"
|
|
||||||
style="max-width: 100%; border: 0"
|
|
||||||
width="400"
|
|
||||||
allowfullscreen="allowfullscreen">
|
|
||||||
</iframe>
|
|
||||||
|
|
||||||
And wouldn't you know it, a friend tagged [`@jwz`][masto-jwz] asking if he had
|
|
||||||
it, and a few moments later I got a reply from [Jamie][jwz] himself.
|
|
||||||
|
|
||||||
<iframe
|
|
||||||
src="https://mastodon.social/@jwz/110817331045294426/embed"
|
|
||||||
class="mastodon-embed"
|
|
||||||
style="max-width: 100%; border: 0"
|
|
||||||
width="400"
|
|
||||||
allowfullscreen="allowfullscreen">
|
|
||||||
</iframe>
|
|
||||||
|
|
||||||
If you don't know, Jamie Zawinski is well-know for working on several important
|
|
||||||
software projects in the '90s. He worked on Netscape Navigator, built and
|
|
||||||
maintains [Xscreensaver][xscreensaver], and several other things. Nowadays, he
|
|
||||||
owns and runs [DNA Lounge][dna] in San Francisco.
|
|
||||||
|
|
||||||
There are a lot of neat bits of web browser history on the page he linked --
|
|
||||||
totally worth a quick look over -- but most important to the quest at hand, it
|
|
||||||
had that Netscape meteors loading animation.
|
|
||||||
|
|
||||||
The original one has some small artifacts on the left side of frame 10 that
|
|
||||||
render as red and orange pixels. These bothered me enough that I made a version
|
|
||||||
that replaces those pixels with ones that match the surrounding pixels. Here's
|
|
||||||
the modified 60×60 one and a bigger 240×240 px one, for good
|
|
||||||
measure:
|
|
||||||
|
|
||||||
{{< content-grid columns=2 >}}
|
|
||||||
{{< figures/image name="netscape-modified60" shouldShowTitle=false shouldResize=false size=small >}}
|
|
||||||
{{< figures/image name="netscape-modified240" shouldShowTitle=false shouldResize=false size=small >}}
|
|
||||||
{{< /content-grid >}}
|
|
||||||
|
|
||||||
<script src="https://mastodon.social/embed.js" async="async"></script>
|
|
||||||
|
|
||||||
[gh-netscape]: https://github.com/zii/netscape
|
|
||||||
[moz-netscape]: https://ftp.mozilla.org/pub/mozilla/source/
|
|
||||||
[masto-jwz]: https://mastodon.social/@jwz
|
|
||||||
[jwz]: https://www.jwz.org
|
|
||||||
[xscreensaver]: https://www.jwz.org/xscreensaver/
|
|
||||||
[dna]: https://www.jwz.org
|
|
||||||
[about-jwz]: https://www.jwz.org/doc/about-jwz.html
|
|
Before Width: | Height: | Size: 248 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 781 KiB |
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
title: Once Upon a Time I Lived on Mars by Kate Greene
|
title: "Once Upon a Time I Lived on Mars by Kate Greene"
|
||||||
slug: once-upon-a-time-i-lived-on-mars-book
|
slug: once-upon-a-time-i-lived-on-mars-book
|
||||||
description: A brief book report.
|
|
||||||
date: 2023-02-20T09:16:48-08:00
|
date: 2023-02-20T09:16:48-08:00
|
||||||
date_finished: 2023-02-20T00:00:00-08:00
|
date_finished: 2023-02-20T00:00:00-08:00
|
||||||
|
layout: two-column
|
||||||
categories: Books
|
categories: Books
|
||||||
tags: [Memoirs, Space]
|
tags: [Memoirs, Space]
|
||||||
series: 2023-books
|
series: 2023-books
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
title: "Pajaro Dunes"
|
|
||||||
date: 2023-05-30T08:31:34-07:00
|
|
||||||
tags: [Travel, Beaches, Tess, EJ, Vacations]
|
|
||||||
---
|
|
||||||
|
|
||||||
{{< tess >}}, EJ, and I took a weekend trip down the coast over Memorial Day
|
|
||||||
weekend this year to stay in a beachside condo in Pajaro Dunes, just west of
|
|
||||||
Watsonville. We enjoyed hanging out on the beach, playing music and games,
|
|
||||||
building [Kiwi Crates][kiwi], and just generally being together. I took a couple
|
|
||||||
photos too. :)
|
|
||||||
|
|
||||||
{{< photo "2023/pajaro-dunes" >}}
|
|
||||||
|
|
||||||
{{< photo "2023/sunset-over-pajaro-dunes" >}}
|
|
||||||
|
|
||||||
[kiwi]: https://www.kiwico.com
|
|
|
@ -3,6 +3,7 @@ title: "The Storyteller by Dave Grohl"
|
||||||
slug: the-storyteller-book
|
slug: the-storyteller-book
|
||||||
date: 2023-01-31T09:17:09-08:00
|
date: 2023-01-31T09:17:09-08:00
|
||||||
date_finished: 2023-01-31T09:17:09-08:00
|
date_finished: 2023-01-31T09:17:09-08:00
|
||||||
|
layout: two-column
|
||||||
series: 2023-books
|
series: 2023-books
|
||||||
categories: Books
|
categories: Books
|
||||||
tags: [Memoirs]
|
tags: [Memoirs]
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
title: Trip to Japan
|
|
||||||
date: 2023-04-14T21:40:21+09:00
|
|
||||||
categories: Travel
|
|
||||||
tags:
|
|
||||||
- Travel
|
|
||||||
- japan
|
|
||||||
---
|
|
||||||
|
|
||||||
At the beginning of April, {{< tess >}} and I took a trip to Japan for two
|
|
||||||
weeks. She had a work meeting to attend in Tōkyō, and we were lucky to be able
|
|
||||||
to extend the trip to take some vacation before her meeting.
|
|
||||||
|
|
||||||
This was my first trip to Japan. I had been wanting to travel there since I was
|
|
||||||
a kid playing Pokémon Red on my OG Game Boy. To say I was excited is a bit of an
|
|
||||||
understatement.
|
|
||||||
|
|
||||||
You can read all about our trip [on my travel log page][series-page]. Tess also
|
|
||||||
wrote about it [on her website][tess-post].
|
|
||||||
|
|
||||||
[series-page]: {{< ref "/series/2023-japan" >}}
|
|
||||||
[tess-post]: https://tess.oconnor.cx/2023/04/japan
|
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
title: What Should I Blog About?
|
|
||||||
date: 2023-12-20T08:06:34-08:00
|
|
||||||
link: https://css-irl.info/what-to-blog-about-when-you-dont-know-what-to-blog-about/
|
|
||||||
categories: Tech
|
|
||||||
tags: [Writing]
|
|
||||||
---
|
|
||||||
|
|
||||||
I came across this handy list of [things to blog about when you don't know what
|
|
||||||
to blog about][link]. As someone who often doesn't know what to blog about, it's
|
|
||||||
nice to have a list of ideas for what to blog about.
|
|
||||||
|
|
||||||
[link]: https://css-irl.info/what-to-blog-about-when-you-dont-know-what-to-blog-about/
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:7bc98cce772e0c075daed5a6dd9b1c18e6fd19e94ac68197893fd14cb58cf790
|
|
||||||
size 83946
|
|
|
@ -1,27 +0,0 @@
|
||||||
---
|
|
||||||
title: "Yerba Buena by Nina LaCour"
|
|
||||||
date: 2023-11-23T09:50:06-07:00
|
|
||||||
series: 2023-books
|
|
||||||
categories: Books
|
|
||||||
tags: [Romance, Lesbians, Queerness]
|
|
||||||
resources:
|
|
||||||
- name: cover
|
|
||||||
src: cover.jpg
|
|
||||||
title:
|
|
||||||
params:
|
|
||||||
alt: An orange book cover with green leafy sprigs around the edges. Profiles of two women, overlapping and facing opposite directions. "Yerba Buena a novel" is written across the top half.
|
|
||||||
---
|
|
||||||
|
|
||||||
I thought this book was a lesbian romance -- and it is -- but it's so much more
|
|
||||||
too. It's the story of two women and the difficult pasts they emerge out of. I
|
|
||||||
really enjoyed how LaCour wove together their processing of that trauma with
|
|
||||||
growing into young women, making questionable choices, finding themselves, and
|
|
||||||
ultimately each other. It's heavy at times, but also beautiful. I really enjoyed
|
|
||||||
it. Thank you, {{< tess >}}, for the gift. ❤️
|
|
||||||
|
|
||||||
Get it at [Folio][folio] in San Francisco, or on [Bookshop.org][bookshop].
|
|
||||||
|
|
||||||
{{< figures/image name=cover class=content-width >}}
|
|
||||||
|
|
||||||
[folio]: https://www.foliosf.com/book/9781250810519
|
|
||||||
[bookshop]: https://bookshop.org/p/books/yerba-buena-nina-lacour/18721506?ean=9781250810519
|
|
|
@ -1,28 +0,0 @@
|
||||||
---
|
|
||||||
title: "You Deserve a Tech Union by Ethan Marcotte"
|
|
||||||
date: 2023-12-16T08:19:41-08:00
|
|
||||||
draft: true
|
|
||||||
series: 2023-books
|
|
||||||
categories: Books
|
|
||||||
tags: [Unions, Tech]
|
|
||||||
---
|
|
||||||
|
|
||||||
Ethan's book came out in August 2023, and I've been eager to read it since he
|
|
||||||
announced it. Unionization has been a hot topic in the US over the last several
|
|
||||||
years. I've rooted for workers at Amazon, Google, Starbucks, and Apple to form
|
|
||||||
unions and advocate for their rights vis á vis their employers. I think that
|
|
||||||
work is so important.
|
|
||||||
|
|
||||||
This book provides an overview of the history of unions in the US, and in the US
|
|
||||||
tech industry, plus some helpful thoughts on how to form unions in your
|
|
||||||
workplace.
|
|
||||||
|
|
||||||
I found his arguments about why unions are important, even in an industrial
|
|
||||||
sector considered to be rather plush compared to many others. Many people think
|
|
||||||
of unions as organizations that advocate for _more_ privileges and protections
|
|
||||||
for workers. However, at least as important as that work, they also ensure that
|
|
||||||
workers retain the privileges they already have.
|
|
||||||
|
|
||||||
Get it from [A Book Apart][aba].
|
|
||||||
|
|
||||||
[aba]: https://abookapart.com/products/you-deserve-a-tech-union
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
title: 2024
|
|
||||||
date: 2024-01-01
|
|
||||||
---
|
|
|
@ -1,56 +0,0 @@
|
||||||
---
|
|
||||||
title: ¡Ay Carmela!
|
|
||||||
description: |
|
|
||||||
Pero nada pueden bombas<br>
|
|
||||||
Donde sobra corazón
|
|
||||||
date: 2024-11-06T08:30:22-08:00
|
|
||||||
categories: Politics
|
|
||||||
tags:
|
|
||||||
- Music
|
|
||||||
- United States
|
|
||||||
- España
|
|
||||||
- Guerra Civil Española
|
|
||||||
---
|
|
||||||
|
|
||||||
> ```text
|
|
||||||
> El ejército del Ebro
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
> El ejército del Ebro
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
>
|
|
||||||
> Una noche el río pasó
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
> Una noche el río pasó
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
>
|
|
||||||
> Pero nada pueden bombas
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
> Pero nada pueden bombas
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
>
|
|
||||||
> Donde sobra corazón
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
> Donde sobra corazón
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
>
|
|
||||||
> Contraataques muy rabiosos
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
> Contraataques muy rabiosos
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
>
|
|
||||||
> <strong>Deberemos resistir</strong>
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
> Deberemos resistir
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
>
|
|
||||||
> Pero igual que combatimos
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
> Pero igual que combatimos
|
|
||||||
> Rumbala rumbala rum-ba-la
|
|
||||||
>
|
|
||||||
> <strong>Prometemos resistir</strong>
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
> Prometemos resistir
|
|
||||||
> ¡Ay Carmela! ¡Ay Carmela!
|
|
||||||
> ```
|
|
||||||
{cite="https://music.apple.com/us/album/ay-carmela/1119265269?i=1119265947" caption="Traditional; emphasis mine"}
|
|
|
@ -1,23 +0,0 @@
|
||||||
---
|
|
||||||
title: B612
|
|
||||||
date: 2024-03-09T08:38:03-08:00
|
|
||||||
description: An open source font I rediscovered recently.
|
|
||||||
categories: Tech
|
|
||||||
tags: [Fonts]
|
|
||||||
resources:
|
|
||||||
- name: specimen
|
|
||||||
src: specimen.png
|
|
||||||
title: B612 y B612 Mono
|
|
||||||
params:
|
|
||||||
alt: "
|
|
||||||
Una muestra de B612 y B612 Mono. El mismo pangrama inglés, “quick brown fox,” es escrito en líneas separadas.
|
|
||||||
"
|
|
||||||
---
|
|
||||||
|
|
||||||
Recientemente, redescubrí la fuente [B612][b612]. Fue engargado por AirBus para
|
|
||||||
los tableros de instrumentos de sus aviones. Hace unos años que hizen de código
|
|
||||||
abierto.
|
|
||||||
|
|
||||||
{{% figures/image name=specimen class=content-width shouldResize=false %}}
|
|
||||||
|
|
||||||
[b612]: https://b612-font.com
|
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
title: B612
|
|
||||||
date: 2024-03-09T08:38:03-08:00
|
|
||||||
description: An open source font I rediscovered recently.
|
|
||||||
categories: Tech
|
|
||||||
tags: [Fonts]
|
|
||||||
resources:
|
|
||||||
- name: specimen
|
|
||||||
src: specimen.png
|
|
||||||
title: B612 and B612 Mono
|
|
||||||
params:
|
|
||||||
alt: "
|
|
||||||
A specimen of B612 and B612 Mono. The same “quick brown fox” pangram sentence is
|
|
||||||
set in each font on separate lines.
|
|
||||||
"
|
|
||||||
---
|
|
||||||
|
|
||||||
I recently rediscovered [B612][b612], a font comissioned by AirBus to serve as
|
|
||||||
the font for their airplanes' instrument panels. They made it open source
|
|
||||||
several years ago.
|
|
||||||
|
|
||||||
{{% figures/image name=specimen class=content-width shouldResize=false %}}
|
|
||||||
|
|
||||||
[b612]: https://b612-font.com
|
|
Before Width: | Height: | Size: 150 KiB |
|
@ -1,64 +0,0 @@
|
||||||
---
|
|
||||||
title: Books I Read in 2024
|
|
||||||
slug: books
|
|
||||||
date: 2024-12-31
|
|
||||||
tags:
|
|
||||||
- Books
|
|
||||||
- Reading
|
|
||||||
---
|
|
||||||
|
|
||||||
I enjoy reading quite a bit. Nevertheless, it's something I need to be
|
|
||||||
intentional about incorporating into my life. I usually to finish a modest
|
|
||||||
number of books in a year. This year I made it to a nice round ten.
|
|
||||||
|
|
||||||
A big theme of the year is the [Murderbot Diaries][murderbot]. My friend Jess
|
|
||||||
recommended them to me a while back, and I finally got around to [All Systems
|
|
||||||
Red][murderbot1] last year. I was hooked enough that {{< tess >}} bought me the
|
|
||||||
subsequent three for Christmas last year. So you're gonna see almost all the
|
|
||||||
Murderbot books on this list.
|
|
||||||
|
|
||||||
[Bindle Punk Bruja](https://marthawells.com/networkeffect.htm) by Desideria Mesa
|
|
||||||
: Mesa's debut novel is set in 1920s Kansas City and centers around Rose, the
|
|
||||||
daughter of Mexican immigrants who takes on the city's mob bosses with the help
|
|
||||||
of friends and some witchy magical powers. This was a gift from my sister
|
|
||||||
[Anna][anna] for Christmas last year.
|
|
||||||
|
|
||||||
[The Deep Sky](https://www.yumekitasei.com/projects-6) by Yume Kitasei
|
|
||||||
: A murder mystery that takes place in space on a one-way mission to colonize a
|
|
||||||
far-off planet. A gift from my sister.
|
|
||||||
|
|
||||||
[Wolfsong](https://www.tjklunebooks.com/wolfsong) by TJ Klune
|
|
||||||
: Warewolves, magic, queerness, and family. A gift from my sister.
|
|
||||||
|
|
||||||
[Fingersmith](https://www.penguinrandomhouse.com/books/348400/fingersmith-by-sarah-waters/) by Sarah Waters
|
|
||||||
: Mystery, intrigue, gay ladies, and some incredible plot twists. Tess gave me
|
|
||||||
this one.
|
|
||||||
|
|
||||||
[Artificial Condition](https://marthawells.com/murderbot2.htm) by Martha Wells
|
|
||||||
: Murderbot investigates its past.
|
|
||||||
|
|
||||||
[Rogue Protocol](https://marthawells.com/murderbot3.htm) by Martha Wells
|
|
||||||
: Murderbot goes digging for evidence against its former corporate master,
|
|
||||||
GrayCris.
|
|
||||||
|
|
||||||
[Exit Strategy](https://marthawells.com/murderbot4.htm) by Martha Wells
|
|
||||||
: Murderbot attempts to save its former owner from GrayCris.
|
|
||||||
|
|
||||||
[Network Effect](https://marthawells.com/networkeffect.htm) by Martha Wells
|
|
||||||
: Continuing the Murderbot obsession, I picked up this one from a [Books
|
|
||||||
Inc][booksinc] in San Francisco's Marina district.
|
|
||||||
|
|
||||||
[I'm Starting to Worry About This Black Box of Doom](https://us.macmillan.com/books/9781250285959/imstartingtoworryaboutthisblackboxofdoom/) by Jason Pargin
|
|
||||||
: Anna and I decided to read this together, book club style. It's a ridiculous
|
|
||||||
romp across the US with some deep commentary on the perils of social media: how
|
|
||||||
it promotes dogpiling in ways that can destroy lives, and the groupthink that
|
|
||||||
arises from being terminally online.
|
|
||||||
|
|
||||||
[The Full Moon Coffee Shop](https://www.penguinrandomhouse.com/books/746333/the-full-moon-coffee-shop-by-mai-mochizuki/) by Mai Mochizuki
|
|
||||||
: A cute, short read about a mysterious popup coffee shop that appears in Kyōto
|
|
||||||
during the full moon.
|
|
||||||
|
|
||||||
[murderbot]: https://marthawells.com/murderbot.htm
|
|
||||||
[murderbot1]: https://marthawells.com/murderbot1.htm
|
|
||||||
[anna]: https://www.instagram.com/anna.e.az/
|
|
||||||
[booksinc]: https://booksinc.net
|
|
|
@ -1,26 +0,0 @@
|
||||||
---
|
|
||||||
title: BSD `make` != GNU `make`
|
|
||||||
date: 2024-07-31T07:27:50-07:00
|
|
||||||
description: Discussing some differences between the BSD and GNU variants of `make(1)`.
|
|
||||||
categories: Tech
|
|
||||||
tags: [BSD, GNU, Make, Software]
|
|
||||||
---
|
|
||||||
|
|
||||||
While working on a little home networking project, I found myself having to
|
|
||||||
write a Makefile in a FreeBSD environment.
|
|
||||||
|
|
||||||
Having written only GNU Makefiles over the course of my life, I was surprised to
|
|
||||||
find that my recipes just ... didn't work.
|
|
||||||
|
|
||||||
Some resources that helped me make the jump to writing BSD style Makefiles are:
|
|
||||||
|
|
||||||
* The [`make(1)`][man] man page that ships with FreeBSD. It's surprisingly
|
|
||||||
thorough, but difficult to search through if you don't know exactly what
|
|
||||||
you're looking for.
|
|
||||||
* This FreeBSD mailing list post quoting [GNU make's summary of
|
|
||||||
differences][diffs] from BSD `make`.
|
|
||||||
* And [PMake: A Tutorial][pmake], hosted on FreeBSD.org.
|
|
||||||
|
|
||||||
[man]: https://man.freebsd.org/cgi/man.cgi?make(1)
|
|
||||||
[diffs]: https://lists.freebsd.org/pipermail/freebsd-questions/2007-April/147533.html
|
|
||||||
[pmake]: https://docs-archive.freebsd.org/44doc/psd/12.make/paper.pdf
|
|
|
@ -1,69 +0,0 @@
|
||||||
---
|
|
||||||
title: "Cat in the Cream Cookies"
|
|
||||||
date: 2024-11-18
|
|
||||||
tags:
|
|
||||||
- Cooking
|
|
||||||
- Cookies
|
|
||||||
- Oberlin
|
|
||||||
- Recipes
|
|
||||||
---
|
|
||||||
|
|
||||||
One of my favorite memories from my time as a student at [Oberlin][ob] was going
|
|
||||||
to the [Cat in the Cream][cat]. I saw a lot of great plays, improv shows, and
|
|
||||||
jazz ensembles by students, and several performances by visiting musical guests.
|
|
||||||
|
|
||||||
The other highlight of these memories was the oatmeal chocolate chip cookies you
|
|
||||||
could get for $1. They're somewhere between a small cake and a cookie, often
|
|
||||||
fresh-baked (they often had a hard time keeping up with demand on show nights).
|
|
||||||
Honestly they're everything I want in a cookie. I think the recipe was printed
|
|
||||||
in an edition of the [Alumni Magazine][mag] at some point in the last several
|
|
||||||
years, but this [video][video] I found online was the closest I had to hand, so
|
|
||||||
I'm copying it here.
|
|
||||||
|
|
||||||
|
|
||||||
## Recipe
|
|
||||||
|
|
||||||
**Yield:** 20 cookies
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
|
|
||||||
* Measuring cups and spoons
|
|
||||||
* 2 large mixing bowls
|
|
||||||
* Spoon or electric mixer to mix wet ingredients
|
|
||||||
* Whisk to combine dry ingredients (optional)
|
|
||||||
* 1 or 2 half-size sheet pans for baking
|
|
||||||
|
|
||||||
|
|
||||||
### Ingredients
|
|
||||||
|
|
||||||
* 2 cups butter, softened
|
|
||||||
* 1 cup granulated sugar
|
|
||||||
* 2 cups brown sugar or 2 tablespoons of molasses
|
|
||||||
* 4 eggs, lightly beaten
|
|
||||||
* 2 teaspoons vanilla
|
|
||||||
* 4 cups flour
|
|
||||||
* 2 teaspoons baking soda
|
|
||||||
* 2 teaspoons salt
|
|
||||||
* Dash cinnamon
|
|
||||||
* 4 cups oats
|
|
||||||
* 3 cups chocolate chips
|
|
||||||
|
|
||||||
|
|
||||||
### Method
|
|
||||||
|
|
||||||
1. Preheat oven to 375°.
|
|
||||||
1. Combine butter, sugars, and/or molasses in a bowl.
|
|
||||||
1. Add eggs and vanilla. Stir to combine.
|
|
||||||
1. Mix flour, baking soda, salt, and cinnamon in a separate bowl.
|
|
||||||
1. Add dry ingredients above to the wet. Stir to combine.
|
|
||||||
1. Add in oats and chocolate chips. Stir to combine.
|
|
||||||
1. Drop in large hunks (about 1/4 cup) onto ungreased baking sheets. They don't
|
|
||||||
spread during baking, but you'll only get six cookies on a half sheet.
|
|
||||||
1. Bake until the edges are slightly brown. The original recipe says 20 minutes,
|
|
||||||
but 15 was enough in my oven.
|
|
||||||
|
|
||||||
|
|
||||||
[ob]: https://www.oberlin.edu
|
|
||||||
[cat]: https://www.thecatinthecream.com/our-menu.html
|
|
||||||
[video]: https://vimeo.com/827645032
|
|
||||||
[mag]: https://www.oberlin.edu/communications/editorial/alumni-magazine
|
|
|
@ -1,68 +0,0 @@
|
||||||
---
|
|
||||||
title: Chicago
|
|
||||||
date: 2024-10-05T18:58:05-05:00
|
|
||||||
description: >
|
|
||||||
Tess and I traveled to Chicago to attend the wedding of one of my oldest
|
|
||||||
friends, visit some family, and do a little sightseeing.
|
|
||||||
resources:
|
|
||||||
- src: wedding.jpg
|
|
||||||
title: Bill and Ashley walked each other in. My dad officiated.
|
|
||||||
params:
|
|
||||||
alt: >
|
|
||||||
To the left, a man wearing a suit and white pastoral stole stands near a
|
|
||||||
mic. To the right, a woman in a white dress, and a man in a tuxedo walk
|
|
||||||
into the frame. Everyone is smiling.
|
|
||||||
- src: quincy.jpg
|
|
||||||
title: >
|
|
||||||
Wood plank platforms, Copperplate lettering, and wingding-style hands to
|
|
||||||
direct you where you need to go.
|
|
||||||
params:
|
|
||||||
alt: >
|
|
||||||
A large station sign, framed with a simple wood frame, hanging on the
|
|
||||||
railing around a station platform stairway. It declares the station's
|
|
||||||
name, Quincy, and points to exits to the left and right.
|
|
||||||
- src: tess-kerryman.jpg
|
|
||||||
title: >
|
|
||||||
The Kerryman is a great Irish bar in River North, owned and operated by
|
|
||||||
one of Tess' distant Irish cousins.
|
|
||||||
params:
|
|
||||||
alt: >
|
|
||||||
Tess, on the left, stands outside a large pub on a stree corner. The pub
|
|
||||||
has outdoor seating.
|
|
||||||
tags:
|
|
||||||
- Travel
|
|
||||||
- Friends
|
|
||||||
- Family
|
|
||||||
- "Chicago, IL"
|
|
||||||
- Illinois
|
|
||||||
---
|
|
||||||
|
|
||||||
This past week {{< tess >}} and I traveled to Chicago to attend the wedding of
|
|
||||||
Bill, one of my oldest friends, and his partner Ashley.
|
|
||||||
|
|
||||||
{{< figures/image name=wedding.jpg >}}
|
|
||||||
|
|
||||||
They've been a couple for a long time. I'm thrilled that they've decided to take
|
|
||||||
this step together, and grateful to have been invited to witness it. Bill also
|
|
||||||
asked my dad to perform the ceremony, and I think he did an excellent job.
|
|
||||||
|
|
||||||
{{< figures/image name=quincy.jpg >}}
|
|
||||||
|
|
||||||
After the wedding day, we spent several days in Chicago sightseeing and visiting
|
|
||||||
with friends and family. We went record shopping at [Reckless][reckless], walked
|
|
||||||
down Navy Pier and Michigan Avenue, rode the L (including a stop a the
|
|
||||||
delightfully old timey [Quincy Station][quincy]), took selfies at the
|
|
||||||
[Bean][bean], and caught Hokusai's [Great Wave Off Kanagawa][wave] at the [Art
|
|
||||||
Institute of Chicago][artic] on a limited exhibition. We had breakfast at
|
|
||||||
[Tary][tary], and stopped in for pints at [The Kerryman][kerryman], a pub owned
|
|
||||||
by one of Tess' distant Irish cousins.
|
|
||||||
|
|
||||||
{{< figures/image name=tess-kerryman.jpg >}}
|
|
||||||
|
|
||||||
[reckless]: https://www.reckless.com
|
|
||||||
[bean]: https://millenniumparkfoundation.org/art-architecture/cloud-gate/
|
|
||||||
[artic]: https://www.artic.edu
|
|
||||||
[tary]: https://tarycoffee.com/menu
|
|
||||||
[kerryman]: https://kerrymanchicago.com/drinks/
|
|
||||||
[wave]: https://en.wikipedia.org/wiki/The_Great_Wave_off_Kanagawa
|
|
||||||
[quincy]: https://www.transitchicago.com/station/quin/
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:3b6c19b8a5f589b21a7ab8ee846e2c23c364335142c04457998cc89e948ac39e
|
|
||||||
size 1843640
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:ad7dcfa33b5a9df82ae941f970324bcac5a25dbd36e900051032724a2af15bb2
|
|
||||||
size 2452294
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:08c30e5304a8341e6734eb5ce53c3249c935eb1eaa0401e90726ff660f8cf0e8
|
|
||||||
size 2870428
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:1d3444135aab32635f480ee8ca4a6797821ff9b6f0f68e51aa4dd0f9b5d6383e
|
|
||||||
size 3407880
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:5c17df7d3f75b0f3b18f9b5d73c1aa63b54ce5d856742f7121f4b9e9c8632e54
|
|
||||||
size 3836629
|
|