A really lovely talk by graphic designer Chip Kidd about the thought process behind some of the book covers he’s designed, like Jurassic Park and IQ84. He’s such a character! I loved this.
She is five. She does not speak in sentences yet, but she knows how to answer a joke with a smirk. She organizes her markers by color, then chaos, then color again. She plays baseball without rules, which is probably the right way to play it. She hums when she’s thinking. She hums a lot.
Holy shit, the level of second-hand cringe I just got reading this is off the charts.
“I’ll go down this thread with [Chat]GPT or Grok and I’ll start to get to the edge of what’s known in quantum physics and then I’m doing the equivalent of vibe coding, except it’s vibe physics,” Kalanick explained. “And we’re approaching what’s known. And I’m trying to poke and see if there’s breakthroughs to be had. And I’ve gotten pretty damn close to some interesting breakthroughs just doing that.”
Look, it’s dhh and we all have our thoughts about him and his political beliefs but when he sticks in his lane of only doing code/technical stuff, he can produce some really good stuff. His Arch/Hyprland setup here is lovely.
I’m trying to switch over to the Affinity design tools (designer, photo). The best way I’ve found to learn a new thing is to give yourself a specific project so you’re forced to poke at the uncomfortable bits. So I decided for this, I’d mock up some Topps trading cards for some rad movies that deserved some trading cards.
Whoa! Teenage Engineering have made an electric scooter and it’s shockingly cheap? (For TE, I mean. The base 30mAh model scooter is only slightly more expensive than the collapsible table they brought out a couple of years ago).
One of my goals for the second half of 2025 is to reduce the amount of duplication in my life. I have a half a dozen read/watch/listen-it-later services. My tasks are strewn across four different productivity systems. I’m too old for this kind of nonsense, and I’d like to get this down into something more manageable.
A nice easy starting point for decluttering: the links system on this site. I migrated from Pinboard to Raindrop to a Linkding instance at bookmarks.johnke.me. That’s where I store my bookmarks. But what about if I find something I want to share on this site? Well for that, I run a bash script (bin/newlink.sh) passing the URL and it will generate a Hugo post for that link. Last year, with version v0.126.0, Hugo introduced a new feature: content adapters. This allows Hugo, a static site generator, to dynamically generate content from another source. In their examples, they just use a dict but you can also use the output of an API. An API like the one provided by Linkding?
Setting this up was shockingly easy!
First, I put some Linkding config in Hugo’s top-level config.yaml:
The ?q==%23johnke.me means that any bookmark I tag with johnke.me will be picked up by the content adapter.
Then I put a _content.gotmpl at the top-level of the section where I want my content created. So in my case, I have a content/link/_content.gotmpl that looks like this:
{{/* Fetch bookmarks from Linkding */}}
{{/* Set using HUGO_PARAMS_LINKDING_URL, HUGO_PARAMS_LINKDING_TOKEN or in Hugo config */}}
{{/* Full endpoint URL with any filters, e.g. "https://bookmarks.johnke.me/api/bookmarks/?q=%23dorkus" */}}
{{ $URL:= .Site.Params.linkding.url }}
{{ $TOKEN:= .Site.Params.linkding.token }}
{{/* Get remote data. */}}
{{ $data:=dict }}
{{ $headers:=dict"Authorization" (print"Token "$TOKEN) }}
{{ $opts:= (dict"headers"$headers) }}
{{ withtry (resources.GetRemote$URL$opts) }}
{{ with .Err }}
{{ errorf"Unable to get remote resource %s: %s"$URL . }}
{{ elsewith .Value }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf"Unable to get remote resource %s"$URL }}
{{ end }}
{{/* Add pages and page resources. */}}
{{ range$data.results }}
{{/* Skip entries with empty titles */}}
{{ if .title }}
{{/* Add page. */}}
{{ $content:=dict"mediaType""text/markdown""value" .notes }}
}}
{{ $dates:=dict"date" (time.AsTime .date_added)
"lastmod" (time.AsTime .date_modified)
}}
{{/* For tags, remove `johnke.me` */}}
{{ $tags:=slice }}
{{ range .tag_names }}
{{ ifne . "johnke.me" }}
{{/* Dashes to spaces */}}
{{ $tag:=replace . "-"" " }}
{{ $tags = $tags | append$tag }}
{{ end }}
{{ end }}
{{/* I use the 'link' parameter to point to the URL */}}
{{ $params:=dict"link" .url"tags"$tags }}
{{/* Create a safe filename from the title */}}
{{ $safePath:= .title | urlize }}
{{ ifnot$safePath }}
{{ $safePath = printf"bookmark-%d" (time.AsTime .date_added).Unix }}
{{ end }}
{{ $page:=dict"path"$safePath"kind""page""title" .title"dates"$dates"params"$params"content"$content }}
{{ $.AddPage$page }}
{{ else }}
{{ warnf"Skipping bookmark with empty title: %s" .url }}
{{ end }}
{{ end }}
Finally, since this whole thing is driven by github actions, I added a schedule to my action definition so twice a day, it will regenerate the site with any new links I’ve added:
on:
...schedule:
# Runs every day at 2:00 AM and 2:00 PM UTC - cron: '0 2,14 * * *'...
And that’s it! I have links coming directly in from Linkding and I have one canonical source of truth for all my bookmarks and one less point of redundancy!
The graph showing the CEO’s wages in relation to those of the average worker is eye-opening. At these numbers, things get hard to visualise but this does the best job I’ve seen.