Monorepo for Aesthetic.Computer aesthetic.computer
at main 176 lines 5.2 kB view raw view rendered
1# Git Commit Status Indicator for Prompt Curtain 2 3## Overview 4Add a visual indicator to the prompt.mjs curtain UI showing whether the deployed site is on the latest git commit or behind, with the commit hash displayed. 5 6## Visual Design 7- **Location**: Below "X HANDLES SET" text on the login curtain 8- **Font**: MatrixChunky8 (same as handles counter) 9- **Colors**: 10 - 🟢 Green (`[0, 255, 0]`) = Site is up-to-date with latest commit 11 - 🟠 Orange (`[255, 165, 0]`) = Site is behind (shows how many commits) 12- **Format Examples**: 13 - `✓ 37da247` (current) 14 - `↑ 2 behind (37da247)` (behind by 2 commits) 15 16--- 17 18## Implementation Steps 19 20### 1. Create Build-Time Version File 21**File**: `system/public/version.json` (generated at build time) 22 23Add to `netlify.toml` build command: 24```toml 25command = "rm -f public/index.html && echo '{\"commit\":\"'$COMMIT_REF'\",\"timestamp\":\"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'\"}' > public/version.json" 26``` 27 28This creates a file like: 29```json 30{"commit":"37da2475abc123...","timestamp":"2026-01-11T22:34:06Z"} 31``` 32 33### 2. Create Netlify Function: `/api/version` 34**File**: `system/netlify/functions/version.mjs` 35 36```javascript 37// Fetches latest commit from GitHub and compares to deployed version 38export default async (request) => { 39 const deployedCommit = process.env.COMMIT_REF || "unknown"; 40 41 try { 42 // Fetch latest commit from GitHub (public, no auth needed) 43 const res = await fetch( 44 "https://api.github.com/repos/whistlegraph/aesthetic-computer/commits?per_page=50", 45 { headers: { "User-Agent": "aesthetic-computer" } } 46 ); 47 const commits = await res.json(); 48 const latestCommit = commits[0]?.sha; 49 50 // Find how many commits behind 51 let behindBy = 0; 52 if (deployedCommit !== "unknown" && latestCommit) { 53 const idx = commits.findIndex(c => c.sha.startsWith(deployedCommit.slice(0, 7))); 54 behindBy = idx === -1 ? 50 : idx; // -1 means very old 55 } 56 57 const status = behindBy === 0 ? "current" : "behind"; 58 59 return Response.json({ 60 deployed: deployedCommit.slice(0, 7), 61 latest: latestCommit?.slice(0, 7), 62 status, 63 behindBy, 64 timestamp: new Date().toISOString() 65 }); 66 } catch (e) { 67 return Response.json({ 68 deployed: deployedCommit.slice(0, 7), 69 status: "unknown", 70 error: e.message 71 }); 72 } 73}; 74 75export const config = { path: "/api/version" }; 76``` 77 78### 3. Add Redirect in `netlify.toml` 79```toml 80[[redirects]] 81from = "/api/version" 82to = "/.netlify/functions/version" 83status = 200 84``` 85 86### 4. Update `prompt.mjs` 87 88#### A. Add State Variables (~line 230) 89```javascript 90let versionInfo = null; // { deployed, latest, status, behindBy } 91``` 92 93#### B. Fetch in `boot()` (~line 640, after handles fetch) 94```javascript 95// Fetch commit/version status 96const fetchVersion = async () => { 97 try { 98 const res = await fetch("/api/version"); 99 versionInfo = await res.json(); 100 needsPaint(); 101 } catch (e) { 102 console.warn("Could not fetch version info:", e); 103 } 104}; 105fetchVersion(); 106// Refresh every 5 minutes 107setInterval(fetchVersion, 5 * 60 * 1000); 108``` 109 110#### C. Render in `paint()` (~line 6081, after handles text) 111```javascript 112// Git commit status indicator 113if (versionInfo && screen.height >= 120) { 114 const versionY = handlesY + 12; 115 let versionText, versionColor; 116 117 if (versionInfo.status === "current") { 118 versionColor = [0, 255, 0]; // Green 119 versionText = `${versionInfo.deployed}`; 120 } else if (versionInfo.status === "behind") { 121 versionColor = [255, 165, 0]; // Orange 122 versionText = `${versionInfo.behindBy} behind (${versionInfo.deployed})`; 123 } else { 124 versionColor = [128, 128, 128]; // Gray for unknown 125 versionText = `? ${versionInfo.deployed || "unknown"}`; 126 } 127 128 ink(versionColor).write( 129 versionText, 130 { center: "x", y: versionY }, 131 undefined, undefined, false, "MatrixChunky8" 132 ); 133} 134``` 135 136--- 137 138## File Changes Summary 139 140| File | Change | 141|------|--------| 142| `system/netlify.toml` | Update build command to generate version.json; add redirect | 143| `system/netlify/functions/version.mjs` | New function (create) | 144| `system/public/aesthetic.computer/disks/prompt.mjs` | Add state var, boot fetch, paint render | 145 146--- 147 148## Technical Notes 149 150### GitHub API 151- **Endpoint**: `GET https://api.github.com/repos/whistlegraph/aesthetic-computer/commits` 152- **Rate limit**: 60 requests/hour unauthenticated (sufficient with 5-min polling + server-side caching) 153- **No auth required** for public repos 154 155### Netlify Environment Variables 156- `COMMIT_REF` - Full SHA of the commit being deployed (available at build/function time) 157- `BUILD_ID` - Unique build identifier 158 159### Caching Strategy 160The Netlify function can add cache headers to reduce GitHub API calls: 161```javascript 162return new Response(JSON.stringify(data), { 163 headers: { 164 "Content-Type": "application/json", 165 "Cache-Control": "public, max-age=60" // Cache for 1 minute 166 } 167}); 168``` 169 170--- 171 172## Future Enhancements 173- Click on commit hash to open GitHub commit page 174- Show commit message preview on hover 175- Add "Update available" notification badge 176- Track deploy history / show last N deploys