Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

notepat-remote: case-doors unfocused overlay

Replaces the red-X/"click to activate keys" unfocused overlay with an
instrument-case metaphor: when focus is lost, two warm-black panels
slide in from the left and right edges and meet at a chrome seam in
the center; when focus returns, the panels retract to reveal the
pads. doorPhase lerps smoothly each paint so both transitions
animate.

Bezel + octave divider are now drawn unconditionally (no longer
gated on \`focused\`) so the "case frame" stays visible during the
door animation — the panels slide *inside* the case.

+35 -41
+35 -41
system/public/aesthetic.computer/disks/notepat-remote.mjs
··· 101 101 // most recent note (darkened) and decays back to idle when nothing is held. 102 102 const bgColor = [4, 2, 6]; 103 103 104 + // Case-doors animation. 0 = fully open (focused, pads visible), 1 = 105 + // fully closed (unfocused, dark panels meet at a chrome seam in the 106 + // middle). Lerps each paint toward the focus target. 107 + let doorPhase = 0; 108 + 104 109 // Live track color, pushed in by the Max patcher via 105 110 // `window.acSetLiveTrackColor(int)` once `live.observer` resolves the 106 111 // device's parent track. Null until that lands; paint falls back to the ··· 677 682 } 678 683 } 679 684 680 - // ── Octave divider + device bezel ─────────────────────────────── 681 - // Both pick up the Live track color when Max has pushed it, so the 682 - // non-pad chrome responds to the hosting track. Fallback is a warm 683 - // muted rose rather than a stark gray. 684 - if (focused) { 685 - const chrome = liveTrackColor || [150, 80, 110]; 686 - // Divider runs the full inner width (skipping the bezel columns so 687 - // the horizontal & vertical chrome meet cleanly at the corners). 685 + // ── Octave divider ────────────────────────────────────────────── 686 + // Pulled out of the focused guard so the chrome stays consistent 687 + // even during the door transition. 688 + const chrome = liveTrackColor || [150, 80, 110]; 689 + { 688 690 const barY = rowEdges[3] - octaveGap; 689 691 ink(...chrome).box(bezel, barY, W - bezel * 2, octaveGap, "fill"); 690 - // 2px bezel as two concentric 1px outlines. 691 - for (let i = 0; i < bezel; i += 1) { 692 - ink(...chrome).box(i, i, W - i * 2, H - i * 2, "outline"); 693 - } 694 692 } 695 693 696 - // ── Unfocused overlay: big red X spanning the device ───────────────── 697 - // When the mjs piece doesn't have focus, the keyboard won't drive 698 - // notes — the red X is the at-a-glance signal for that. 699 - if (!focused) { 700 - // Darken the grid so the X reads as "inactive" not "on top of art" 701 - ink(4, 0, 0, 150).box(0, 0, W, H, "fill"); 702 - 703 - // Pulsing red border — same cue scheme as before, just thinner 704 - const borderAlpha = floor(140 + blinkPhase * 115); 705 - for (let i = 0; i < 2; i += 1) { 706 - ink(255, 40, 40, borderAlpha).box(i, i, W - i * 2, H - i * 2, "outline"); 694 + // ── Case doors (unfocused overlay) ───────────────────────────── 695 + // Instrument-case metaphor: when the piece loses focus, two panels 696 + // slide in from the left and right and meet at a chrome seam. When 697 + // focused the panels retract off-screen and the pads are fully 698 + // visible. Lerps smoothly so focus transitions animate. 699 + const doorTarget = focused ? 0 : 1; 700 + doorPhase += (doorTarget - doorPhase) * 0.18; 701 + if (doorPhase > 0.01) { 702 + const halfW = floor(W / 2); 703 + const doorW = floor(halfW * doorPhase); 704 + const panel = [18, 12, 18]; // warm near-black case material 705 + // Panel bodies — slide in from each edge. 706 + ink(...panel).box(0, 0, doorW, H, "fill"); 707 + ink(...panel).box(W - doorW, 0, doorW, H, "fill"); 708 + // Inner edges of each panel get a chrome stripe that matches the 709 + // bezel/divider color — becomes a 2px center seam when closed. 710 + if (doorW > 0) { 711 + ink(...chrome).line(doorW - 1, 0, doorW - 1, H - 1); 712 + ink(...chrome).line(W - doorW, 0, W - doorW, H - 1); 707 713 } 714 + } 708 715 709 - // Thick red X from corner to corner. AC's line() is 1px, so stack 710 - // a handful of parallel lines to build a visible stroke. 711 - const xAlpha = floor(180 + blinkPhase * 75); 712 - const xThick = 2; // gives a 5px-equivalent X across (t from -2..2) 713 - for (let t = -xThick; t <= xThick; t += 1) { 714 - ink(255, 60, 60, xAlpha).line(0, t, W - 1, H - 1 + t); 715 - ink(255, 60, 60, xAlpha).line(W - 1, t, 0, H - 1 + t); 716 - } 717 - 718 - // Small hint badge at the top so the reason is readable 719 - const hint = "click to activate keys"; 720 - const hintW = hint.length * 6; 721 - const hintBoxX = floor((W - hintW) / 2) - 4; 722 - const hintBoxY = 2; 723 - ink(0, 0, 0, 200).box(hintBoxX, hintBoxY, hintW + 8, 12, "fill"); 724 - ink(255, 80, 80, borderAlpha).box(hintBoxX, hintBoxY, hintW + 8, 12, "outline"); 725 - ink(blinkOn ? 255 : 200, 140, 140) 726 - .write(hint, { x: floor((W - hintW) / 2), y: hintBoxY + 2 }); 716 + // ── Device bezel (drawn last, always on top) ───────────────────── 717 + // The "case frame" — stays visible whether or not the doors are 718 + // closed, so the device outline is always present. 719 + for (let i = 0; i < bezel; i += 1) { 720 + ink(...chrome).box(i, i, W - i * 2, H - i * 2, "outline"); 727 721 } 728 722 } 729 723
system/public/m4l/notepat.com.amxd

This is a binary file and will not be displayed.