diff options
Diffstat (limited to '')
| -rw-r--r-- | www/causal.agency/Makefile | 2 | ||||
| -rw-r--r-- | www/causal.agency/dais.html | 11 | ||||
| -rw-r--r-- | www/causal.agency/index.7 | 21 | ||||
| -rw-r--r-- | www/causal.agency/style.css | 4 | ||||
| -rw-r--r-- | www/photo.causal.agency/.gitignore | 7 | ||||
| -rw-r--r-- | www/photo.causal.agency/c35/body | 1 | ||||
| -rw-r--r-- | www/photo.causal.agency/c35/lens | 1 | ||||
| -rw-r--r-- | www/photo.causal.agency/fx-3/body | 1 | ||||
| -rw-r--r-- | www/photo.causal.agency/fx-3/lens | 1 | ||||
| -rw-r--r-- | www/photo.causal.agency/gear.html | 68 | ||||
| -rw-r--r-- | www/photo.causal.agency/generate.sh | 287 | ||||
| -rw-r--r-- | www/photo.causal.agency/mastodon.sh | 54 | ||||
| -rw-r--r-- | www/photo.causal.agency/rsync.sh | 5 | ||||
| -rw-r--r-- | www/photo.causal.agency/trips.html | 373 | ||||
| -rw-r--r-- | www/text.causal.agency/043-little-blessings.7 | 78 | ||||
| -rw-r--r-- | www/text.causal.agency/044-film-review.7 | 208 | ||||
| -rw-r--r-- | www/text.causal.agency/045-time-2025.7 | 131 | ||||
| -rw-r--r-- | www/text.causal.agency/Makefile | 3 | ||||
| -rw-r--r-- | www/they.causal.agency/Makefile | 4 | ||||
| -rw-r--r-- | www/they.causal.agency/post-update.sh | 41 |
20 files changed, 1286 insertions, 15 deletions
diff --git a/www/causal.agency/Makefile b/www/causal.agency/Makefile index 8c74f8f1..d3af7265 100644 --- a/www/causal.agency/Makefile +++ b/www/causal.agency/Makefile @@ -1,7 +1,7 @@ WEBROOT = /var/www/causal.agency GEN = index.html scheme.css scheme.png -FILES = ${GEN} style.css alpha.html lands.html +FILES = ${GEN} style.css alpha.html dais.html lands.html all: ${FILES} diff --git a/www/causal.agency/dais.html b/www/causal.agency/dais.html new file mode 100644 index 00000000..109654d3 --- /dev/null +++ b/www/causal.agency/dais.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<title>Dais</title> +<meta charset="utf-8"> +<style> +body { line-height: 1.5em; max-width: 78ch; margin: auto; padding: 0.5em 1ch; } +</style> + +<h1>First occurrences of the word “dais”</h1> +<ol> +<li>A. K. Larkwood, The Unspoken Name: p. 19 +</ol> diff --git a/www/causal.agency/index.7 b/www/causal.agency/index.7 index d29c1a6b..bc212a5e 100644 --- a/www/causal.agency/index.7 +++ b/www/causal.agency/index.7 @@ -1,10 +1,10 @@ -.Dd November 28, 2023 +.Dd August 27, 2025 .Dt CAUSAL.AGENCY 7 .Os "Causal Agency" . .Sh NAME .Nm june -.Nd computer enthusiast (she/her) +.Nd enthusiast (she/they) . .Sh SYNOPSIS .Nm mail @@ -15,24 +15,21 @@ in on tilde.chat . .Sh DESCRIPTION -I make mostly IRC software in C. -I like -.Ox -but also the GPL. -I just want to read books -and try to learn to be kinder. -When I can I'd like to talk to strangers -and experience more magic. +primarily a photographer these days. +I used to write IRC software in C. +I still use it every day. . .Pp -.Lk https://git.causal.agency code +.Lk https://photo.causal.agency photos \(em .Lk https://text.causal.agency words \(em +.Lk https://git.causal.agency code +\(em .Lk /list/ mailist . .Pp -These are some things I've done: +these are some computer things I've done: .Bl -tag -width Ds .It Lk https://git.causal.agency/pounce/about pounce a multi-client-first IRC bouncer diff --git a/www/causal.agency/style.css b/www/causal.agency/style.css index ee218533..265c62c2 100644 --- a/www/causal.agency/style.css +++ b/www/causal.agency/style.css @@ -12,8 +12,8 @@ code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, code.Cd { font-weight: bold; font-family: inherit; } div.head, div.foot { display: flex; justify-content: space-between; } -.head-ltitle, .foot-left { flex: 1; } -.head-vol, .foot-date { flex: 0 1 auto; text-align: center; } +.head-ltitle, .foot-date { flex: 1; } +.head-vol { flex: 0 1 auto; text-align: center; } .head-rtitle, .foot-os { flex: 1; text-align: right; } html { font-family: monospace; line-height: 1.25em; } diff --git a/www/photo.causal.agency/.gitignore b/www/photo.causal.agency/.gitignore new file mode 100644 index 00000000..22c11f6e --- /dev/null +++ b/www/photo.causal.agency/.gitignore @@ -0,0 +1,7 @@ +[0-9]*/ +*.jpg +*.JPG +app.json +posted.txt +static/ +token.json diff --git a/www/photo.causal.agency/c35/body b/www/photo.causal.agency/c35/body new file mode 100644 index 00000000..3676b877 --- /dev/null +++ b/www/photo.causal.agency/c35/body @@ -0,0 +1 @@ +Konica C35 Automatic diff --git a/www/photo.causal.agency/c35/lens b/www/photo.causal.agency/c35/lens new file mode 100644 index 00000000..3fef9a43 --- /dev/null +++ b/www/photo.causal.agency/c35/lens @@ -0,0 +1 @@ +Konica Hexanon 38mm f/2.8 diff --git a/www/photo.causal.agency/fx-3/body b/www/photo.causal.agency/fx-3/body new file mode 100644 index 00000000..0962ee7d --- /dev/null +++ b/www/photo.causal.agency/fx-3/body @@ -0,0 +1 @@ +Yashica FX-3 diff --git a/www/photo.causal.agency/fx-3/lens b/www/photo.causal.agency/fx-3/lens new file mode 100644 index 00000000..eaab4375 --- /dev/null +++ b/www/photo.causal.agency/fx-3/lens @@ -0,0 +1 @@ +Carl Zeiss Planar T* 50mm f/1.7 diff --git a/www/photo.causal.agency/gear.html b/www/photo.causal.agency/gear.html new file mode 100644 index 00000000..04cd3781 --- /dev/null +++ b/www/photo.causal.agency/gear.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Photography Equipment</title> +<style> +html { color: #bbb; background-color: black; font-family: monospace; } +body { max-width: 76ch; margin: auto; } +</style> + +<h1>Photography Equipment</h1> +<p> +this is a (probably incomplete) list of equipment I use to Do Photography. + +<h2>Cameras</h2> +<ul> +<li>Yashica FX-3 (every day) +<li>Konica C35 Automatic (Hexanon 38mm f/2.8) +<li>Minolta XE-5 +<li>Praktica MTL3 +<li>Zenit-122 +<li>Yashica FX-2 +<li>Yashica-D (Yashikor 80mm f/3.5) +</ul> + +<h2>Lenses</h2> +<ul> +<li>Carl Zeis Planar T* 50mm f/1.7 (FX-3 default) +<li>Yashica DSB 50mm f/1.9 (FX-2 kit lens) +<li>Helios-44M-5 58mm f/2 (Zenit-122 kit lens) +<li>Pentacon 50mm f/1.8 (MTL3 kit lens) +<li>Minolta MD Rokkor-X 50mm f/1.7 (XE-5 kit lens) +<li>Osawa MC 70-210mm f/4-5 (C/Y) +<li>Takumar SMC 35mm f/3.5 +<li>Super-Takumar 135mm f/3.5 +<li>Yashica ML 28-85mm f/3.5-4.5 (wonky focus at 28mm) +<li>Yashica ML 42-75mm f/3.5-4.5 +<li>Yashica MC 35-70mm f/3.5-4.5 (bad aperture) +<li>Yashica ML 50mm f/2 (sticky aperture) +<li>Yashica DSB 28mm f/2.8 +<li>Yashica DSB 135mm f/2.8 +<li>Yashica ML Macro 55mm f/2.8 +<li>Yashica ML 28mm f/2.8 +<li>Yashica ML 75-150mm f/4 +</ul> + +<h2>Flash</h2> +<ul> +<li>Reflx Lab Simple Flash +<li>Starblitz 318M +</ul> + +<h2>Tripod</h2> +<ul> +<li>Sirui Traveler 5C +</ul> + +<h2>Scanning</h2> +<ul> +<li>Filmomat SmartConvert +<li>Fujifilm X-T5 +<li>Yashica ML Macro 55mm f/2.8 (at f/11) +<li>Yashica 13mm extension tube (also have 20mm and 27mm) +<li>Urth C/Y-X adapter +<li>Skier CS-700 copy stand +<li>Valoi 135 & 120 film holders +<li>CineStill CS-Lite +<li>the box the CS-Lite came in +</ul> diff --git a/www/photo.causal.agency/generate.sh b/www/photo.causal.agency/generate.sh new file mode 100644 index 00000000..2fbdcb68 --- /dev/null +++ b/www/photo.causal.agency/generate.sh @@ -0,0 +1,287 @@ +#!/bin/sh +set -eu + +mkdir -p static/preview static/thumbnail + +resize() { + local photo=$1 size=$2 output=$3 + if ! test -f $output; then + # FIXME: convert complains about not understanding XML + echo $output >&2 + convert $photo -auto-orient -thumbnail $size $output 2>/dev/null ||: + fi +} + +preview() { + local photo=$1 + local preview=preview/${photo##*/} + resize $photo 1500000@ static/$preview + echo $preview +} + +thumbnail() { + local photo=$1 + local thumbnail=thumbnail/${photo##*/} + resize $photo 60000@ static/$thumbnail + echo $thumbnail +} + +encode() { + sed ' + s/&/\&/g + s/</\</g + s/"/\"/g + ' "$@" +} + +page_title() { + case $1 in + (leader) echo 'Film Leader';; + (20*) date -j -f '%F' $1 '+%B %e, %Y';; + (0*) echo Roll $(dc -e "${1}p");; + esac +} + +page_head() { + local page=$1 + local title=$(page_title $page) + local date body lens film chem note + + if test -f $page/date; then + date=$(sed 's/\([0-9]\)-\([0-9]\)/\1–\2/g' $page/date | encode) + fi + if test -f $page/body; then + body=$(encode $page/body) + fi + if test -f $page/lens; then + lens=$( + sed ' + s,f/,ƒ/,g + s/\([0-9]\)-\([0-9]\)/\1–\2/g + ' $page/lens | + encode + ) + else + lens=$( + identify -format '%[EXIF:LensModel]' \ + $page/$(ls -1 $page | head -n 1) 2>/dev/null | + sed ' + s/\([A-Z]\)\([0-9]\)/\1 \2/ + s,f/,ƒ/, + s/\([0-9]\)-\([0-9]\)/\1–\2/g + ' | + encode + ) + fi + if test -f $page/film; then + film=$(encode $page/film) + fi + if test -f $page/chem; then + chem=$(encode $page/chem) + fi + if test -f $page/note; then + note=$(encode $page/note) + fi + + cat <<-EOF + <!DOCTYPE html> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="alternate" type="application/atom+xml" href="../feed.atom"> + <title>${title}</title> + <style> + html { color: #bbb; background-color: black; font-family: monospace; } + p { text-align: center; } + figure { margin: 1em; padding-top: 0.5em; text-align: center; } + img { max-width: calc(100vw - 2.5em); max-height: calc(100vh - 2.5em); } + details { max-width: 78ch; margin: 0.5em auto; } + </style> + <h1>${title}</h1> + <p>${date:+📆 }${date:-} 📷 ${body:-}${body:+ 🔘 }${lens:-}${film:+ 🎞️ }${film:-}${chem:+ 🧪 }${chem:-}</p> + ${note:+<p>}${note:-}${note:+</p>} + EOF +} + +photo_info() { + local photo=$1 + ExposureTime= + FNumber= + FocalLength= + PhotographicSensitivity= + eval $( + identify -format '%[EXIF:*]' $photo 2>/dev/null | + grep -E 'ExposureTime|FNumber|FocalLength|PhotographicSensitivity' | + sed 's/^exif://' + ) +} + +photo_id() { + local photo=$1 + photo=${photo##*/} + photo=${photo%%.*} + echo $photo +} + +page_photo() { + local photo=$1 preview=$2 description=$3 + photo_info $photo + cat <<-EOF + <figure id="$(photo_id $photo)"> + <a href="${photo##*/}"> + EOF + if test -f $description; then + cat <<-EOF + <img src="../${preview}" alt="$(encode $description)"> + EOF + else + cat <<-EOF + <img src="../${preview}"> + EOF + fi + cat <<-EOF + </a> + <figcaption> + EOF + if test -n "${ExposureTime}"; then + cat <<-EOF + ${ExposureTime} · + ƒ/$(bc -S 1 -e ${FNumber}) · + $(bc -e ${FocalLength}) mm · + ${PhotographicSensitivity} ISO + EOF + fi + if test -f $description; then + cat <<-EOF + <details> + <summary>description</summary> + $(encode $description) + </details> + EOF + fi + cat <<-EOF + </figcaption> + </figure> + EOF +} + +index_head() { + cat <<-EOF + <!DOCTYPE html> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="alternate" type="application/atom+xml" href="feed.atom"> + <title>Photos</title> + <style> + html { color: #bbb; background-color: black; font-family: sans-serif; } + a { text-decoration: none; color: inherit; } + </style> + EOF +} + +index_page() { + local date=$1 root=${2:-} + cat <<-EOF + <h1><a href="${root}${root:+/}${date}/">$(page_title $date)</a></h1> + EOF +} + +index_photo() { + local date=$1 photo=$2 thumbnail=$3 root=${4:-} + cat <<-EOF + <a href="${root}${root:+/}${date}/#$(photo_id $photo)"> + <img src="${root}${root:+/}${thumbnail}"> + </a> + EOF +} + +Root=https://photo.causal.agency + +atom_head() { + local updated=$(date -u '+%FT%TZ') + cat <<-EOF + <?xml version="1.0" encoding="utf-8"?> + <feed xmlns="http://www.w3.org/2005/Atom"> + <title>Photos</title> + <author><name>june</name><email>june@causal.agency</email></author> + <link href="${Root}"/> + <link rel="self" href="${Root}/feed.atom"/> + <id>${Root}/</id> + <updated>${updated}</updated> + EOF +} + +atom_entry_head() { + local date=$1 + local updated=$( + date -ju -f '%s' $(stat -f '%m' static/${date}/index.html) '+%FT%TZ' + ) + cat <<-EOF + <entry> + <title>$(page_title $date)</title> + <link href="${Root}/${date}/"/> + <id>${Root}/${date}/</id> + <updated>${updated}</updated> + <content type="html"> + EOF +} + +atom_entry_tail() { + cat <<-EOF + </content> + </entry> + EOF +} + +atom_tail() { + cat <<-EOF + </feed> + EOF +} + +set -- +for entry in 20* 0*; do + mkdir -p static/${entry} + page=static/${entry}/index.html + if ! test -f $page; then + echo $page >&2 + page_head $entry >$page + for photo in ${entry}/*.[Jj][Pp][Gg]; do + preview=$(preview $photo) + if ! test -f static/${photo}; then + ln $photo static/${photo} + fi + page_photo $photo $preview ${photo%.[Jj][Pp][Gg]}.txt >>$page + done + fi + set -- $entry "$@" +done + +mkdir -p static/leader +page=static/leader/index.html +if [ leader -nt $page ]; then + echo $page >&2 + page_head leader >$page + for photo in leader/*.[Jj][Pp][Gg]; do + preview=$(preview $photo) + if ! test -f static/${photo}; then + ln $photo static/${photo} + fi + page_photo $photo $preview xxx >>$page + done +fi + +echo static/index.html >&2 +index_head >static/index.html +echo static/feed.atom >&2 +atom_head >static/feed.atom +for date; do + index_page $date >>static/index.html + atom_entry_head $date >>static/feed.atom + for photo in ${date}/*.[Jj][Pp][Gg]; do + thumbnail=$(thumbnail $photo) + index_photo $date $photo $thumbnail >>static/index.html + index_photo $date $photo $thumbnail $Root | encode >>static/feed.atom + done + atom_entry_tail >>static/feed.atom +done +atom_tail >>static/feed.atom diff --git a/www/photo.causal.agency/mastodon.sh b/www/photo.causal.agency/mastodon.sh new file mode 100644 index 00000000..1eaa1114 --- /dev/null +++ b/www/photo.causal.agency/mastodon.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -eu + +Instance=https://tilde.zone +Root=${1:-static} + +if ! test -f app.json; then + echo 'No app.json!' >&2 + exit 1 +fi +chmod 600 app.json + +if ! test -f token.json; then + client_id=$(jq -r .client_id app.json) + client_secret=$(jq -r .client_secret app.json) + echo "Please open ${Instance}/oauth/authorize?client_id=${client_id}&scope=write&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code" + printf 'Enter code: ' + read -r code + curl -Ss -X POST \ + -F 'grant_type=authorization_code' \ + -F "client_id=${client_id}" \ + -F "client_secret=${client_secret}" \ + -F 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \ + -F "code=${code}" \ + ${Instance}/oauth/token >token.json +fi +chmod 600 token.json + +access_token=$(jq -r .access_token token.json) + +if ! test -f posted.txt; then + touch posted.txt +fi + +photo=$( + find ${Root} -type f -path '*/0*/*.jpg' | + sort | comm -13 posted.txt - | head -n 1 +) +preview=${Root}/preview/${photo##*/} + +media_id=$( + curl -Ss -X POST \ + -H "Authorization: Bearer ${access_token}" \ + -F "file=@${preview}" \ + ${Instance}/api/v2/media | + jq -r .id +) + +curl -Ss -X POST \ + -H "Authorization: Bearer ${access_token}" \ + -F "media_ids[]=${media_id}" \ + ${Instance}/api/v1/statuses >/dev/null + +echo ${photo} >>posted.txt diff --git a/www/photo.causal.agency/rsync.sh b/www/photo.causal.agency/rsync.sh new file mode 100644 index 00000000..957911d2 --- /dev/null +++ b/www/photo.causal.agency/rsync.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu + +sh generate.sh +rsync -av static/ scout:/var/www/photo.causal.agency diff --git a/www/photo.causal.agency/trips.html b/www/photo.causal.agency/trips.html new file mode 100644 index 00000000..e81be6ef --- /dev/null +++ b/www/photo.causal.agency/trips.html @@ -0,0 +1,373 @@ +<!DOCTYPE html> +<title>Photo Trips</title> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> + +<style> +body { + font-family: sans-serif; + line-height: 1.5em; + max-width: 52ch; +} +input, button, select { font-size: 100%; } +form { + display: grid; + grid-template-columns: auto 1fr; + gap: 0.5em 1ch; +} +input[type="number"] { width: 5ch; } +#trip-lens { width: 100%; } +#lens-length { width: 7ch; } +#lens-aperture { width: 8ch; } +</style> + +<section id="rolls"> +<h1>Rolls</h1> +<ul> +</ul> + +<form> +<label for="roll-body">Camera:</label> +<select id="roll-body" class="body" required> +</select> +<label for="roll-film">Film:</label> +<input id="roll-film" list="films" required> +<span>Exposures:</span> +<span> +<input id="roll-36" type="radio" name="roll-exposures" value="36" checked> +<label for="roll-36">36</label> +<input id="roll-27" type="radio" name="roll-exposures" value="27"> +<label for="roll-27">27</label> +<input id="roll-24" type="radio" name="roll-exposures" value="24"> +<label for="roll-24">24</label> +<input id="roll-12" type="radio" name="roll-exposures" value="12"> +<label for="roll-12">12</label> +</span> +<button type="button" onclick="loadRoll()">Load</button> +</form> + +<datalist id="films"> +</datalist> +</section> + +<section id="trips"> +<h1>Trips</h1> + +<form> +<label for="trip-date">Date:</label> +<input id="trip-date" type="date" required> +<label for="trip-body">Camera:</label> +<select id="trip-body" class="body" onchange="setTripBody()" required> +</select> +<label for="trip-lens">Lens:</label> +<select id="trip-lens" required> +</select> +<label for="trip-film">Film:</label> +<input id="trip-film" readonly required> +<label for="trip-first">Exposures:</label> +<span> +<input id="trip-first" type="number" required min="0" max="36"> +– +<input id="trip-last" type="number" required min="0" max="36"> +</span> +<label for="trip-note">Note:</label> +<input id="trip-note"> +<button type="button" onclick="addTrip()">Record</button> +</form> + +<ul> +</ul> +</section> + +<section id="bodies"> +<h1>Cameras</h1> +<ul> +</ul> + +<form> + <label for="body-name">Name:</label> + <input id="body-name" required> + <label for="body-mount">Mount:</label> + <input id="body-mount" list="mounts" required> + <button type="button" onclick="addBody()">Add</button> +</form> + +<datalist id="mounts"> + <option>Contax/Yashica</option> + <option>M42</option> +</datalist> +</section> + +<section id="lenses"> +<h1>Lenses</h1> +<ul> +</ul> + +<form> + <label for="lens-name">Name:</label> + <input id="lens-name" required> + <label for="lens-length">Focal length:</label> + <span><input id="lens-length" required pattern="[0-9-]+">mm</span> + <label for="lens-aperture">Aperture:</label> + <span>ƒ/<input id="lens-aperture" required pattern="[0-9.-]+"></span> + <label for="lens-mount">Mount:</label> + <input id="lens-mount" list="mounts" required> + <button type="button" onclick="addLens()">Add</button> +</form> +</section> + +<script> +let bodies = JSON.parse(localStorage.getItem("bodies")) || []; +let lenses = JSON.parse(localStorage.getItem("lenses")) || []; +let rolls = JSON.parse(localStorage.getItem("rolls")) || {}; +let trips = JSON.parse(localStorage.getItem("trips")) || []; +let nextId = +localStorage.getItem("nextId") || 1; + +document.getElementById("trip-date").valueAsDate = new Date(); + +function removeButton(onclick) { + let remove = document.createElement("a"); + remove.appendChild(document.createTextNode("⛔")); + remove.onclick = onclick; + return remove; +} + +function setBodies() { + localStorage.setItem("bodies", JSON.stringify(bodies)); + let ul = document.querySelector("#bodies > ul"); + let selects = document.querySelectorAll("select.body"); + ul.innerHTML = ""; + selects.forEach(select => select.innerHTML = ""); + for (let [index, body] of bodies.entries()) { + let li = document.createElement("li"); + li.appendChild(document.createTextNode(` + ${body.name} (${body.mount}) + `)); + li.appendChild(removeButton(removeBody.bind(null, index))); + ul.appendChild(li); + for (let select of selects) { + let option = document.createElement("option"); + option.appendChild(document.createTextNode(body.name)); + select.appendChild(option); + } + } + if (trips.length) { + selects.forEach(select => select.value = trips[trips.length-1].body); + } +} +setBodies(); + +function endashify(str) { + return str.replaceAll("-", "–"); +} +function lensString(lens) { + return ` + ${lens.name} + ${endashify(lens.focalLength)}mm + ƒ/${endashify(lens.aperture)} + `.replace(/\s+/g, " ").trim(); +} + +function setLenses() { + localStorage.setItem("lenses", JSON.stringify(lenses)); + let ul = document.querySelector("#lenses > ul"); + ul.innerHTML = ""; + for (let [index, lens] of lenses.entries()) { + let li = document.createElement("li"); + li.appendChild(document.createTextNode(` + ${lensString(lens)} (${lens.mount}) + `)); + li.appendChild(removeButton(removeLens.bind(null, index))); + ul.appendChild(li); + } +} +setLenses(); + +function setRolls() { + localStorage.setItem("rolls", JSON.stringify(rolls)); + let ul = document.querySelector("#rolls > ul"); + ul.innerHTML = ""; + for (body in rolls) { + let roll = rolls[body]; + let li = document.createElement("li"); + li.appendChild(document.createTextNode(` + ${body}: ${roll.film} (${roll.used}/${roll.exposures}) + `)); + if (roll.used == roll.exposures) { + li.style.textDecoration = "line-through"; + } + ul.appendChild(li); + } +} +setRolls(); + +function setTrips() { + localStorage.setItem("trips", JSON.stringify(trips)); + let ul = document.querySelector("#trips > ul"); + ul.innerHTML = ""; + let tripsByRoll = Object.groupBy(trips, trip => trip.rollId); + for (let rollId = nextId - 1; rollId > 0; --rollId) { + let rollTrips = tripsByRoll[rollId]; + if (!rollTrips) continue; + let rollLi = document.createElement("li"); + let rollB = document.createElement("b"); + rollB.appendChild(document.createTextNode(rollTrips[0].film)); + rollLi.appendChild(rollB); + rollLi.appendChild(document.createTextNode(` (${rollTrips[0].body})`)); + let body = bodies.find(body => body.name == rollTrips[0].body); + let rollUl = document.createElement("ul"); + for (let [index, trip] of rollTrips.entries()) { + let li = document.createElement("li"); + let b = document.createElement("b"); + b.appendChild(document.createTextNode(trip.date)); + li.appendChild(b); + li.appendChild(document.createTextNode( + `: ${trip.firstExposure}–${trip.lastExposure}` + )); + if ( + (!body || body.mount != body.name) && + (!index || trip.lens != rollTrips[index-1].lens) + ) { + li.appendChild(document.createElement("br")); + li.appendChild(document.createTextNode(trip.lens)); + } + if (trip.note) { + li.appendChild(document.createElement("br")); + li.appendChild(document.createTextNode(`“${trip.note}”`)); + } + rollUl.appendChild(li); + } + rollLi.appendChild(rollUl); + ul.appendChild(rollLi); + } +} +setTrips(); + +function setTripBody() { + let bodyName = document.getElementById("trip-body").value; + let body = bodies.find(body => body.name == bodyName); + let select = document.getElementById("trip-lens"); + select.innerHTML = ""; + for (lens of lenses.filter(lens => lens.mount == body.mount)) { + let option = document.createElement("option"); + option.appendChild(document.createTextNode(lensString(lens))); + select.appendChild(option); + } + let lastTrip = trips.findLast(trip => trip.body == bodyName); + if (lastTrip) { + select.value = lastTrip.lens; + } + let roll = rolls[body.name]; + if (roll) { + document.getElementById("trip-film").value = roll.film; + let next = (roll.used > 0 ? roll.used + 1 : roll.used); + document.getElementById("trip-first").value = next; + document.getElementById("trip-last").value = next; + } else { + document.getElementById("trip-film").value = ""; + document.getElementById("trip-first").value = ""; + document.getElementById("trip-last").value = ""; + } +} +setTripBody(); + +function clearForm(form) { + let inputs = form.querySelectorAll("input"); + for (input of inputs) { + if (input.type == "radio") continue; + input.value = null; + } +} + +function addBody() { + let form = document.querySelector("#bodies > form"); + if (!form.checkValidity()) return; + let name = document.getElementById("body-name").value; + let mount = document.getElementById("body-mount").value; + bodies.push({ name, mount }); + setBodies(); + clearForm(form); +} + +function removeBody(index) { + let body = bodies[index]; + if (!confirm(`Are you sure you want to remove ${body.name}?`)) { + return; + } + bodies.splice(index, 1); + delete rolls[body.name]; + setBodies(); + setRolls(); +} + +function addLens() { + let form = document.querySelector("#lenses > form"); + if (!form.checkValidity()) return; + let name = document.getElementById("lens-name").value; + let focalLength = document.getElementById("lens-length").value; + let aperture = document.getElementById("lens-aperture").value; + let mount = document.getElementById("lens-mount").value; + lenses.push({ name, focalLength, aperture, mount }); + setLenses(); + clearForm(form); +} + +function removeLens(index) { + let lens = lenses[index]; + if (!confirm(`Are you sure you want to remove ${lensString(lens)}?`)) { + return; + } + lenses.splice(index, 1); + setLenses(); + setTripBody(); +} + +function loadRoll() { + let form = document.querySelector("#rolls > form"); + if (!form.checkValidity()) return; + let body = document.getElementById("roll-body").value; + let film = document.getElementById("roll-film").value; + let exposures = +new FormData(form).get("roll-exposures"); + rolls[body] = { id: nextId++, film, exposures, used: 0 }; + localStorage.setItem("nextId", nextId); + setRolls(); + clearForm(form); + setTripBody(); +} + +function addTrip() { + let form = document.querySelector("#trips > form"); + if (!form.checkValidity()) return; + let date = document.getElementById("trip-date").value; + let body = document.getElementById("trip-body").value; + let lens = document.getElementById("trip-lens").value; + let film = document.getElementById("trip-film").value; + let firstExposure = +document.getElementById("trip-first").value; + let lastExposure = +document.getElementById("trip-last").value; + let note = document.getElementById("trip-note").value; + let trip = { + date, body, lens, film, rollId: rolls[body].id, + firstExposure, lastExposure, note + }; + trips.push(trip); + rolls[body].used = lastExposure; + setTrips(); + setRolls(); + document.getElementById("trip-date").valueAsDate = new Date(); + document.getElementById("trip-note").value = ""; + setTripBody(); +} + +function setFilms() { + let datalist = document.getElementById("films"); + datalist.innerHTML = ""; + let films = new Set(trips.reverse().map(trip => trip.film)); + for (let film of films.values().take(20)) { + let option = document.createElement("option"); + option.innerText = film; + datalist.appendChild(option); + } +} +setFilms(); + +</script> diff --git a/www/text.causal.agency/043-little-blessings.7 b/www/text.causal.agency/043-little-blessings.7 new file mode 100644 index 00000000..957c6289 --- /dev/null +++ b/www/text.causal.agency/043-little-blessings.7 @@ -0,0 +1,78 @@ +.Dd March 24, 2024 +.Dt LITTLE-BLESSINGS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm little blessings +.Nd life's +. +.Sh DESCRIPTION +today I went out to go around. +run some errands and do some shopping. +along the way I was given +several of life's little blessings. +. +.Pp +while walking on ste-cath +between berri and complexe desjardins, +there was a somewhat disheveled man +walking in the same direction and singing. +he had a beautiful voice. +he was singing a sad song in french, +and he sung it well and enunciated every word. +. +.Pp +in the mcdonald's at complexe desjardins, +while waiting for my order, +there were what appeared to be +a teenager and her younger brother, +who must have been +looking at the display of +current happy meal toys. +the teenager was playing smash or pass, +to the amusement of the younger one. +they got ice cream +and ate it across the room from me downstairs. +. +.Pp +later, +taking the 24 home from atwater +carrying my new vacuum cleaner, +the bus got lost. +I think the driver missed the stop +and tried to compensate +by turning north onto peel +and stopping there. +but then he had to keep going up peel. +he turned right onto docteur-penfield, +which just brings you further up the mountain. +when it met des pins, +he turned left and pulled over, +asking for guidance over the radio. +we got moving again, +back towards peel. +that's how I ended up +on a 24 +.Dq sherbrooke +east, +facing west on des pins. +it was actually quite scenic. +and amusing. +I was in no rush. +. +.Pp +after getting back onto sherbrooke, +the bus had to take another detour, +this one planned. +so my ride on the 24, +which normally only drives on sherbrooke, +ended up going on peel, +docteur-penfield, +des pins, +de bleury, +ren\('e-l\('evesque +and saint-laurent. +it was a very exciting bus trip. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/044-film-review.7 b/www/text.causal.agency/044-film-review.7 new file mode 100644 index 00000000..8e8feca8 --- /dev/null +++ b/www/text.causal.agency/044-film-review.7 @@ -0,0 +1,208 @@ +.Dd October 12, 2024 +.Dt FILM-REVIEW 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm film review +.Nd stock, that is +. +.Sh DESCRIPTION +this summer I got really into analog photography. +I've tried out a bunch of different film stocks, +thanks to the local photo lab, +and I've +.Em developed +(pun intended) +some preferences. +here they are. +. +.Sh BLACK & WHITE +.Bl -enum +.It +Ilford FP4+ (ISO 125, United Kingdom) +.Pp +definitely my favourite b&w film. +I love the fine grain and contrast +with good shadow detail. +really just exactly what I want +out of a b&w film I think. +ISO 125 is quite generous for what it is, +but it's still best suited for sunny days. +.Pp +sample: +.Lk https://photo.causal.agency/2024-09-29/ +. +.It +Fomapan Creative (ISO 200, Czech Republic) +.Pp +I've only shot one roll of this so far, +but I really like the balance it strikes +between fine grain and high speed. +it just seems like a good go-to film +for what I like to do with b&w photography, +given the extra flexibility over FP4. +.Pp +sample: +.Lk https://photo.causal.agency/2024-09-14/ +. +.It +Ferrania P30 (ISO 80, Italy) +.Pp +another that I've only shot one roll of, +but I really like the results. +obviously it swings in the other direction +in terms of film sensitivity, +but more importantly +it has a distinctive look. +that's harder in b&w than it is in colour! +.Pp +sample: +.Lk https://photo.causal.agency/2024-10-05/ +. +.It +Ilford Delta 100 (United Kingdom) +.Pp +as far as I'm concerned this is just more expensive FP4. +it certainly looks good +but I'd rather save the couple extra dollars. +.Pp +sample: +.Lk https://photo.causal.agency/2024-09-22/ +. +.It +Ilford HP5+ (ISO 400, United Kingdom) +.Pp +it's like, ok. +more grainy than I'd like, +but that's to be expected of high speed. +my real problem with it +is the lack of contrast. +maybe I should only be shooting it pushed, +but I don't want to pay the extra fee +to have my local photo lab do that. +.Pp +sample: +.Lk https://photo.causal.agency/2024-09-07/ +. +.It +Fomapan Action (ISO 400, Czech Republic) +.Pp +I almost wonder if something went wrong +either in shooting or processing +the one roll of this I shot. +everything came out very low contrast. +.Pp +sample: +I didn't end up uploading any. +.El +. +.Sh COLOUR +.Bl -enum +.It +Shanghai Color (ISO 400, China) +.Pp +I love the desaturated colours +and the grain on this. +I guess I like fine grain in b&w +and coarse grain in colour. +I think this is well suited +to the subjects I like to photograph, +like old brick buildings, +but it also does nature quite nicely. +I think this will be a good one to capture fall with. +.Pp +ok so this is almost certainly repackaged +Wolfen Color NC500 +(made in germany). +but the thing is, +shanghai does a better job packaging it. +they use real metal cassettes +and add film edge markings. +and their box design is way nicer. +and on top of THAT, +my local photo lab +sells it for cheaper than NC500. +.Pp +sample: +.Lk https://photo.causal.agency/2024-09-22/ +. +.It +Harman Phoenix (ISO 200, United Kingdom) +.Pp +phoenix is a fun film! +the lack of yellow filter +and anti-halation layer +can produce some neat effects. +in the right conditions +it also sometimes looks exceedingly normal. +but it also sometimes just... +doesn't work well. +underexposed areas can get really bad. +apparently it can be better to shoot it at ISO 100. +I should give that a try, +or just be more diligent with +how I'm metering. +.Pp +sample: +.Lk https://photo.causal.agency/2024-08-10/ +. +.It +CineStill 800T (USA?) +.Pp +I can't really say much about this yet. +I don't have much experience with indoor photography. +the lack of anti-halation layer +does tend to make lights look sinister as hell, though. +I'll probably shoot +one of the cheaper repackagings +of ISO 800 cinema film +in the future. +.Pp +sample: +.Lk https://photo.causal.agency/2024-10-06/ +. +.It +Film Washi +.Dq X +(ISO 100, France) +.Pp +this is mostly pretty normal film +without a yellow filter. +not much to say about it. +I'd be more interested to try washi's +other repackaged b&w technical films, +but I think I missed them being in stock here. +.Pp +sample: +.Lk https://photo.causal.agency/2024-08-23/ +. +.It +Fujifilm 400 (Japan?) +.Pp +I shot my two first ever rolls on this. +they were surprisingly good! +the scans did the film dirty though. +that was before I found the good photo lab. +. +.It +Kodak Gold (ISO 200, USA) +.Pp +ok so this is a cheap film, right? +but it's too damn good. +fine grain, accurate colour. +it looks like digital to me, +and that's not what I want. +even fuji has a little more character to it than this. +puts me off kodak. +.Pp +sample: +.Lk https://photo.causal.agency/2024-07-01/ +.El +. +.Sh AUTHORS +.An Juniper Aq Mt june@causal.agency +. +.Pp +if you have suggestions +for film stocks I should try, +send me an email. diff --git a/www/text.causal.agency/045-time-2025.7 b/www/text.causal.agency/045-time-2025.7 new file mode 100644 index 00000000..80fa428b --- /dev/null +++ b/www/text.causal.agency/045-time-2025.7 @@ -0,0 +1,131 @@ +.Dd August 18, 2025 +.Dt TIME-2025 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm time +.Nd 2025 +. +.Sh DESCRIPTION +time has passed. +this blog still exists. +I don't feel bad about not writing. +I haven't had anything I want to say in this form. +. +.Pp +I go back and read my own posts here fairly often. +most recently I looked up how I calculated +.Dq unique lines of code +all the way back in 2018. +I read my own post on apologies a lot. +I'm glad I wrote that down. +. +.Pp +other people, too, +still refer to my old posts. +.Dq operating systems +has been repeatedly referenced +by a friend for years. +I still occasionally get emails in reply to +.Dq inability . +I try to wish those people well. +recently I got asked about +.Dq names . +it was interesting trying to explain +an idea I was playing with four years ago. +. +.Pp +some time in the last year +I had started writing +a semi-ficticious history of my life. +I never finished it +and I don't think I'm interested in the idea anymore. +. +.Pp +I used to write posts about +books I'd read +or albums I'd listened to +in the year. +since 2022 I haven't really listened to new music. +I put a lot of songs I really like +in a big playlist called +.Dq more tunes +and I put that on shuffle +whenever I want music. +I don't know why +music doesn't play the same role +in my life anymore. +. +.Pp +I've read books since 2021, +though not at a very high rate. +I still love Becky Chambers +and Alix E. Harrow. +I still need to finish +the Andrea Stewart trilogy I started. +I read an old collection +of short erotic fiction +by trans authors. +that was really good. +I just finished a novel draft by a friend. +. +.Pp +I've shot a lot more film +since my last post. +I was wrong about a lot. +I don't feel like writing more about it. +. +.Pp +in october of 2022 +I started a relationship +that lasted two years. +we moved in together in 2023. +by early 2024 things were going badly. +in february I posted +.Dq comfort music . +I think someone emailed me +because they didn't think I was doing well. +I wasn't. +from summer 2024 +to summer 2025 +was the worst year I've had. +in october someone I barely knew at the time +messaged me to ask if I was ok. +I think I was sitting in a tim hortons +after getting a blood test. +I felt bad all the time +and I didn't know what to do. +. +.Pp +we broke up 2 weeks after 2 years together. +everything got worse. +it wasn't a clean breakup. +I was still clinging onto +the familiar pieces of the relationship +that had used to make me happy. +they didn't anymore. +it was torture. +I lived in agony for months. +I think I lost my mind a little, +trying to handle things I couldn't. +. +.Pp +in march I went no-contact. +I started going to therapy. +I went on a weekend trip to ottawa by myself. +I looked at art in the national gallery. +I started trying to become myself again. +. +.Pp +in june I invited people out +for my 30th birthday. +I was terrified, +convinced up until the last second +that no one was going to come. +but they did. +and since then I've been doing better. +I think I've picked up where I left off, +at some point in the last few years. +. +.Sh AUTHORS +.Nm june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile index 44ce676c..c9e86ab2 100644 --- a/www/text.causal.agency/Makefile +++ b/www/text.causal.agency/Makefile @@ -42,6 +42,9 @@ TXTS += 039-apologies.txt TXTS += 040-sound-memory.txt TXTS += 041-albums-2022.txt TXTS += 042-comfort-music.txt +TXTS += 043-little-blessings.txt +TXTS += 044-film-review.txt +TXTS += 045-time-2025.txt all: colb ${TXTS} diff --git a/www/they.causal.agency/Makefile b/www/they.causal.agency/Makefile new file mode 100644 index 00000000..81f47ea9 --- /dev/null +++ b/www/they.causal.agency/Makefile @@ -0,0 +1,4 @@ +ROOT = /var/www/man + +install: post-update.sh + install post-update.sh ${ROOT}/post-update diff --git a/www/they.causal.agency/post-update.sh b/www/they.causal.agency/post-update.sh new file mode 100644 index 00000000..db2d5936 --- /dev/null +++ b/www/they.causal.agency/post-update.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -eu + +do_tree() { + tree=$1 + manpath=$2 + echo "Copying manuals for ${tree}..." + git ls-tree $tree | while read -r mode type hash name; do + if [ $type != blob ]; then + continue + fi + case "$name" in + (README.7) + continue + ;; + (*.[1-9]) + section=${name##*.} + mkdir -p /var/www/man/${manpath}/man${section} + git cat-file $type $hash \ + >/var/www/man/${manpath}/man${section}/${name} + ;; + esac + done + if test -d /var/www/man/${manpath}; then + makewhatis /var/www/man/${manpath} + if ! fgrep -q ${manpath} /var/www/man/manpath.conf; then + echo $manpath >>/var/www/man/manpath.conf + sort -o /var/www/man/manpath.conf /var/www/man/manpath.conf + fi + fi +} + +do_tree HEAD HEAD + +repo=${PWD##*/} +for tag in $(git tag); do + manpath=${repo%.git}-${tag} + if ! test -d /var/www/man/${manpath}; then + do_tree $tag $manpath + fi +done |