Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

blank: responsive widescreen layout for OG thumbnail / 16:9 views

Detect widescreen (w/h > 1.5) and adjust layout to prevent element overlap:
- Smaller title text, tighter top spacing
- Wider description bounds (fewer wrap lines)
- Laptop 3D pushed lower to sit below buy button
- Bottom buttons in horizontal row instead of vertical stack

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

+78 -47
+78 -47
system/public/aesthetic.computer/disks/blank.mjs
··· 185 185 frame += 1; 186 186 const w = screen.width; 187 187 const h = screen.height; 188 + const wide = w / h > 1.5; 188 189 189 190 // Theme colors 190 191 const bg = isDark ? [14, 12, 20] : [232, 228, 238]; ··· 237 238 238 239 // Compute button zone height 239 240 const btnH = buyBtn ? buyBtn.height + 6 : 26; 240 - const uiZoneH = btnH * 2 + 20; 241 + const uiZoneH = wide ? btnH + 16 : btnH * 2 + 20; 241 242 const contentBottom = h - uiZoneH - 20; 242 243 243 244 // 💻 Wireframe laptop (turntable swivel) 244 245 { 245 246 const cx = floor(w / 2); 246 - const cy = floor(h * 0.54); // slightly below center for optical balance 247 + const cy = floor(h * (wide ? 0.68 : 0.54)); // push lower in widescreen to avoid overlap 247 248 // Keep laptop fully within viewport (account for rotation + lid overshoot) 248 249 const availW = w * 0.42; 249 250 const availH = (contentBottom - 20) * 0.32; ··· 725 726 } 726 727 727 728 // Title + product description (painted on top of laptop wireframe) 728 - ink(sr, sg, sb, shadowAlpha).write("AC Blank Laptop", { center: "x", y: 30 + shadowOff, size: 2, screen }); 729 - ink(fg).write("AC Blank Laptop", { center: "x", y: 30, size: 2, screen }); 729 + const titleY = wide ? 10 : 30; 730 + const titleSize = wide ? 1 : 2; 731 + ink(sr, sg, sb, shadowAlpha).write("AC Blank Laptop", { center: "x", y: titleY + shadowOff, size: titleSize, screen }); 732 + ink(fg).write("AC Blank Laptop", { center: "x", y: titleY, size: titleSize, screen }); 730 733 // Measure wrapped description to position buy button below it 731 - const descBounds = floor(w * 0.85); 732 - const descY = 54; 734 + const descBounds = floor(w * (wide ? 0.95 : 0.85)); 735 + const descY = wide ? titleY + 12 : 54; 733 736 const descBox = text.box(DESCRIPTION_PLAIN, { center: "x", y: descY, screen }, descBounds); 734 737 const descBottom = descY + (descBox ? descBox.box.height : charH); 735 738 ··· 800 803 $.needsPaint(); 801 804 } 802 805 803 - // Bottom-left link stack with animated question labels 804 - const labelGap = 2; 805 - const labelH = charH + labelGap; 806 - const btnGap = 4; 807 - const t = frame * 0.04; 808 - 809 - // Animated label helper — cycling color shadow with wobble 810 - const paintLabel = (label, bottomY, phase) => { 811 - const lx = 6; 812 - const ly = h - bottomY - charH; 813 - const wobble = floor(sin(t + phase) * 1.5); 814 - ink(sr, sg, sb, shadowAlpha).write(label, { x: lx + shadowOff, y: ly + shadowOff + wobble, screen }); 815 - ink(fg).write(label, { x: lx, y: ly + wobble, screen }); 816 - }; 817 - 818 - // Stack from bottom: Why!? + paper, What!? + manual, How!? + os 819 - let stackY = 20; 820 - 821 - // --- Why!? + Paper --- 806 + // Color schemes for bottom link buttons 822 807 const paperScheme = isDark 823 808 ? [[25, 20, 20], [200, 140, 80], [240, 190, 130]] 824 809 : [[240, 230, 220], [140, 80, 30], [100, 55, 15]]; 825 810 const paperHover = isDark 826 811 ? [[35, 28, 28], [240, 180, 100], [255, 210, 150]] 827 812 : [[235, 222, 210], [160, 100, 40], [120, 70, 20]]; 828 - if (paperBtn) { 829 - paperBtn.reposition({ x: 6, bottom: stackY, screen }, "PLORK'ing the Planet"); 830 - paperBtn.paint($btn, paperScheme, paperHover); 831 - stackY += paperBtn.height + btnGap; 832 - } 833 - paintLabel("Why!?", stackY, 0); 834 - stackY += labelH; 835 - 836 - // --- What!? + Manual --- 837 813 const manualScheme = isDark 838 814 ? [[20, 20, 30], [100, 140, 200], [160, 190, 240]] 839 815 : [[220, 225, 240], [50, 70, 140], [30, 50, 100]]; 840 816 const manualHover = isDark 841 817 ? [[30, 30, 45], [140, 180, 240], [200, 220, 255]] 842 818 : [[210, 215, 235], [40, 60, 160], [20, 40, 120]]; 843 - if (manualBtn) { 844 - manualBtn.reposition({ x: 6, bottom: stackY, screen }, "ThinkPad 11e Yoga Manual"); 845 - manualBtn.paint($btn, manualScheme, manualHover); 846 - stackY += manualBtn.height + btnGap; 847 - } 848 - paintLabel("What!?", stackY, PI * 0.66); 849 - stackY += labelH; 850 - 851 - // --- How!? + OS --- 852 819 const osScheme = isDark 853 820 ? [[20, 25, 20], [80, 200, 120], [140, 240, 170]] 854 821 : [[225, 240, 225], [30, 120, 50], [15, 90, 30]]; 855 822 const osHover = isDark 856 823 ? [[28, 35, 28], [120, 240, 150], [180, 255, 200]] 857 824 : [[215, 235, 215], [20, 140, 60], [10, 110, 40]]; 858 - if (osBtn) { 859 - osBtn.reposition({ x: 6, bottom: stackY, screen }, "AC Native OS"); 860 - osBtn.paint($btn, osScheme, osHover); 825 + 826 + const btnGap = 4; 827 + const t = frame * 0.04; 828 + 829 + if (wide) { 830 + // Horizontal bottom layout for widescreen — no labels, just buttons in a row 831 + const colW = floor(w / 3); 832 + const btnBottom = 4; 833 + const charW = 6, btnPad = 8; 834 + const estW = (label) => label.length * charW + btnPad; 835 + 836 + if (paperBtn) { 837 + const label = "PLORK'ing the Planet"; 838 + const bw = estW(label); 839 + paperBtn.reposition({ x: floor(colW * 0.5 - bw / 2), bottom: btnBottom, screen }, label); 840 + paperBtn.paint($btn, paperScheme, paperHover); 841 + } 842 + if (manualBtn) { 843 + const label = "ThinkPad 11e Yoga Manual"; 844 + const bw = estW(label); 845 + manualBtn.reposition({ x: floor(colW * 1.5 - bw / 2), bottom: btnBottom, screen }, label); 846 + manualBtn.paint($btn, manualScheme, manualHover); 847 + } 848 + if (osBtn) { 849 + const label = "AC Native OS"; 850 + const bw = estW(label); 851 + osBtn.reposition({ x: floor(colW * 2.5 - bw / 2), bottom: btnBottom, screen }, label); 852 + osBtn.paint($btn, osScheme, osHover); 853 + } 854 + } else { 855 + // Vertical stack layout for portrait/square 856 + const labelGap = 2; 857 + const labelH = charH + labelGap; 858 + 859 + const paintLabel = (label, bottomY, phase) => { 860 + const lx = 6; 861 + const ly = h - bottomY - charH; 862 + const wobble = floor(sin(t + phase) * 1.5); 863 + ink(sr, sg, sb, shadowAlpha).write(label, { x: lx + shadowOff, y: ly + shadowOff + wobble, screen }); 864 + ink(fg).write(label, { x: lx, y: ly + wobble, screen }); 865 + }; 866 + 867 + let stackY = 20; 868 + 869 + // --- Why!? + Paper --- 870 + if (paperBtn) { 871 + paperBtn.reposition({ x: 6, bottom: stackY, screen }, "PLORK'ing the Planet"); 872 + paperBtn.paint($btn, paperScheme, paperHover); 873 + stackY += paperBtn.height + btnGap; 874 + } 875 + paintLabel("Why!?", stackY, 0); 876 + stackY += labelH; 877 + 878 + // --- What!? + Manual --- 879 + if (manualBtn) { 880 + manualBtn.reposition({ x: 6, bottom: stackY, screen }, "ThinkPad 11e Yoga Manual"); 881 + manualBtn.paint($btn, manualScheme, manualHover); 882 + stackY += manualBtn.height + btnGap; 883 + } 884 + paintLabel("What!?", stackY, PI * 0.66); 885 + stackY += labelH; 886 + 887 + // --- How!? + OS --- 888 + if (osBtn) { 889 + osBtn.reposition({ x: 6, bottom: stackY, screen }, "AC Native OS"); 890 + osBtn.paint($btn, osScheme, osHover); 891 + } 892 + paintLabel("How!?", stackY + (osBtn ? osBtn.height + btnGap : 0), PI * 1.33); 861 893 } 862 - paintLabel("How!?", stackY + (osBtn ? osBtn.height + btnGap : 0), PI * 1.33); 863 894 } 864 895 865 896 function act({ event: e, screen, jump, sound, ui, api }) {