Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

ac-electron v0.1.39: Notepat Overlay mode (transparent floating widget)

New entry point: "Open Notepat Overlay ๐ŸชŸ" in the main tray menu.
Opens a transparent, chromeless BrowserWindow at Electron's
`screen-saver` always-on-top level and `setVisibleOnAllWorkspaces(true,
{ visibleOnFullScreen: true })` so the window floats above every other
app โ€” including fullscreen โ€” and follows the user across Spaces. Like
a macOS HUD / widget.

Companion tray toggle "Overlay: Click-Through" calls
`setIgnoreMouseEvents(true, { forward: true })` so the overlay becomes
visual-only (clicks drop through to whatever app is under it). Toggled
off, it captures clicks so notes play. Persisted to preferences.json
as `overlayClickThrough`.

New `renderer/notepat-overlay.html` is intentionally minimal โ€” just a
10px transparent drag strip and a corner resize grip, no frame, no
border, no close button. The inside webview points at
`/notepat?desktop&nogap=true&overlay=true`.

Matching piece-side change in `system/public/aesthetic.computer/disks/
notepat.mjs`: reads `?overlay=true` at boot and skips the per-frame
background wipe so the canvas stays transparent and the desktop shows
through between the piano keys.

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

+227 -5
+93
ac-electron/main.js
··· 82 82 // default). When false (new default), they behave like normal windows 83 83 // and can be ordered underneath. Toggled from the tray menu. 84 84 alwaysOnTop: false, 85 + // When the Notepat Overlay is open, should it pass mouse events 86 + // through to the app underneath (visual-only widget) or capture them 87 + // (playable floating piano)? Toggled from the tray while the overlay 88 + // is open. 89 + overlayClickThrough: false, 85 90 }; 86 91 87 92 function loadPreferences() { ··· 846 851 click: () => openNotepatWindow() 847 852 }); 848 853 854 + // Overlay variant โ€” transparent, chromeless, floats above fullscreen 855 + // apps and every Space. Click-through is a sibling checkbox that only 856 + // takes effect while the overlay is open. 857 + menuItems.push({ 858 + label: 'Open Notepat Overlay ๐ŸชŸ', 859 + click: () => openNotepatOverlayWindow() 860 + }); 861 + 862 + menuItems.push({ 863 + label: 'Overlay: Click-Through', 864 + type: 'checkbox', 865 + checked: !!preferences.overlayClickThrough, 866 + click: (item) => { 867 + preferences.overlayClickThrough = !!item.checked; 868 + savePreferences(); 869 + if (notepatOverlayWindow && !notepatOverlayWindow.isDestroyed()) { 870 + notepatOverlayWindow.setIgnoreMouseEvents(preferences.overlayClickThrough, { forward: true }); 871 + } 872 + } 873 + }); 874 + 849 875 // Always-on-top toggle. Persisted to preferences.json and applied to 850 876 // every current AC window on change; new windows inherit via their 851 877 // BrowserWindow opts. ··· 1259 1285 }); 1260 1286 1261 1287 return notepatWindow; 1288 + } 1289 + 1290 + // Open a Notepat Overlay โ€” transparent, chromeless, screen-saver-level 1291 + // window that floats above every app and follows the user across Spaces. 1292 + // Like a macOS HUD / widget. Click-through can be toggled from the tray 1293 + // so the overlay becomes visual-only (mouse events pass to whatever's 1294 + // behind it). The notepat piece reads ?overlay=true and skips its 1295 + // background wipe so the desktop shows through between keys. 1296 + let notepatOverlayWindow = null; 1297 + function openNotepatOverlayWindow() { 1298 + if (notepatOverlayWindow && !notepatOverlayWindow.isDestroyed()) { 1299 + notepatOverlayWindow.show(); 1300 + notepatOverlayWindow.focus(); 1301 + return notepatOverlayWindow; 1302 + } 1303 + 1304 + const baseUrl = startInDevMode ? 'http://localhost:8888' : 'https://aesthetic.computer'; 1305 + const display = screen.getPrimaryDisplay().workAreaSize; 1306 + const w = 340, h = 180; 1307 + 1308 + notepatOverlayWindow = new BrowserWindow({ 1309 + width: w, 1310 + height: h, 1311 + x: display.width - w - 24, 1312 + y: display.height - h - 24, 1313 + minWidth: 220, 1314 + minHeight: 130, 1315 + title: 'Notepat Overlay', 1316 + frame: false, 1317 + transparent: true, 1318 + hasShadow: false, 1319 + resizable: true, 1320 + skipTaskbar: true, 1321 + backgroundColor: '#00000000', 1322 + type: process.platform === 'darwin' ? 'panel' : undefined, 1323 + webPreferences: { 1324 + nodeIntegration: true, 1325 + contextIsolation: false, 1326 + webviewTag: true, 1327 + backgroundThrottling: false, 1328 + }, 1329 + }); 1330 + 1331 + // screen-saver level sits above fullscreen apps on macOS. Follow the 1332 + // user across Spaces (and into fullscreen Spaces) so the overlay is 1333 + // actually always visible. 1334 + notepatOverlayWindow.setAlwaysOnTop(true, 'screen-saver'); 1335 + notepatOverlayWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); 1336 + 1337 + if (preferences.overlayClickThrough) { 1338 + notepatOverlayWindow.setIgnoreMouseEvents(true, { forward: true }); 1339 + } 1340 + 1341 + notepatOverlayWindow.loadFile( 1342 + getAppPath('renderer/notepat-overlay.html'), 1343 + { query: { piece: 'notepat', base: baseUrl } }, 1344 + ); 1345 + 1346 + const windowId = windowIdCounter++; 1347 + windows.set(windowId, { window: notepatOverlayWindow, mode: 'notepat-overlay' }); 1348 + 1349 + notepatOverlayWindow.on('closed', () => { 1350 + windows.delete(windowId); 1351 + notepatOverlayWindow = null; 1352 + }); 1353 + 1354 + return notepatOverlayWindow; 1262 1355 } 1263 1356 1264 1357 // Open KidLisp window (kidlisp.com)
+2 -2
ac-electron/package-lock.json
··· 1 1 { 2 2 "name": "aesthetic-computer", 3 - "version": "0.1.38", 3 + "version": "0.1.39", 4 4 "lockfileVersion": 3, 5 5 "requires": true, 6 6 "packages": { 7 7 "": { 8 8 "name": "aesthetic-computer", 9 - "version": "0.1.38", 9 + "version": "0.1.39", 10 10 "license": "MIT", 11 11 "dependencies": { 12 12 "@electron/rebuild": "^4.0.2",
+1 -1
ac-electron/package.json
··· 1 1 { 2 2 "name": "aesthetic-computer", 3 - "version": "0.1.38", 3 + "version": "0.1.39", 4 4 "description": "Aesthetic Computer", 5 5 "homepage": "https://aesthetic.computer", 6 6 "author": "Aesthetic Computer <hi@aesthetic.computer>",
+117
ac-electron/renderer/notepat-overlay.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <title>Notepat Overlay</title> 6 + <style> 7 + * { margin: 0; padding: 0; box-sizing: border-box; } 8 + html, body { 9 + width: 100%; 10 + height: 100%; 11 + overflow: hidden; 12 + background: transparent; 13 + } 14 + 15 + /* A very subtle drag strip across the top 10px of the overlay. No 16 + visible chrome โ€” the whole window should feel like content floating 17 + directly on the desktop. Users can still grab the top edge to move 18 + the window because the strip is drag-registered. */ 19 + #drag-strip { 20 + position: absolute; 21 + top: 0; 22 + left: 0; 23 + right: 0; 24 + height: 10px; 25 + -webkit-app-region: drag; 26 + z-index: 10; 27 + background: transparent; 28 + } 29 + 30 + /* Corner resize handle (bottom-right). Small visual hint, draggable 31 + via OS-level window resize since frame:false strips the default 32 + resize corners. Electron forwards the drag to the window manager. */ 33 + #resize-grip { 34 + position: absolute; 35 + bottom: 0; 36 + right: 0; 37 + width: 14px; 38 + height: 14px; 39 + cursor: nwse-resize; 40 + /* No app-region โ€” Electron re-adds resize from frameless windows 41 + on hover over the very edge, but a visible grip helps discoverability. */ 42 + z-index: 10; 43 + opacity: 0.5; 44 + background: 45 + linear-gradient(135deg, transparent 0%, transparent 55%, 46 + rgba(255,255,255,0.6) 55%, rgba(255,255,255,0.6) 60%, 47 + transparent 60%, transparent 70%, 48 + rgba(255,255,255,0.6) 70%, rgba(255,255,255,0.6) 75%, 49 + transparent 75%, transparent 85%, 50 + rgba(255,255,255,0.6) 85%, rgba(255,255,255,0.6) 90%, 51 + transparent 90%); 52 + } 53 + @media (prefers-color-scheme: light) { 54 + #resize-grip { background: 55 + linear-gradient(135deg, transparent 0%, transparent 55%, 56 + rgba(0,0,0,0.5) 55%, rgba(0,0,0,0.5) 60%, 57 + transparent 60%, transparent 70%, 58 + rgba(0,0,0,0.5) 70%, rgba(0,0,0,0.5) 75%, 59 + transparent 75%, transparent 85%, 60 + rgba(0,0,0,0.5) 85%, rgba(0,0,0,0.5) 90%, 61 + transparent 90%); 62 + } 63 + } 64 + 65 + #note-webview { 66 + position: absolute; 67 + inset: 0; 68 + width: 100%; 69 + height: 100%; 70 + border: none; 71 + background: transparent; 72 + } 73 + </style> 74 + </head> 75 + <body> 76 + <div id="drag-strip"></div> 77 + <webview 78 + id="note-webview" 79 + src="about:blank" 80 + allowpopups 81 + preload="../webview-preload.js" 82 + transparent="true" 83 + ></webview> 84 + <div id="resize-grip"></div> 85 + <script> 86 + const { ipcRenderer } = require('electron'); 87 + const webviewEl = document.getElementById('note-webview'); 88 + 89 + const params = new URLSearchParams(location.search); 90 + const base = params.get('base') || 'https://aesthetic.computer'; 91 + const piece = (params.get('piece') || 'notepat').replace(/^\/+/, ''); 92 + // overlay=true tells the notepat piece to skip its background wipe 93 + // so the window transparency actually shows through. 94 + webviewEl.src = `${base}/${piece}?desktop&nogap=true&overlay=true`; 95 + 96 + window.addEventListener('keydown', (e) => { 97 + const mod = navigator.platform.toLowerCase().includes('mac') ? e.metaKey : e.ctrlKey; 98 + if (mod && e.key.toLowerCase() === 'w') { 99 + e.preventDefault(); 100 + window.close(); 101 + } 102 + }); 103 + 104 + webviewEl.addEventListener('did-navigate', (e) => { 105 + try { 106 + const pathname = new URL(e.url).pathname; 107 + const current = pathname.replace(/^\//, '').split('/')[0] || piece; 108 + ipcRenderer.invoke('set-current-piece', current); 109 + } catch (_) {} 110 + }); 111 + 112 + webviewEl.addEventListener('did-fail-load', (e) => { 113 + console.warn('[notepat-overlay] webview failed to load:', e.errorCode, e.errorDescription, e.validatedURL); 114 + }); 115 + </script> 116 + </body> 117 + </html>
+14 -2
system/public/aesthetic.computer/disks/notepat.mjs
··· 1065 1065 1066 1066 // ๐ŸŽน DAW sync state (Ableton Live via M4L) 1067 1067 let dawMode = false; // Set from query param OR auto-detected from DAW messages 1068 + let overlayMode = false; // ?overlay=true โ€” transparent bg for electron overlay window 1068 1069 let dawAutoDetected = false; // True if we auto-detected DAW mode from messages 1069 1070 let dawSynced = false; // True when receiving valid DAW data 1070 1071 let dawBpm = null; // BPM from DAW ··· 1439 1440 // ๐ŸŽน Check if we're in DAW mode (loaded from Ableton M4L) 1440 1441 dawMode = query?.daw === "1" || query?.daw === 1 || query?.daw === true; 1441 1442 console.log("๐ŸŽน Notepat: dawMode =", dawMode, "query.daw =", query?.daw, typeof query?.daw); 1443 + 1444 + // ?overlay=true โ†’ transparent-bg mode for the ac-electron Notepat Overlay 1445 + // window. Skip background wipes in paint() so the desktop shows through. 1446 + overlayMode = query?.overlay === "true" || query?.overlay === true || query?.overlay === "1"; 1447 + if (overlayMode) console.log("๐ŸชŸ Notepat: overlay mode โ€” skipping background wipes"); 1442 1448 1443 1449 // Also check if we already have DAW data (survives hot reload) 1444 1450 if (!dawMode && sound.daw?.bpm) { ··· 3095 3101 sidePanelWidth: autopatConfig.sidePanelWidth, 3096 3102 }).layout; 3097 3103 3098 - // ๐ŸŽจ KidLisp visualization โ€” bounded to available space above/between buttons 3099 - if (kidlispBgEnabled && kidlispBackground && !paintPictureOverlay) { 3104 + // ๐ŸชŸ Overlay mode: skip background fill entirely so the electron window's 3105 + // transparency lets the desktop show through between keys. All branches 3106 + // below (kidlisp viz, fullscreen visualizer, recital, plain bg) paint a 3107 + // base background each frame โ€” none of them are wanted here. 3108 + if (overlayMode) { 3109 + // Also skip the background visualizers (viz, recital, kidlisp viz) โ€” 3110 + // overlay mode is strictly the piano keys + labels on transparent. 3111 + } else if (kidlispBgEnabled && kidlispBackground && !paintPictureOverlay) { 3100 3112 wipe(bg); // Base background first 3101 3113 const klY = OS_BAR_BOTTOM; 3102 3114 const klBottom = earlyLayout.topButtonY;