Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

papers/platter: move dashboard to /platter/jeffrey/, add platter index

- new /platter/ namespace lists per-platter dashboards (jeffrey now,
whistlegraph soon); lets the URL space breathe as more platters add
visual surfaces
- /platter/jeffrey/ retitled "jeffrey's instagram" with breadcrumb back
to the platter index; entry on /platter/ uses a recent thumb as cover
- Caddyfile try_files now covers both `/jeffrey` (no slash →
{path}/index.html) and `/jeffrey/` (slash → {path}index.html); without
the no-slash term the bare /jeffrey URL fell through to the SPA root
- build-thumbnails.mjs writes manifest to the new location
- platter README updated with new URL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+171 -19
+11 -10
lith/Caddyfile
··· 56 56 } 57 57 root * /opt/ac/system/public/papers.aesthetic.computer 58 58 59 - # SPA fallback for unknown paths: if neither {path}, {path}.html, nor 60 - # {path}index.html exists on disk, serve /index.html — but mark it 61 - # no-cache. Without this, a request for a not-yet-deployed PDF gets 62 - # the SPA HTML, and Cloudflare pins that HTML to the PDF URL for 4h 63 - # via its default static-asset cache. (See: 2026-04-26 latency-paper 64 - # deploy gap.) The {path}index.html term lets directory-style URLs 65 - # like /jeffrey/ resolve to /jeffrey/index.html instead of falling 66 - # through to the SPA root. (See: 2026-04-29 jeffrey-platter deploy.) 59 + # SPA fallback for unknown paths: if none of {path}, {path}.html, 60 + # {path}/index.html, or {path}index.html exists on disk, serve 61 + # /index.html — but mark it no-cache. Without this, a request for a 62 + # not-yet-deployed PDF gets the SPA HTML, and Cloudflare pins that 63 + # HTML to the PDF URL for 4h via its default static-asset cache. 64 + # (See: 2026-04-26 latency-paper deploy gap.) The two index.html 65 + # terms cover both /platter (no slash → {path}/index.html) and 66 + # /platter/ (slash → {path}index.html). (See: 2026-04-29 67 + # jeffrey-platter URL move.) 67 68 @missing not file { 68 - try_files {path} {path}.html {path}index.html 69 + try_files {path} {path}.html {path}/index.html {path}index.html 69 70 } 70 71 handle @missing { 71 72 header Cache-Control "no-cache, must-revalidate, max-age=0" ··· 73 74 file_server 74 75 } 75 76 76 - try_files {path} {path}.html {path}index.html 77 + try_files {path} {path}.html {path}/index.html {path}index.html 77 78 file_server 78 79 } 79 80
+5 -5
papers/jeffrey-platter/README.md
··· 41 41 42 42 ## 2. External (assets CDN + sites) 43 43 44 - ### Hosted dashboard — `papers.aesthetic.computer/jeffrey/` 44 + ### Hosted dashboard — `papers.aesthetic.computer/platter/jeffrey/` 45 45 46 - Public sortable/filterable gallery of the 457 confirmed-jeffrey IG selfies from `@whistlegraph` (face-matched against the AV shoot, described per-image by GPT-4o). Lives at: 46 + Public sortable/filterable gallery of the 457 confirmed-jeffrey IG selfies from `@whistlegraph` (face-matched against the AV shoot, described per-image by GPT-4o). Lives under the new `/platter/` namespace alongside future per-platter dashboards: 47 47 48 - - **Dashboard:** https://papers.aesthetic.computer/jeffrey/ (static `index.html`) 49 - - **Source HTML:** [system/public/papers.aesthetic.computer/jeffrey/index.html](../../system/public/papers.aesthetic.computer/jeffrey/index.html) 50 - - **Manifest JSON:** [system/public/papers.aesthetic.computer/jeffrey/manifest.json](../../system/public/papers.aesthetic.computer/jeffrey/manifest.json) (slim per-row schema with CDN thumb URL) 48 + - **Dashboard:** https://papers.aesthetic.computer/platter/jeffrey/ (static `index.html`, listed on the [platter index](https://papers.aesthetic.computer/platter/) as "Jeffrey's Instagram") 49 + - **Source HTML:** [system/public/papers.aesthetic.computer/platter/jeffrey/index.html](../../system/public/papers.aesthetic.computer/platter/jeffrey/index.html) 50 + - **Manifest JSON:** [system/public/papers.aesthetic.computer/platter/jeffrey/manifest.json](../../system/public/papers.aesthetic.computer/platter/jeffrey/manifest.json) (slim per-row schema with CDN thumb URL) 51 51 - **Thumbnails:** `assets.aesthetic.computer/jeffreys/whistlegraph/<shortcode>.jpg` (256px, ~30 KB each, ~5 MB total) 52 52 - **Build script:** [`portraits/jeffrey/bin/build-thumbnails.mjs`](../../portraits/jeffrey/bin/build-thumbnails.mjs) — reads `portraits/jeffrey/curated/jeffrey-described.jsonl`, generates thumbs into `system/public/assets/jeffreys/whistlegraph/`, and rewrites `manifest.json`. 53 53 - **Refresh:** `node portraits/jeffrey/bin/build-thumbnails.mjs && npm run assets:sync:up`
+1 -1
portraits/jeffrey/bin/build-thumbnails.mjs
··· 37 37 const DESCRIBED = join(REPO, "portraits", "jeffrey", "curated", "jeffrey-described.jsonl"); 38 38 const THUMB_DIR = join(REPO, "system", "public", "assets", "jeffreys", "whistlegraph"); 39 39 const MANIFEST_OUT = join( 40 - REPO, "system", "public", "papers.aesthetic.computer", "jeffrey", "manifest.json", 40 + REPO, "system", "public", "papers.aesthetic.computer", "platter", "jeffrey", "manifest.json", 41 41 ); 42 42 const CDN_PREFIX = "https://assets.aesthetic.computer/jeffreys/whistlegraph"; 43 43
+5 -3
system/public/papers.aesthetic.computer/jeffrey/index.html system/public/papers.aesthetic.computer/platter/jeffrey/index.html
··· 3 3 <head> 4 4 <meta charset="UTF-8"> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 - <title>jeffrey · platter</title> 6 + <title>jeffrey's instagram · platter</title> 7 7 <meta name="description" content="An index of Jeffrey Alan Scudder's confirmed-self IG selfie corpus from @whistlegraph — 457 photos with face-match scores and per-image GPT-4o descriptions."> 8 8 <link rel="icon" href="https://aesthetic.computer/icon/128x128/prompt.png" type="image/png"> 9 9 <link rel="stylesheet" href="https://aesthetic.computer/type/webfonts/berkeley-mono-variable.css"> ··· 150 150 <body> 151 151 152 152 <header> 153 - <h1>jeffrey<span class="accent">·</span>platter</h1> 153 + <h1><a href="../" style="color:inherit;text-decoration:none;">platter</a><span class="accent">·</span>jeffrey's instagram</h1> 154 154 <div class="sub"> 155 155 confirmed-self selfie corpus from 156 156 <a href="https://www.instagram.com/whistlegraph/" target="_blank">@whistlegraph</a>, ··· 180 180 </div> 181 181 182 182 <footer> 183 + <a href="../">all platters</a> 184 + &nbsp;·&nbsp; 183 185 <a href="https://github.com/digitpain/aesthetic-computer/blob/main/papers/jeffrey-platter/README.md">jeffrey-platter README</a> 184 186 &nbsp;·&nbsp; 185 - <a href="../">papers.aesthetic.computer</a> 187 + <a href="../../">papers.aesthetic.computer</a> 186 188 </footer> 187 189 188 190 <script type="module">
system/public/papers.aesthetic.computer/jeffrey/manifest.json system/public/papers.aesthetic.computer/platter/jeffrey/manifest.json
+149
system/public/papers.aesthetic.computer/platter/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>platter · papers.aesthetic.computer</title> 7 + <meta name="description" content="Per-platter dashboards for Aesthetic Computer research material — visual indexes that complement the textual platter READMEs in papers/."> 8 + <link rel="icon" href="https://aesthetic.computer/icon/128x128/prompt.png" type="image/png"> 9 + <link rel="stylesheet" href="https://aesthetic.computer/type/webfonts/berkeley-mono-variable.css"> 10 + <style> 11 + :root { 12 + --bg: #111; --bg2: #1a1a1a; --fg: #ddd; --fg2: #888; 13 + --accent: #ff6b9d; --accent2: #4ecdc4; --gold: #ffd93d; 14 + --border: #333; --hover: #222; 15 + } 16 + * { margin: 0; padding: 0; box-sizing: border-box; } 17 + html, body { background: var(--bg); color: var(--fg); } 18 + body { 19 + font-family: 'Berkeley Mono Variable', ui-monospace, monospace; 20 + font-size: 13px; line-height: 1.5; 21 + min-height: 100vh; 22 + } 23 + a { color: var(--accent2); text-decoration: none; } 24 + a:hover { color: var(--accent); } 25 + 26 + header { 27 + padding: 24px 16px 16px; 28 + border-bottom: 1px solid var(--border); 29 + } 30 + h1 { 31 + font-size: 20px; font-weight: normal; letter-spacing: 5px; 32 + margin-bottom: 6px; 33 + } 34 + h1 .accent { color: var(--accent); } 35 + .sub { font-size: 11px; color: var(--fg2); max-width: 640px; } 36 + 37 + main { 38 + max-width: 880px; 39 + margin: 0 auto; 40 + padding: 16px; 41 + display: grid; 42 + grid-template-columns: 1fr; 43 + gap: 16px; 44 + } 45 + @media (min-width: 700px) { 46 + main { grid-template-columns: 1fr 1fr; } 47 + } 48 + 49 + .platter-card { 50 + display: block; 51 + background: var(--bg2); border: 1px solid var(--border); 52 + overflow: hidden; 53 + transition: border-color 0.1s, transform 0.1s; 54 + color: inherit; 55 + } 56 + .platter-card:hover { border-color: var(--accent2); } 57 + .platter-card:active { transform: scale(0.99); } 58 + 59 + .platter-card .cover { 60 + width: 100%; aspect-ratio: 16/9; 61 + background: #000; 62 + display: flex; align-items: center; justify-content: center; 63 + overflow: hidden; 64 + } 65 + .platter-card .cover img { 66 + width: 100%; height: 100%; object-fit: cover; display: block; 67 + } 68 + .platter-card .cover.coming-soon { 69 + color: var(--fg2); 70 + font-size: 11px; letter-spacing: 2px; text-transform: uppercase; 71 + } 72 + 73 + .platter-card .info { 74 + padding: 12px 14px; 75 + display: flex; flex-direction: column; gap: 6px; 76 + } 77 + .platter-card .info h2 { 78 + font-size: 14px; font-weight: normal; letter-spacing: 2px; 79 + color: var(--fg); 80 + } 81 + .platter-card .info .meta { 82 + font-size: 11px; color: var(--fg2); 83 + } 84 + .platter-card .info .desc { 85 + font-size: 12px; color: var(--fg); 86 + line-height: 1.5; 87 + } 88 + .platter-card.disabled { 89 + opacity: 0.5; pointer-events: none; 90 + } 91 + 92 + footer { 93 + padding: 24px 16px; text-align: center; font-size: 11px; 94 + color: var(--fg2); border-top: 1px solid var(--border); 95 + margin-top: 32px; 96 + } 97 + footer a { color: var(--fg2); } 98 + footer a:hover { color: var(--accent2); } 99 + </style> 100 + </head> 101 + <body> 102 + 103 + <header> 104 + <h1>platter<span class="accent">·</span>index</h1> 105 + <div class="sub"> 106 + visual dashboards over Aesthetic Computer's research platters — the source-material collections behind the 107 + <a href="../">papers</a>. each platter is an evolving body of references, captures, and notes; these pages are the navigable surface. 108 + </div> 109 + </header> 110 + 111 + <main> 112 + 113 + <a class="platter-card" href="./jeffrey/"> 114 + <div class="cover"> 115 + <img src="https://assets.aesthetic.computer/jeffreys/whistlegraph/DFtJH9uzU3C.jpg" alt="" loading="lazy"> 116 + </div> 117 + <div class="info"> 118 + <h2>jeffrey's instagram</h2> 119 + <div class="meta">457 confirmed-self selfies · 2014–2026 · face-matched + GPT-4o described</div> 120 + <div class="desc"> 121 + the curated archive of jeffrey alan scudder's selfies from 122 + <a href="https://www.instagram.com/whistlegraph/" target="_blank">@whistlegraph</a>, 123 + cross-matched against the AV studio shoot for identity and tagged per-image with subject, expression, pose, and tags. 124 + </div> 125 + </div> 126 + </a> 127 + 128 + <div class="platter-card disabled"> 129 + <div class="cover coming-soon">whistlegraph · soon</div> 130 + <div class="info"> 131 + <h2>whistlegraph</h2> 132 + <div class="meta">drawing-while-singing scores · 2019–2026</div> 133 + <div class="desc"> 134 + index of the whistlegraph corpus — pieces, scores, public showings, press, fan submissions. textual platter exists at 135 + <a href="https://github.com/digitpain/aesthetic-computer/blob/main/papers/whistlegraph-platter/README.md">papers/whistlegraph-platter</a>. 136 + </div> 137 + </div> 138 + </div> 139 + 140 + </main> 141 + 142 + <footer> 143 + <a href="../">papers.aesthetic.computer</a> 144 + &nbsp;·&nbsp; 145 + <a href="https://github.com/digitpain/aesthetic-computer/tree/main/papers">papers/ on github</a> 146 + </footer> 147 + 148 + </body> 149 + </html>