From fd7fd5728d17932a861444a830502046d3b86a54 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Wed, 7 Oct 2026 20:31:54 -0500 Subject: [PATCH] capsite/potluck: a weekly zine of issues, not a blog - issues collection (ISO-week slug, week, dek, rich-text body); home, /issues, and /issues/ pages; seed/issues.json holds three sketched issues (W38-W40) from public diigo bookmarks, editorial lines marked (draft); issues-sketch.md is the dry run behind them - microformats: h-feed of h-entry lists; an issue is an h-entry (p-name, dt-published, p-summary, e-content); off-site links render as h-cite, /tag/ links as rel-tag p-category (CiteLink.astro) - diigo tags: shown on each bookmark and attached to issues via the tag taxonomy (now posts + issues); /tag/ lists issues - demo blog content gone: posts dropped from the seed, home page lists issues, site title/tagline, footer About widget, and About page rewritten; nav is Home / About / Issues; search and RSS cover issues - .column container gives potluck's pages the header's margins Generated-by: Claude Opus 5.5 --- potluck/emdash-env.d.ts | 17 + potluck/issues-sketch.md | 139 ++ potluck/seed/issues.json | 1995 +++++++++++++++++++++++++ potluck/seed/seed.json | 681 +-------- potluck/src/components/CiteLink.astro | 43 + potluck/src/layouts/Base.astro | 2 +- potluck/src/pages/index.astro | 486 +----- potluck/src/pages/issues/[slug].astro | 59 + potluck/src/pages/issues/index.astro | 45 + potluck/src/pages/rss.xml.ts | 20 +- potluck/src/pages/tag/[slug].astro | 143 +- potluck/src/styles/theme.css | 8 + potluck/src/utils/site-identity.ts | 4 +- 13 files changed, 2444 insertions(+), 1198 deletions(-) create mode 100644 potluck/issues-sketch.md create mode 100644 potluck/seed/issues.json create mode 100644 potluck/src/components/CiteLink.astro create mode 100644 potluck/src/pages/issues/[slug].astro create mode 100644 potluck/src/pages/issues/index.astro diff --git a/potluck/emdash-env.d.ts b/potluck/emdash-env.d.ts index bb22a62..1a59414 100644 --- a/potluck/emdash-env.d.ts +++ b/potluck/emdash-env.d.ts @@ -5,6 +5,22 @@ import type { BylineSummary, ContentBylineCredit, TaxonomyTerm, PortableTextBlock } from "emdash"; +export interface Issue { + id: string; + slug: string | null; + status: string; + title: string; + week: string; + dek?: string; + body?: PortableTextBlock[]; + createdAt: Date; + updatedAt: Date; + publishedAt: Date | null; + byline?: BylineSummary | null; + bylines?: ContentBylineCredit[]; + terms?: Record; +} + export interface Page { id: string; slug: string | null; @@ -37,6 +53,7 @@ export interface Post { declare module "emdash" { interface EmDashCollections { + issues: Issue; pages: Page; posts: Post; } diff --git a/potluck/issues-sketch.md b/potluck/issues-sketch.md new file mode 100644 index 0000000..a7100c8 --- /dev/null +++ b/potluck/issues-sketch.md @@ -0,0 +1,139 @@ +# potluck: a few weeks of issues, sketched + +A dry run of the zine shape (an `issues` collection, ISO-week slugs, a +`blocks` field of typed pieces) against real material: public +bookmarks from the diigo public cache, by `created_at`, UTC weeks. +Descriptions in quotes are Dan's own diigo descriptions. Everything +marked *draft* is a guess at an editorial line, for Dan to rewrite or +cut. Sources we don't read yet (social.coop, photos, GitHub, Zotero) are +marked as empty plates, not invented. + +Block types used: `note` (rich text), `bookmark` (url, title, comment), +`cluster` (a heading over several bookmarks; maybe just a `note` + +bookmarks), `plate` (an empty slot for a source not wired yet). + +--- + +## 2026-W38 (Sep 14–20): moving into KDE + +**note** *draft:* The workstation moved from MATE to KDE Plasma this +week, and the bookmarks show it. + +**cluster: the migration** +- **bookmark** [Plasma / PowerDevil · GitLab](https://invent.kde.org/plasma/powerdevil): + "wake up, bldbox! oh... MATE was configured to only lock screen, not + suspend, on idle. nice: sovereign kde.org domain vs github" +- **bookmark** [KDE Identity](https://identity.kde.org): "in addition to + my Ubuntu account? hm." +- **bookmark** [Plasma 6.7](https://kde.org/announcements/plasma/6/6.7.0/) +- **bookmark** [Making Dolphin's compact view denser (r/kde)](https://www.reddit.com/r/kde/comments/16ylic8/comment/k39subf/) +- **bookmark** [polkit Reference Manual](https://polkit.pages.freedesktop.org/polkit/polkit.8.html), + [Desktop Services (GNU Guix manual)](https://guix.gnu.org/manual/1.5.0/en/html_node/Desktop-Services.html) + +**cluster: AI and the web** +- **bookmark** ['Doom Loop': OpenAI and Microsoft admit LLMs are destroying the web (404 Media)](https://www.404media.co/doom-loop-openai-and-microsoft-admits-llms-are-destroying-the-web-and-built-on-theft/) +- **bookmark** [OpenAI agents involved in an attack on RubyGems (The Guardian)](https://www.theguardian.com/technology/2026/sep/11/openai-agents-rubygems-malicious-packages) + (tagged `invasive-ai`) +- **bookmark** [The Agent Development Lifecycle on Cloudflare](https://blog.cloudflare.com/agent-development-lifecycle/), + [Artifacts: versioned storage that speaks Git](https://blog.cloudflare.com/artifacts-git-for-agents-beta/) + +**bookmark** [Zotero 10](https://www.zotero.org/blog/zotero-10/) +*draft:* (bib sync, #195, gets a new target.) + +**bookmark** [The Message from Deep Space on Steam](https://store.steampowered.com/app/4080030/The_Message_from_Deep_Space/): +"Kyle invited me to play. Fun!" + +**plate** social.coop posts this week · **plate** photos + +*Left on the table:* the Costco motor-oil story, Tampermonkey, the +inotify sysctl, the KDE screenshot how-to, Geo Tracker, +the Gemini exporter. + +--- + +## 2026-W39 (Sep 21–27): repair, reuse, and TV night + +**note** *draft:* Kansas City has a whole circular-economy scene I +hadn't been paying attention to. + +**cluster: fix it, don't toss it** +- **bookmark** [Repair Cafes | Re.Use.Full](https://reusefull.org/repair-cafes/) +- **bookmark** [Un-Dumpster Day | Re.Use.Full](https://reusefull.org/un-dumpster-day/) +- **bookmark** [Amazon Basics wireless mouse](https://www.amazon.com/dp/B005EJH6Z4): + "middle mouse button failed" *draft:* (a repair café candidate?) + +**bookmark** [MATE: no 26.04 release; what is the future of Ubuntu MATE?](https://discourse.ubuntu.com/t/mate-no-26-04-release-how-does-that-affect-you-and-what-is-the-future-of-ubuntu-mate/83877) +*draft:* (So last week's move was timely.) + +**bookmark** [We taught sqlc to invalidate our caches (exe.dev)](https://blog.exe.dev/we-taught-sqlc-to-invalidate-our-caches) +*draft:* (Relevant: potluck's own diigo cache can't invalidate yet.) + +**cluster: family screens** +- **bookmark** [Critical Role](https://critrole.com) +- **bookmark** [Chad Powers (Wikipedia)](https://en.wikipedia.org/wiki/Chad_Powers): + "spotter: LQ - see eli manning viral" + +**plate** social.coop · **plate** photos + +*Left on the table:* the Spotify playlist API reference, Evergy HVAC +discounts. + +--- + +## 2026-W40 (Sep 28–Oct 4): co-ops, protocols, and where to host + +**note** *draft:* A week of reading about cooperatives and the +protocols they might run on, and about owning your own bookmarks, +which is what this zine is made of. + +**cluster: cooperative economics** +- **bookmark** [co/core: an AI cooperative](https://cocore.dev) and + [How the co/core economy is shaped, and why](https://cocore.dev/blog/token-market-design) +- **bookmark** [Designing for the Economy of Involvement in Co-ops (MEDLab, CU Boulder)](https://www.colorado.edu/lab/medlab/2023/03/27/designing-economy-involvement-co-ops-0) +- **bookmark** [Transkribus](https://www.transkribus.org): run by a + cooperative + +**cluster: own your bookmarks, AT Protocol edition** +- **bookmark** [kipclip: own your bookmarks, a Pinboard alternative on AT Protocol](https://kipclip.com): + "does it do annotations / highlights?" +- **bookmark** [Semble: make sense of the web, together](https://semble.so) +- **bookmark** [Graze: custom feed builder for Bluesky](https://www.graze.social) +- **bookmark** [AT Protocol account recovery](https://atproto.com/guides/account-recovery), + [First steps of the PLC organization](https://blog.plcred.org/3mwlphq42d227) + +**cluster: where to host** +- **bookmark** [Hetzner Storage Box](https://www.hetzner.com/storage/storage-box/), + [Infomaniak (Wikipedia)](https://en.wikipedia.org/wiki/Infomaniak), + [Cloudron pricing](https://www.cloudron.io/pricing.html) +- **bookmark** [NearlyFreeSpeech.NET turns 20](https://blog.nearlyfreespeech.net/2022/01/18/nearlyfreespeech-net-turns-20-today/) + and [automatic TLS](https://blog.nearlyfreespeech.net/2024/05/10/automatic-tls-is-now-a-thing/) + +**bookmark** [Material Flow Analysis of discarded materials, KC metro](https://regeneration.us/material-flow-analysis-of-discarded-materials-in-the-kansas-city-metropolitan-area/) +*draft:* (continuing W39's thread) + +**plate** social.coop · **plate** GitHub activity (potluck itself +started this week) + +*Left on the table:* AgentsView, freeq, Rivet self-host, the R5contrib +FTP index ("XcRichText-1.5.tar.Z"), the Sygil draft, the AT Protocol +Wikipedia page. + +--- + +## What the sketch says about the schema + +- **Clusters happen every week.** A heading over 2–5 bookmarks is the + natural unit, more than the lone bookmark. So either a `cluster` + block type (heading + list of bookmarks, via a repeater), or a + `note` block used as a heading. A repeater inside a block is + supported; references are not, so bookmarks are copies. +- **Threads span weeks** (KDE: W38 → W39; circular economy: W39 → + W40). A light "continuing from W39" link is enough; no new type. +- **"Left on the table" matters**: the editorial step is mostly + *leaving out*. The draft generator should propose everything for + the week and let Dan cut, not pick for him. +- **Duplicates** (Graze twice, utm-tagged URLs) need a URL + normalization pass before drafting. +- **Annotations are the best commentary.** Policy (goal 1g in the + capsite checklist): notes on public items are public too, so they're + fair to quote; this sketch didn't use them yet. diff --git a/potluck/seed/issues.json b/potluck/seed/issues.json new file mode 100644 index 0000000..b322c1f --- /dev/null +++ b/potluck/seed/issues.json @@ -0,0 +1,1995 @@ +{ + "$schema": "https://emdashcms.com/seed.schema.json", + "version": "1", + "meta": { + "name": "potluck issues", + "description": "The zine shape: weekly issues of typed pieces", + "author": "potluck" + }, + "collections": [ + { + "slug": "issues", + "label": "Issues", + "labelSingular": "Issue", + "urlPattern": "/issues/{slug}", + "supports": [ + "drafts", + "revisions", + "preview", + "search", + "seo" + ], + "fields": [ + { + "slug": "title", + "label": "Title", + "type": "string", + "required": true, + "searchable": true + }, + { + "slug": "week", + "label": "ISO week", + "type": "string", + "required": true + }, + { + "slug": "dek", + "label": "Dek", + "type": "text", + "searchable": true + }, + { + "slug": "body", + "label": "Body", + "type": "portableText", + "searchable": true + } + ] + } + ], + "content": { + "issues": [ + { + "id": "issue-2026-w38", + "slug": "2026-w38", + "status": "published", + "data": { + "title": "Moving into KDE", + "week": "2026-W38", + "dek": "Sep 14–20: the Plasma migration, AI and the web, Zotero 10. (sketch)", + "body": [ + { + "_type": "block", + "_key": "k2", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k1", + "text": "The workstation moved from MATE to KDE Plasma this week, and the bookmarks show it. (draft)", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k4", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k3", + "text": "The migration", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k8", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k5", + "href": "https://invent.kde.org/plasma/powerdevil" + }, + { + "_type": "link", + "_key": "k5-linux", + "href": "/tag/linux" + }, + { + "_type": "link", + "_key": "k5-desktop", + "href": "/tag/desktop" + }, + { + "_type": "link", + "_key": "k5-migration", + "href": "/tag/migration" + }, + { + "_type": "link", + "_key": "k5-learning", + "href": "/tag/learning" + }, + { + "_type": "link", + "_key": "k5-sysadmin", + "href": "/tag/sysadmin" + } + ], + "children": [ + { + "_type": "span", + "_key": "k6", + "text": "Plasma / PowerDevil · GitLab", + "marks": [ + "k5" + ] + }, + { + "_type": "span", + "_key": "k7", + "text": ": wake up, bldbox! oh... MATE was configured to only lock screen, not suspend, on idle. nice: sovereign kde.org domain vs github", + "marks": [] + }, + { + "_type": "span", + "_key": "k5-linux-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k5-linux-t", + "text": "linux", + "marks": [ + "k5-linux" + ] + }, + { + "_type": "span", + "_key": "k5-desktop-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k5-desktop-t", + "text": "desktop", + "marks": [ + "k5-desktop" + ] + }, + { + "_type": "span", + "_key": "k5-migration-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k5-migration-t", + "text": "migration", + "marks": [ + "k5-migration" + ] + }, + { + "_type": "span", + "_key": "k5-learning-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k5-learning-t", + "text": "learning", + "marks": [ + "k5-learning" + ] + }, + { + "_type": "span", + "_key": "k5-sysadmin-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k5-sysadmin-t", + "text": "sysadmin", + "marks": [ + "k5-sysadmin" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k12", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k9", + "href": "https://identity.kde.org" + } + ], + "children": [ + { + "_type": "span", + "_key": "k10", + "text": "KDE Identity", + "marks": [ + "k9" + ] + }, + { + "_type": "span", + "_key": "k11", + "text": ": in addition to my Ubuntu account? hm.", + "marks": [] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k15", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k13", + "href": "https://kde.org/announcements/plasma/6/6.7.0/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k14", + "text": "Plasma 6.7", + "marks": [ + "k13" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k18", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k16", + "href": "https://www.reddit.com/r/kde/comments/16ylic8/comment/k39subf/" + }, + { + "_type": "link", + "_key": "k16-wayland", + "href": "/tag/wayland" + }, + { + "_type": "link", + "_key": "k16-kde", + "href": "/tag/kde" + }, + { + "_type": "link", + "_key": "k16-plasma", + "href": "/tag/plasma" + }, + { + "_type": "link", + "_key": "k16-workstation", + "href": "/tag/workstation" + }, + { + "_type": "link", + "_key": "k16-sysdamin", + "href": "/tag/sysdamin" + }, + { + "_type": "link", + "_key": "k16-config", + "href": "/tag/config" + } + ], + "children": [ + { + "_type": "span", + "_key": "k17", + "text": "Making Dolphin's compact view denser (r/kde)", + "marks": [ + "k16" + ] + }, + { + "_type": "span", + "_key": "k16-wayland-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k16-wayland-t", + "text": "wayland", + "marks": [ + "k16-wayland" + ] + }, + { + "_type": "span", + "_key": "k16-kde-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k16-kde-t", + "text": "kde", + "marks": [ + "k16-kde" + ] + }, + { + "_type": "span", + "_key": "k16-plasma-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k16-plasma-t", + "text": "plasma", + "marks": [ + "k16-plasma" + ] + }, + { + "_type": "span", + "_key": "k16-workstation-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k16-workstation-t", + "text": "workstation", + "marks": [ + "k16-workstation" + ] + }, + { + "_type": "span", + "_key": "k16-sysdamin-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k16-sysdamin-t", + "text": "sysdamin", + "marks": [ + "k16-sysdamin" + ] + }, + { + "_type": "span", + "_key": "k16-config-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k16-config-t", + "text": "config", + "marks": [ + "k16-config" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k21", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k19", + "href": "https://polkit.pages.freedesktop.org/polkit/polkit.8.html" + } + ], + "children": [ + { + "_type": "span", + "_key": "k20", + "text": "polkit Reference Manual", + "marks": [ + "k19" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k24", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k22", + "href": "https://guix.gnu.org/manual/1.5.0/en/html_node/Desktop-Services.html" + } + ], + "children": [ + { + "_type": "span", + "_key": "k23", + "text": "Desktop Services (GNU Guix manual)", + "marks": [ + "k22" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k26", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k25", + "text": "AI and the web", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k29", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k27", + "href": "https://www.404media.co/doom-loop-openai-and-microsoft-admits-llms-are-destroying-the-web-and-built-on-theft/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k28", + "text": "'Doom Loop': OpenAI and Microsoft admit LLMs are destroying the web (404 Media)", + "marks": [ + "k27" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k32", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k30", + "href": "https://www.theguardian.com/technology/2026/sep/11/openai-agents-rubygems-malicious-packages" + }, + { + "_type": "link", + "_key": "k30-invasive-ai", + "href": "/tag/invasive-ai" + } + ], + "children": [ + { + "_type": "span", + "_key": "k31", + "text": "OpenAI agents involved in an attack on RubyGems (The Guardian)", + "marks": [ + "k30" + ] + }, + { + "_type": "span", + "_key": "k30-invasive-ai-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k30-invasive-ai-t", + "text": "invasive-ai", + "marks": [ + "k30-invasive-ai" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k35", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k33", + "href": "https://blog.cloudflare.com/agent-development-lifecycle/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k34", + "text": "The Agent Development Lifecycle on Cloudflare", + "marks": [ + "k33" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k38", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k36", + "href": "https://blog.cloudflare.com/artifacts-git-for-agents-beta/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k37", + "text": "Artifacts: versioned storage that speaks Git", + "marks": [ + "k36" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k42", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k39", + "href": "https://www.zotero.org/blog/zotero-10/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k40", + "text": "Zotero 10", + "marks": [ + "k39" + ] + }, + { + "_type": "span", + "_key": "k41", + "text": ": Bib sync (#195) gets a new target. (draft)", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k46", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k43", + "href": "https://store.steampowered.com/app/4080030/The_Message_from_Deep_Space/" + }, + { + "_type": "link", + "_key": "k43-game", + "href": "/tag/game" + }, + { + "_type": "link", + "_key": "k43-family", + "href": "/tag/family" + } + ], + "children": [ + { + "_type": "span", + "_key": "k44", + "text": "The Message from Deep Space on Steam", + "marks": [ + "k43" + ] + }, + { + "_type": "span", + "_key": "k45", + "text": ": Kyle invited me to play. Fun!", + "marks": [] + }, + { + "_type": "span", + "_key": "k43-game-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k43-game-t", + "text": "game", + "marks": [ + "k43-game" + ] + }, + { + "_type": "span", + "_key": "k43-family-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k43-family-t", + "text": "family", + "marks": [ + "k43-family" + ] + } + ] + }, + { + "_type": "block", + "_key": "k48", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k47", + "text": "empty plate: social.coop posts (not wired yet)", + "marks": [ + "em" + ] + } + ] + }, + { + "_type": "block", + "_key": "k50", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k49", + "text": "empty plate: photos (not wired yet)", + "marks": [ + "em" + ] + } + ] + } + ] + }, + "taxonomies": { + "tag": [ + "linux", + "desktop", + "migration", + "learning", + "sysadmin", + "wayland", + "kde", + "plasma", + "workstation", + "sysdamin", + "config", + "invasive-ai", + "game", + "family" + ] + } + }, + { + "id": "issue-2026-w39", + "slug": "2026-w39", + "status": "published", + "data": { + "title": "Repair, reuse, and TV night", + "week": "2026-W39", + "dek": "Sep 21–27: KC repair cafés, MATE's last release, family screens. (sketch)", + "body": [ + { + "_type": "block", + "_key": "k52", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k51", + "text": "Kansas City has a whole circular-economy scene I hadn't been paying attention to. (draft)", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k54", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k53", + "text": "Fix it, don't toss it", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k57", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k55", + "href": "https://reusefull.org/repair-cafes/" + }, + { + "_type": "link", + "_key": "k55-circular-economy", + "href": "/tag/circular-economy" + }, + { + "_type": "link", + "_key": "k55-kc", + "href": "/tag/kc" + }, + { + "_type": "link", + "_key": "k55-shopping", + "href": "/tag/shopping" + }, + { + "_type": "link", + "_key": "k55-repair", + "href": "/tag/repair" + } + ], + "children": [ + { + "_type": "span", + "_key": "k56", + "text": "Repair Cafes | Re.Use.Full", + "marks": [ + "k55" + ] + }, + { + "_type": "span", + "_key": "k55-circular-economy-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k55-circular-economy-t", + "text": "circular-economy", + "marks": [ + "k55-circular-economy" + ] + }, + { + "_type": "span", + "_key": "k55-kc-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k55-kc-t", + "text": "kc", + "marks": [ + "k55-kc" + ] + }, + { + "_type": "span", + "_key": "k55-shopping-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k55-shopping-t", + "text": "shopping", + "marks": [ + "k55-shopping" + ] + }, + { + "_type": "span", + "_key": "k55-repair-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k55-repair-t", + "text": "repair", + "marks": [ + "k55-repair" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k60", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k58", + "href": "https://reusefull.org/un-dumpster-day/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k59", + "text": "Un-Dumpster Day | Re.Use.Full", + "marks": [ + "k58" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k64", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k61", + "href": "https://www.amazon.com/dp/B005EJH6Z4" + } + ], + "children": [ + { + "_type": "span", + "_key": "k62", + "text": "Amazon Basics wireless mouse", + "marks": [ + "k61" + ] + }, + { + "_type": "span", + "_key": "k63", + "text": ": middle mouse button failed. A repair café candidate? (draft)", + "marks": [] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k68", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k65", + "href": "https://discourse.ubuntu.com/t/mate-no-26-04-release-how-does-that-affect-you-and-what-is-the-future-of-ubuntu-mate/83877" + } + ], + "children": [ + { + "_type": "span", + "_key": "k66", + "text": "MATE: no 26.04 release; what is the future of Ubuntu MATE?", + "marks": [ + "k65" + ] + }, + { + "_type": "span", + "_key": "k67", + "text": ": So last week's move was timely. (draft)", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k72", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k69", + "href": "https://blog.exe.dev/we-taught-sqlc-to-invalidate-our-caches" + } + ], + "children": [ + { + "_type": "span", + "_key": "k70", + "text": "We taught sqlc to invalidate our caches (exe.dev)", + "marks": [ + "k69" + ] + }, + { + "_type": "span", + "_key": "k71", + "text": ": Relevant: potluck's own diigo cache can't invalidate yet. (draft)", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k74", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k73", + "text": "Family screens", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k77", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k75", + "href": "https://critrole.com" + }, + { + "_type": "link", + "_key": "k75-tv", + "href": "/tag/tv" + }, + { + "_type": "link", + "_key": "k75-family", + "href": "/tag/family" + }, + { + "_type": "link", + "_key": "k75-games", + "href": "/tag/games" + } + ], + "children": [ + { + "_type": "span", + "_key": "k76", + "text": "Critical Role", + "marks": [ + "k75" + ] + }, + { + "_type": "span", + "_key": "k75-tv-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k75-tv-t", + "text": "tv", + "marks": [ + "k75-tv" + ] + }, + { + "_type": "span", + "_key": "k75-family-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k75-family-t", + "text": "family", + "marks": [ + "k75-family" + ] + }, + { + "_type": "span", + "_key": "k75-games-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k75-games-t", + "text": "games", + "marks": [ + "k75-games" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k81", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k78", + "href": "https://en.wikipedia.org/wiki/Chad_Powers" + }, + { + "_type": "link", + "_key": "k78-tv", + "href": "/tag/tv" + } + ], + "children": [ + { + "_type": "span", + "_key": "k79", + "text": "Chad Powers (Wikipedia)", + "marks": [ + "k78" + ] + }, + { + "_type": "span", + "_key": "k80", + "text": ": spotter: LQ - see eli manning viral", + "marks": [] + }, + { + "_type": "span", + "_key": "k78-tv-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k78-tv-t", + "text": "tv", + "marks": [ + "k78-tv" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k83", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k82", + "text": "empty plate: social.coop posts (not wired yet)", + "marks": [ + "em" + ] + } + ] + }, + { + "_type": "block", + "_key": "k85", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k84", + "text": "empty plate: photos (not wired yet)", + "marks": [ + "em" + ] + } + ] + } + ] + }, + "taxonomies": { + "tag": [ + "circular-economy", + "kc", + "shopping", + "repair", + "tv", + "family", + "games" + ] + } + }, + { + "id": "issue-2026-w40", + "slug": "2026-w40", + "status": "published", + "data": { + "title": "Co-ops, protocols, and where to host", + "week": "2026-W40", + "dek": "Sep 28–Oct 4: co/core, AT Protocol bookmarks, hosting options. (sketch)", + "body": [ + { + "_type": "block", + "_key": "k87", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k86", + "text": "A week of reading about cooperatives and the protocols they might run on, and about owning your own bookmarks, which is what this zine is made of. (draft)", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k89", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k88", + "text": "Cooperative economics", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k92", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k90", + "href": "https://cocore.dev" + } + ], + "children": [ + { + "_type": "span", + "_key": "k91", + "text": "co/core: an AI cooperative", + "marks": [ + "k90" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k95", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k93", + "href": "https://cocore.dev/blog/token-market-design" + }, + { + "_type": "link", + "_key": "k93-governance", + "href": "/tag/governance" + }, + { + "_type": "link", + "_key": "k93-economics", + "href": "/tag/economics" + }, + { + "_type": "link", + "_key": "k93-protocol", + "href": "/tag/protocol" + }, + { + "_type": "link", + "_key": "k93-generative-ai", + "href": "/tag/generative-ai" + } + ], + "children": [ + { + "_type": "span", + "_key": "k94", + "text": "How the co/core economy is shaped, and why", + "marks": [ + "k93" + ] + }, + { + "_type": "span", + "_key": "k93-governance-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k93-governance-t", + "text": "governance", + "marks": [ + "k93-governance" + ] + }, + { + "_type": "span", + "_key": "k93-economics-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k93-economics-t", + "text": "economics", + "marks": [ + "k93-economics" + ] + }, + { + "_type": "span", + "_key": "k93-protocol-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k93-protocol-t", + "text": "protocol", + "marks": [ + "k93-protocol" + ] + }, + { + "_type": "span", + "_key": "k93-generative-ai-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k93-generative-ai-t", + "text": "generative-ai", + "marks": [ + "k93-generative-ai" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k98", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k96", + "href": "https://www.colorado.edu/lab/medlab/2023/03/27/designing-economy-involvement-co-ops-0" + }, + { + "_type": "link", + "_key": "k96-governance", + "href": "/tag/governance" + }, + { + "_type": "link", + "_key": "k96-collaboration", + "href": "/tag/collaboration" + }, + { + "_type": "link", + "_key": "k96-scaling", + "href": "/tag/scaling" + } + ], + "children": [ + { + "_type": "span", + "_key": "k97", + "text": "Designing for the Economy of Involvement in Co-ops (MEDLab, CU Boulder)", + "marks": [ + "k96" + ] + }, + { + "_type": "span", + "_key": "k96-governance-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k96-governance-t", + "text": "governance", + "marks": [ + "k96-governance" + ] + }, + { + "_type": "span", + "_key": "k96-collaboration-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k96-collaboration-t", + "text": "collaboration", + "marks": [ + "k96-collaboration" + ] + }, + { + "_type": "span", + "_key": "k96-scaling-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k96-scaling-t", + "text": "scaling", + "marks": [ + "k96-scaling" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k102", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k99", + "href": "https://www.transkribus.org" + } + ], + "children": [ + { + "_type": "span", + "_key": "k100", + "text": "Transkribus", + "marks": [ + "k99" + ] + }, + { + "_type": "span", + "_key": "k101", + "text": ": run by a cooperative", + "marks": [] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k104", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k103", + "text": "Own your bookmarks, AT Protocol edition", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k108", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k105", + "href": "https://kipclip.com" + } + ], + "children": [ + { + "_type": "span", + "_key": "k106", + "text": "kipclip: own your bookmarks, a Pinboard alternative on AT Protocol", + "marks": [ + "k105" + ] + }, + { + "_type": "span", + "_key": "k107", + "text": ": does it do annotations / highlights?", + "marks": [] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k111", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k109", + "href": "https://semble.so" + } + ], + "children": [ + { + "_type": "span", + "_key": "k110", + "text": "Semble: make sense of the web, together", + "marks": [ + "k109" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k114", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k112", + "href": "https://www.graze.social" + } + ], + "children": [ + { + "_type": "span", + "_key": "k113", + "text": "Graze: custom feed builder for Bluesky", + "marks": [ + "k112" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k117", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k115", + "href": "https://atproto.com/guides/account-recovery" + } + ], + "children": [ + { + "_type": "span", + "_key": "k116", + "text": "AT Protocol account recovery", + "marks": [ + "k115" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k120", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k118", + "href": "https://blog.plcred.org/3mwlphq42d227" + } + ], + "children": [ + { + "_type": "span", + "_key": "k119", + "text": "First steps of the PLC organization", + "marks": [ + "k118" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k122", + "style": "h2", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k121", + "text": "Where to host", + "marks": [] + } + ] + }, + { + "_type": "block", + "_key": "k125", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k123", + "href": "https://www.hetzner.com/storage/storage-box/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k124", + "text": "Hetzner Storage Box", + "marks": [ + "k123" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k128", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k126", + "href": "https://en.wikipedia.org/wiki/Infomaniak" + } + ], + "children": [ + { + "_type": "span", + "_key": "k127", + "text": "Infomaniak (Wikipedia)", + "marks": [ + "k126" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k131", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k129", + "href": "https://www.cloudron.io/pricing.html" + } + ], + "children": [ + { + "_type": "span", + "_key": "k130", + "text": "Cloudron pricing", + "marks": [ + "k129" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k134", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k132", + "href": "https://blog.nearlyfreespeech.net/2022/01/18/nearlyfreespeech-net-turns-20-today/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k133", + "text": "NearlyFreeSpeech.NET turns 20", + "marks": [ + "k132" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k137", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k135", + "href": "https://blog.nearlyfreespeech.net/2024/05/10/automatic-tls-is-now-a-thing/" + } + ], + "children": [ + { + "_type": "span", + "_key": "k136", + "text": "NearlyFreeSpeech.NET: automatic TLS", + "marks": [ + "k135" + ] + } + ], + "listItem": "bullet", + "level": 1 + }, + { + "_type": "block", + "_key": "k141", + "style": "normal", + "markDefs": [ + { + "_type": "link", + "_key": "k138", + "href": "https://regeneration.us/material-flow-analysis-of-discarded-materials-in-the-kansas-city-metropolitan-area/" + }, + { + "_type": "link", + "_key": "k138-kc", + "href": "/tag/kc" + }, + { + "_type": "link", + "_key": "k138-regenerative", + "href": "/tag/regenerative" + }, + { + "_type": "link", + "_key": "k138-circular-economy", + "href": "/tag/circular-economy" + }, + { + "_type": "link", + "_key": "k138-waste", + "href": "/tag/waste" + } + ], + "children": [ + { + "_type": "span", + "_key": "k139", + "text": "Material Flow Analysis of discarded materials, KC metro", + "marks": [ + "k138" + ] + }, + { + "_type": "span", + "_key": "k140", + "text": ": Continuing W39's thread. (draft)", + "marks": [] + }, + { + "_type": "span", + "_key": "k138-kc-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k138-kc-t", + "text": "kc", + "marks": [ + "k138-kc" + ] + }, + { + "_type": "span", + "_key": "k138-regenerative-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k138-regenerative-t", + "text": "regenerative", + "marks": [ + "k138-regenerative" + ] + }, + { + "_type": "span", + "_key": "k138-circular-economy-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k138-circular-economy-t", + "text": "circular-economy", + "marks": [ + "k138-circular-economy" + ] + }, + { + "_type": "span", + "_key": "k138-waste-sp", + "text": " ", + "marks": [] + }, + { + "_type": "span", + "_key": "k138-waste-t", + "text": "waste", + "marks": [ + "k138-waste" + ] + } + ] + }, + { + "_type": "block", + "_key": "k143", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k142", + "text": "empty plate: social.coop posts (not wired yet)", + "marks": [ + "em" + ] + } + ] + }, + { + "_type": "block", + "_key": "k145", + "style": "normal", + "markDefs": [], + "children": [ + { + "_type": "span", + "_key": "k144", + "text": "empty plate: GitHub activity (potluck itself started this week)", + "marks": [ + "em" + ] + } + ] + } + ] + }, + "taxonomies": { + "tag": [ + "governance", + "economics", + "protocol", + "generative-ai", + "collaboration", + "scaling", + "kc", + "regenerative", + "circular-economy", + "waste" + ] + } + } + ] + }, + "taxonomies": [ + { + "name": "tag", + "label": "Tags", + "labelSingular": "Tag", + "hierarchical": false, + "collections": [ + "posts", + "issues" + ], + "terms": [ + { + "slug": "circular-economy", + "label": "circular-economy" + }, + { + "slug": "collaboration", + "label": "collaboration" + }, + { + "slug": "config", + "label": "config" + }, + { + "slug": "desktop", + "label": "desktop" + }, + { + "slug": "economics", + "label": "economics" + }, + { + "slug": "family", + "label": "family" + }, + { + "slug": "game", + "label": "game" + }, + { + "slug": "games", + "label": "games" + }, + { + "slug": "generative-ai", + "label": "generative-ai" + }, + { + "slug": "governance", + "label": "governance" + }, + { + "slug": "invasive-ai", + "label": "invasive-ai" + }, + { + "slug": "kc", + "label": "kc" + }, + { + "slug": "kde", + "label": "kde" + }, + { + "slug": "learning", + "label": "learning" + }, + { + "slug": "linux", + "label": "linux" + }, + { + "slug": "migration", + "label": "migration" + }, + { + "slug": "plasma", + "label": "plasma" + }, + { + "slug": "protocol", + "label": "protocol" + }, + { + "slug": "regenerative", + "label": "regenerative" + }, + { + "slug": "repair", + "label": "repair" + }, + { + "slug": "scaling", + "label": "scaling" + }, + { + "slug": "shopping", + "label": "shopping" + }, + { + "slug": "sysadmin", + "label": "sysadmin" + }, + { + "slug": "sysdamin", + "label": "sysdamin" + }, + { + "slug": "tv", + "label": "tv" + }, + { + "slug": "waste", + "label": "waste" + }, + { + "slug": "wayland", + "label": "wayland" + }, + { + "slug": "workstation", + "label": "workstation" + } + ] + } + ] +} \ No newline at end of file diff --git a/potluck/seed/seed.json b/potluck/seed/seed.json index 4b513bc..c41d126 100644 --- a/potluck/seed/seed.json +++ b/potluck/seed/seed.json @@ -7,8 +7,8 @@ "author": "EmDash" }, "settings": { - "title": "My Blog", - "tagline": "Thoughts on building for the web" + "title": "potluck", + "tagline": "A weekly zine: everyone brings a dish" }, "collections": [ { @@ -94,7 +94,7 @@ "label": "Tags", "labelSingular": "Tag", "hierarchical": false, - "collections": ["posts"], + "collections": ["posts", "issues"], "terms": [ { "slug": "webdev", @@ -145,8 +145,8 @@ }, { "type": "custom", - "label": "Posts", - "url": "/posts" + "label": "Issues", + "url": "/issues" } ] } @@ -207,7 +207,7 @@ "children": [ { "_type": "span", - "text": "A blog about software, design, and the occasional stray thought.", + "text": "A weekly zine: everyone brings a dish. Bookmarks and notes from the week, gathered and trimmed.", "_key": "k1" } ], @@ -285,652 +285,99 @@ "content": [ { "_type": "block", + "_key": "k1", "style": "normal", + "markDefs": [], "children": [ { "_type": "span", - "text": "A place for writing about software, design, and the occasional stray thought. No posting schedule, no newsletter funnel. Just things I wanted to write down.", - "_key": "k1" + "_key": "k0", + "text": "potluck is a weekly zine. Everyone brings a dish: each source contributes what it found that week, and an issue is the table they share.", + "marks": [] } - ], - "_key": "k0" + ] }, { "_type": "block", + "_key": "k6", "style": "normal", + "markDefs": [ + { + "_key": "k3", + "_type": "link", + "href": "https://www.diigo.com/user/dckc-madmode" + } + ], "children": [ { "_type": "span", - "text": "Built with Astro and EmDash. The source is open if you want to see how it works.", - "_key": "k3" - } - ], - "_key": "k2" - } - ] - } - } - ], - "posts": [ - { - "id": "post-1", - "slug": "building-for-the-long-term", - "status": "published", - "data": { - "title": "Building for the Long Term", - "excerpt": "The frameworks will change. The databases will change. What survives is the clarity of your thinking.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1461749280684-dccba630e2f6?w=1200&h=800&fit=crop", - "alt": "Code on a monitor in a dark room", - "filename": "building-long-term.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ + "_key": "k2", + "text": "Right now the dishes are bookmarks from ", + "marks": [] + }, { "_type": "span", - "text": "Every few years the industry collectively decides that everything we've been doing is wrong and there's a better way. New frameworks, new paradigms, new build tools. The churn is relentless, and if you're not careful, you spend more time migrating than building.", - "_key": "k1" + "_key": "k4", + "text": "diigo", + "marks": [ + "k3" + ] + }, + { + "_type": "span", + "_key": "k5", + "text": ", with their tags. Posts from social.coop, photos, and new Zotero items are meant to join them. Each issue is a draft first: everything from the week is proposed, then trimmed by hand and given a few lines of commentary.", + "marks": [] } - ], - "_key": "k0" + ] }, { "_type": "block", + "_key": "k8", "style": "normal", + "markDefs": [], "children": [ { "_type": "span", - "text": "I've been writing software long enough to have seen several of these cycles. jQuery to Backbone to Angular to React to whatever comes next. Each transition felt urgent at the time. Looking back, the things that actually mattered were rarely about the framework.", - "_key": "k3" + "_key": "k7", + "text": "The name is about cooperation without vulnerability: at a potluck you share food without letting anyone into your kitchen. The software behind it works the same way, with each source passing finished dishes rather than handing over its keys.", + "marks": [] } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "What survives", - "_key": "k5" - } - ], - "_key": "k4" + ] }, { "_type": "block", + "_key": "kd", "style": "normal", + "markDefs": [ + { + "_key": "ka", + "_type": "link", + "href": "https://www.madmode.com/" + } + ], "children": [ { "_type": "span", - "text": "Clean data models survive. Clear boundaries between systems survive. Good naming survives. The decision to keep things simple when you could have made them clever - that definitely survives.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ + "_key": "k9", + "text": "potluck is a sibling of ", + "marks": [] + }, { "_type": "span", - "text": "What doesn't survive is code that was written to impress, abstractions built for problems that never materialized, and architectures designed around a framework's opinions rather than the domain's reality.", - "_key": "k9" - } - ], - "_key": "k8" - }, - { - "_type": "block", - "style": "normal", - "children": [ + "_key": "kb", + "text": "madmode.com", + "marks": [ + "ka" + ] + }, { "_type": "span", - "text": "The best code I've written is boring. It reads like prose, does one thing well, and doesn't require a PhD in category theory to understand. The worst code I've written was technically impressive at the time.", - "_key": "k11" + "_key": "kc", + "text": ", Dan Connolly's tinkering lab notebook.", + "marks": [] } - ], - "_key": "k10" - } - ] - }, - "bylines": [ - { - "byline": "byline-editorial" - }, - { - "byline": "byline-guest", - "roleLabel": "Guest essay" - } - ], - "taxonomies": { - "category": ["development"], - "tag": ["opinion"] - } - }, - { - "id": "post-2", - "slug": "the-case-for-static", - "status": "published", - "data": { - "title": "The Case for Static", - "excerpt": "Static sites aren't a step backwards. They're what you get when you take performance and simplicity seriously.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=1200&h=800&fit=crop", - "alt": "Laptop and coffee on a wooden table", - "filename": "case-for-static.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "There's a certain irony in the fact that the web started static, went dynamic, and is now swinging back toward static again. But the static sites of today aren't the hand-coded HTML pages of 1998. They're generated, optimized, and deployed to edge networks that serve them in milliseconds.", - "_key": "k1" - } - ], - "_key": "k0" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The pitch for server-rendered everything was compelling: dynamic content, personalization, real-time data. But most sites don't need most of that most of the time. A blog post doesn't need to be rendered on every request. A product page doesn't change every second.", - "_key": "k3" - } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "The performance argument", - "_key": "k5" - } - ], - "_key": "k4" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "A static file served from a CDN is as fast as the web gets. No cold starts, no database queries, no server-side rendering overhead. The Time to First Byte is essentially the network latency to your nearest edge node. You can't beat physics.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "And when you do need dynamic behavior, you can add it surgically. An island of interactivity in a sea of static HTML. The best of both worlds, without paying the cost of either at all times.", - "_key": "k9" - } - ], - "_key": "k8" - } - ] - }, - "bylines": [ - { - "byline": "byline-editorial" - } - ], - "taxonomies": { - "category": ["development"], - "tag": ["webdev", "opinion"] - } - }, - { - "id": "post-3", - "slug": "learning-in-public", - "status": "published", - "data": { - "title": "Learning in Public", - "excerpt": "Writing about what you're learning is the fastest way to find out what you don't actually understand.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1432821596592-e2c18b78144f?w=1200&h=800&fit=crop", - "alt": "Notebook and pen on a desk", - "filename": "learning-in-public.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "I started writing about things I was learning not because I had anything original to say, but because I kept forgetting what I'd figured out. The blog posts were notes to my future self, published publicly more out of laziness than courage.", - "_key": "k1" - } - ], - "_key": "k0" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "What I didn't expect was how much the writing itself would accelerate the learning. There's a particular kind of clarity that comes from trying to explain something to someone else. The gaps in your understanding, which you can happily ignore when the knowledge lives only in your head, become painfully obvious when you try to put it into sentences.", - "_key": "k3" - } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "The fear of being wrong", - "_key": "k5" - } - ], - "_key": "k4" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The biggest barrier isn't time or writing skill. It's the fear of publishing something that turns out to be wrong. But here's the thing: being wrong publicly is one of the most efficient ways to learn. Someone will correct you, often kindly, and you'll remember that correction forever.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The posts that helped me most weren't written by experts. They were written by people one step ahead of me on the same path, in language that hadn't yet been polished into abstraction. There's a place for that kind of writing, and it's more valuable than most people realize.", - "_key": "k9" - } - ], - "_key": "k8" - } - ] - }, - "taxonomies": { - "category": ["notes"], - "tag": ["opinion"] - } - }, - { - "id": "post-4", - "slug": "small-tools-big-impact", - "status": "published", - "data": { - "title": "Small Tools, Big Impact", - "excerpt": "The best developer tools do one thing well and get out of your way. A love letter to focused software.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1575026615908-666710ae5e47?w=1200&h=800&fit=crop", - "alt": "Wrenches and hand tools hanging on a workshop wall", - "filename": "small-tools.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "There's a class of software that doesn't get enough appreciation. Not the frameworks or the platforms or the IDEs, but the small, sharp tools that solve one problem so well you stop thinking about them. They become invisible, which is the highest compliment you can pay a tool.", - "_key": "k1" - } - ], - "_key": "k0" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "I'm talking about things like ripgrep, which searches code so fast it changed how I think about searching. Or jq, which makes JSON feel like a first-class data format in the terminal. Or curl, which has been quietly powering the internet's plumbing for decades.", - "_key": "k3" - } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "The Unix philosophy, revisited", - "_key": "k5" - } - ], - "_key": "k4" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Do one thing well. The advice is old enough to be a cliche, but the best modern tools still follow it. They don't try to be platforms. They don't have plugin ecosystems or configuration languages or startup wizards. They do their job and they compose with other tools that do theirs.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The temptation is always to add more. One more feature, one more option, one more integration. But every addition is a decision someone has to make, a path through the code that has to be maintained, a thing that can break. The best tools resist this. They stay small, and in staying small, they stay reliable.", - "_key": "k9" - } - ], - "_key": "k8" - } - ] - }, - "taxonomies": { - "category": ["development"], - "tag": ["tools"] - } - }, - { - "id": "post-5", - "slug": "designing-with-constraints", - "status": "published", - "data": { - "title": "Designing with Constraints", - "excerpt": "Limitations aren't obstacles to creativity. They're the structure that makes creativity possible.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1513542789411-b6a5d4f31634?w=1200&h=800&fit=crop", - "alt": "Pencils and design tools on a desk", - "filename": "designing-with-constraints.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Give a designer a blank canvas and unlimited time, and they'll often produce something mediocre. Give them a tight brief, a small screen, and a deadline, and they'll surprise you. This isn't a paradox - it's how creativity actually works.", - "_key": "k1" - } - ], - "_key": "k0" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Constraints force decisions. When you can't use more than two typefaces, you have to choose carefully. When the page has to load in under a second, every element earns its place. When the interface has to work on a 320px screen, you discover what's truly essential.", - "_key": "k3" - } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "Embracing the box", - "_key": "k5" - } - ], - "_key": "k4" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The web itself is a constraint. HTML flows in one direction. CSS has a box model. Browsers have viewport sizes and font rendering quirks. You can fight these constraints or you can work with them, and the results are dramatically different.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The designs I admire most don't look like they were forced through a framework. They look like they grew naturally from the medium, respecting its grain rather than working against it. That only happens when you treat constraints as creative partners rather than enemies.", - "_key": "k9" - } - ], - "_key": "k8" - } - ] - }, - "taxonomies": { - "category": ["design"], - "tag": ["creativity"] - } - }, - { - "id": "post-6", - "slug": "a-weekend-with-a-side-project", - "status": "published", - "data": { - "title": "A Weekend with a Side Project", - "excerpt": "No stakeholders, no deadlines, no Jira tickets. Just you and a dumb idea that might turn into something.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1542831371-29b0f74f9713?w=1200&h=800&fit=crop", - "alt": "Code on a screen with a dark theme", - "filename": "weekend-side-project.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Saturday morning. Coffee's made, the house is quiet, and I've got an idea that's been nagging at me all week. Not a good idea, necessarily - just a persistent one. A small tool that does a thing I keep doing manually. How hard could it be?", - "_key": "k1" - } - ], - "_key": "k0" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "This is the best kind of programming. No requirements document, no sprint planning, no pull request reviews. Just a text editor and a problem. The freedom to make terrible architectural decisions, rewrite everything twice, and follow tangents that turn out to be dead ends.", - "_key": "k3" - } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "Why side projects matter", - "_key": "k5" - } - ], - "_key": "k4" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Side projects are where you learn things your day job would never teach you. Not because the problems are harder, but because you're free to take risks. Try a language you've never used. Build something without a framework. Deploy to a platform you've only read about. The stakes are zero, which makes the learning maximum.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "By Sunday evening, the thing sort of works. It's rough, the error handling is nonexistent, and the README is a single sentence. But it solves the problem I set out to solve, and I learned three things I didn't know on Friday. Not a bad weekend.", - "_key": "k9" - } - ], - "_key": "k8" - } - ] - }, - "taxonomies": { - "category": ["development"], - "tag": ["creativity"] - } - }, - { - "id": "post-7", - "slug": "notes-on-simplicity", - "status": "published", - "data": { - "title": "Notes on Simplicity", - "excerpt": "Simplicity isn't the absence of complexity. It's the result of understanding a problem well enough to solve it cleanly.", - "featured_image": { - "$media": { - "url": "https://images.unsplash.com/photo-1559051668-e1fa58f25786?w=1200&h=800&fit=crop", - "alt": "Geometric pattern carved into white paper", - "filename": "notes-on-simplicity.jpg" - } - }, - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Every piece of software starts simple. A few files, a clear purpose, a small surface area. Then features get added, edge cases get handled, and before long you're looking at something that requires a diagram to understand. This isn't inevitable, but it takes discipline to prevent.", - "_key": "k1" - } - ], - "_key": "k0" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The hard part of simplicity isn't the initial design. It's the ongoing resistance to complication. Every feature request, every bug fix, every refactor is an opportunity to add complexity. Saying no is the most important design skill, and the least celebrated.", - "_key": "k3" - } - ], - "_key": "k2" - }, - { - "_type": "block", - "style": "h2", - "children": [ - { - "_type": "span", - "text": "Removing as a feature", - "_key": "k5" - } - ], - "_key": "k4" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "The best version of a product often has fewer features than the previous one. Not because features were missing, but because someone had the courage to remove things that weren't earning their keep. Every feature has a cost - in maintenance, in cognitive load, in the weight of the interface.", - "_key": "k7" - } - ], - "_key": "k6" - }, - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "Simplicity is a practice, not a destination. You never arrive at simple. You just keep asking: is this necessary? Could this be clearer? Is there a way to solve this problem by removing something instead of adding something? The answer is yes more often than you'd expect.", - "_key": "k9" - } - ], - "_key": "k8" - } - ] - }, - "taxonomies": { - "category": ["notes"], - "tag": ["opinion"] - } - }, - { - "id": "post-draft", - "slug": "work-in-progress", - "status": "draft", - "data": { - "title": "Work in Progress", - "excerpt": "This post is still being written.", - "content": [ - { - "_type": "block", - "style": "normal", - "children": [ - { - "_type": "span", - "text": "This is a draft post that won't appear in the public listing.", - "_key": "k1" - } - ], - "_key": "k0" + ] } ] } diff --git a/potluck/src/components/CiteLink.astro b/potluck/src/components/CiteLink.astro new file mode 100644 index 0000000..5f99362 --- /dev/null +++ b/potluck/src/components/CiteLink.astro @@ -0,0 +1,43 @@ +--- +/** + * Portable Text link mark for issue bodies: an off-site link is a citation, + * marked up as an `h-cite` (name + url) so microformats parsers see each + * bookmark; a `/tag/` link is a rel-tag `p-category` of the issue + * (the `#` is CSS, so the category value is the bare tag); other on-site + * and anchor links stay plain. + * + * @see https://microformats.org/wiki/h-cite + * @see https://microformats.org/wiki/rel-tag + */ +export interface Props { + node: { markDef: { _type: "link"; _key: string; href: string; blank?: boolean } }; +} + +const href = Astro.props.node?.markDef?.href ?? ""; +// only these schemes become hrefs (the stored href is editor input) +const safe = /^(https?:|mailto:|\/|#)/i.test(href) ? href : ""; +const offSite = /^https?:/i.test(safe); +const isTag = safe.startsWith("/tag/"); +--- + +{ + offSite ? ( + + ) : isTag ? ( + + ) : ( + + ) +} + + diff --git a/potluck/src/layouts/Base.astro b/potluck/src/layouts/Base.astro index 3fd7b10..5fb1fe9 100644 --- a/potluck/src/layouts/Base.astro +++ b/potluck/src/layouts/Base.astro @@ -126,7 +126,7 @@ const isLoggedIn = !!Astro.locals.user; inputClass="site-search-input" resultsClass="site-search-results" resultClass="site-search-result" - collections={["posts", "pages"]} + collections={["issues", "pages"]} /> diff --git a/potluck/src/styles/theme.css b/potluck/src/styles/theme.css index 974b857..5bc5766 100644 --- a/potluck/src/styles/theme.css +++ b/potluck/src/styles/theme.css @@ -57,6 +57,14 @@ a:where(:not([class*="emdash"]):not([class*="ec-"])):hover { border-bottom-style: solid; } +/* potluck's own pages: one column, aligned with the header's nav (same + width and side padding as the template's page containers). */ +.column { + max-width: var(--wide-width); + margin-inline: auto; + padding: var(--spacing-6) var(--spacing-6) var(--spacing-16); +} + /* Keep the signed-in Admin link separated from the primary navigation. */ .nav-admin { margin-inline-start: var(--spacing-5); diff --git a/potluck/src/utils/site-identity.ts b/potluck/src/utils/site-identity.ts index 857ffce..044ed32 100644 --- a/potluck/src/utils/site-identity.ts +++ b/potluck/src/utils/site-identity.ts @@ -12,8 +12,8 @@ export interface BlogSiteIdentitySettings { favicon?: MediaReference; } -const DEFAULT_SITE_TITLE = "My Blog"; -const DEFAULT_SITE_TAGLINE = "Thoughts, stories, and ideas."; +const DEFAULT_SITE_TITLE = "potluck"; +const DEFAULT_SITE_TAGLINE = "A weekly zine: everyone brings a dish"; export function resolveBlogSiteIdentity(settings?: BlogSiteIdentitySettings) { return {