11---
22import { getCollection , getEntry } from ' astro:content' ;
3+ import Controls from ' ./gameboy/Controls.astro' ;
4+ import Hud from ' ./gameboy/Hud.astro' ;
5+ import PowerSwitch from ' ./gameboy/PowerSwitch.astro' ;
6+ import TabsHeader from ' ./gameboy/TabsHeader.astro' ;
7+ import TurnButton from ' ./gameboy/TurnButton.astro' ;
8+ import LinksTab from ' ./gameboy/tabs/LinksTab.astro' ;
9+ import AboutTab from ' ./gameboy/tabs/AboutTab.astro' ;
10+ import HelpTab from ' ./gameboy/tabs/HelpTab.astro' ;
311import ' ../styles/hit-areas.css' ;
412import ' ../styles/console-layout.css' ;
513import ' ../styles/view-transitions.css' ;
614import ' ../styles/screen.css' ;
715import ' ../styles/controls.css' ;
816import ' ../styles/responsive.css' ;
917
10- // Fetch Data
1118const allLinks = await getCollection (' links' );
1219const sortedLinks = allLinks .sort ((a , b ) => a .data .order - b .data .order );
1320const aboutEntry = await getEntry (' sections' , ' about' );
14- const aboutText = aboutEntry && aboutEntry .body ? aboutEntry .body .trim () : " " ;
15-
16- // Map icons to SVGs (Inline for performance)
17- // --- 60Hz Quantization Heartbeat ---
18- // Game Boy Original Refresh Rate: ~59.73Hz (~16.74ms)
19- // We align all animations to this frame time to prevent jitter.
20- // Quantized to 60fps (~16.74ms)
21-
22- const iconSvgs = {
23- instagram: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>`,
24- twitter: ` <svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg> ` ,
25- linkedin: ` <svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg> ` ,
26- goodreads: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.43 23.995c-3.608-.208-6.274-2.077-6.448-5.078.695.007 1.375-.013 2.07-.006.224 1.342 1.065 2.43 2.683 3.026 1.558.496 3.085.393 4.571-.322 1.298-.768 1.914-1.795 2.15-3.219H16.4c-.023.278-.074.958-.106 1.09-1.284 2.088-3.587 3.226-6.117 3.226-.013 0-.026-.003-.04-.003-.353 0-.704-.016-1.053-.035l.346-18.68h2.07l-.03 5.59c.672-.804 1.604-1.378 2.768-1.572 1.463-.242 2.851.04 4.063.88 1.227.85 2.02 2.023 2.398 3.453.4 1.514.357 3.013-.173 4.47-.574 1.57-1.634 2.717-3.189 3.396-1.098.479-2.253.62-3.443.505-1.118-.108-2.109-.515-2.971-1.22-.013.01-.02.018-.028.028-.51.698-1.19 1.203-2.024 1.47-.83.26-1.68.28-2.526.082-1.234-.286-2.21-.936-2.86-2.028-.63-1.063-.804-2.211-.514-3.4.28-1.144.91-2.072 1.89-2.732.964-.65 2.033-.877 3.18-.688 1.13.186 2.07.707 2.772 1.58l.017-.025.03-5.595-2.07.018-.031 7.59c-.014.71-.245 1.262-.736 1.682-.49.42-1.086.612-1.783.574-.697-.038-1.273-.306-1.714-.791-.442-.486-.632-1.065-.58-1.775.06-.817.386-1.47.987-1.96.568-.463 1.227-.672 1.976-.623.6.039 1.105.232 1.534.597-.004-.043-.006-.082-.007-.123-.01-.173-.02-.345-.03-.517-.03-.535-.06-1.07-.09-1.605-.03-.535-.06-1.07-.089-1.603-.018-.313-.035-.627-.053-.94l-2.07.006.034 1.787c-.672-.91-1.578-1.466-2.768-1.628-1.24-.17-2.392.08-3.456.73-1.3.798-2.114 1.955-2.43 3.457-.346 1.647-.02 3.166.93 4.483.933 1.293 2.206 2.047 3.81 2.217z"/></svg>`,
27- email: ` <svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z"/></svg> ` ,
28- playstation: ` <svg viewBox="0 0 24 24" fill="currentColor"><path d="M8.985 2.596v17.548l3.915 1.261V6.688c0-.69.304-1.151.794-.991.636.181.76.814.76 1.505v5.876c2.441 1.193 4.362-.002 4.362-3.153 0-3.237-1.126-4.675-4.438-5.827-1.307-.448-3.728-1.186-5.393-1.502zm5.728 15.029-9.732-3.326c-1.085-.353-1.231-.787-.326-1.001.83-.197 2.334-.149 3.433.197l4.556 1.621v-2.539l-.259-.091c-1.449-.494-2.983-.724-4.572-.624-2.072.16-4.036.781-4.036.781-2.129.738-2.386 1.787-1.129 2.604 1.109.712 3.018 1.248 5.328 1.536v2.842l1.607-.561s-3.236-1.164-4.418-1.6c-.96-.358-1.101-.788-.31-1.004.731-.199 2.074-.196 3.396.161l4.462 1.593v2.411z"/></svg> ` ,
29- github: ` <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg> `
30- };
21+ const aboutText = aboutEntry && aboutEntry .body ? aboutEntry .body .trim () : ' ' ;
3122---
3223
3324<main class =" console" >
3425 <div class =" console-body" >
35-
36- <div class =" power-switch-container" >
37- <div class =" switch-track" id =" power-switch" role =" switch" aria-checked =" true" tabindex =" 0" >
38- <div class =" switch-handle" ></div >
39- </div >
40- </div >
41-
42- <div class =" left-controls" >
43- <div class =" dpad-area" >
44- <div class =" dpad" >
45- <button id =" up" class =" dpad-btn dpad-up" aria-label =" Up" ></button >
46- <button id =" right" class =" dpad-btn dpad-right" aria-label =" Right" ></button >
47- <button id =" down" class =" dpad-btn dpad-down" aria-label =" Down" ></button >
48- <button id =" left" class =" dpad-btn dpad-left" aria-label =" Left" ></button >
49- <div class =" dpad-center" ></div >
50- </div >
51- </div >
52- </div >
26+ <PowerSwitch />
27+ <Controls />
5328
5429 <section class =" screen-lens" >
5530 <div class =" power-indicator" >
@@ -68,62 +43,23 @@ const iconSvgs = {
6843 </div >
6944 </div >
7045
71- <div class =" top-hud" id =" top-hud" aria-label =" Status HUD" >
72- <div class =" hud-item" id =" hud-time" >--:--</div >
73- <div class =" hud-item" id =" hud-date" >-- ---</div >
74- <div class =" hud-item mute-icon" id =" hud-mute" style =" display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;position:relative;top:0.1em;visibility:hidden" aria-label =" Muted" >
75- <svg viewBox =" 0 0 24 24" fill =" currentColor" width =" 1em" height =" 1em" style =" display:block;margin:auto;" >
76- <path d =" M16.5 12c0-1.77-1.02-3.29-2.5-4.03V5a1 1 0 0 0-1.62-.78l-3.38 2.78H5a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h4l3.38 2.78A1 1 0 0 0 13 19v-2.97c1.48-.74 2.5-2.26 2.5-4.03zm-2.5 2.03V17l-3.38-2.78A1 1 0 0 0 9 13H5v-6h4a1 1 0 0 0 .62-.22L13 5v2.97c-1.48.74-2.5 2.26-2.5 4.03s1.02 3.29 2.5 4.03zM19.78 4.22a.75.75 0 0 0-1.06 0l-14 14a.75.75 0 1 0 1.06 1.06l14-14a.75.75 0 0 0 0-1.06z" />
77- </svg >
78- </div >
79- </div >
80-
46+ <Hud />
47+
8148 <div class =" battery" id =" battery-indicator" >
8249 <div class =" battery-level" id =" battery-level" ></div >
8350 </div >
8451
85- <div class =" tabs-header" role =" tablist" aria-label =" Profile sections" >
86- <button type =" button" class =" tab-indicator active" id =" tab-ind-0" role =" tab" aria-selected =" true" aria-controls =" tab-0" >LINKS</button >
87- <button type =" button" class =" tab-indicator" id =" tab-ind-1" role =" tab" aria-selected =" false" aria-controls =" tab-1" >ABOUT</button >
88- <button type =" button" class =" tab-indicator" id =" tab-ind-2" role =" tab" aria-selected =" false" aria-controls =" tab-2" >HELP</button >
89- </div >
52+ <TabsHeader />
9053
9154 <div class =" tab-container" >
9255 <div class =" tab-content active" id =" tab-0" role =" tabpanel" aria-labelledby =" tab-ind-0" >
93- <nav class =" links" aria-label =" Social links" >
94- { sortedLinks .map ((link : any , index : number ) => (
95- <a href = { link .data .url }
96- target = { link .data .url .startsWith (' mailto' ) ? ' _self' : ' _blank' }
97- rel = { link .data .url .startsWith (' mailto' ) ? ' ' : ' noopener noreferrer' }
98- class = { ` social-link ${link .data .icon } ${index === 0 ? ' active' : ' ' } ` }
99- data-index = { index } >
100- <Fragment set :html = { iconSvgs [link .data .icon as keyof typeof iconSvgs ] || ' ' } />
101- <span class = " link-text" >{ link .data .title } </span >
102- </a >
103- ))}
104- </nav >
56+ <LinksTab links ={ sortedLinks } />
10557 </div >
106- <!-- Pass text to data attribute for JS to find -->
10758 <div class =" tab-content" id =" tab-1" role =" tabpanel" aria-labelledby =" tab-ind-1" hidden >
108- <div class =" about-text" id =" about-text-content" data-text ={ aboutText } >
109- <!-- Text will be typed here by JS -->
110- </div >
59+ <AboutTab aboutText ={ aboutText } />
11160 </div >
11261 <div class =" tab-content" id =" tab-2" role =" tabpanel" aria-labelledby =" tab-ind-2" hidden >
113- <section class =" help-panel" aria-label =" Controls and secrets" >
114- <h3 >KEYMAP</h3 >
115- <ul >
116- <li ><span >ARROWS / WASD</span ><span >NAVIGATE</span ></li >
117- <li ><span >A / ENTER / Z</span ><span >OPEN LINK</span ></li >
118- <li ><span >B / X</span ><span >SWITCH THEME</span ></li >
119- <li ><span >SELECT / Q</span ><span >SWITCH THEME</span ></li >
120- <li ><span >START</span ><span >OPEN HELP TAB</span ></li >
121- <li ><span >LEFT / RIGHT</span ><span >CHANGE TAB</span ></li >
122- <li ><span >M</span ><span >MUTE AUDIO</span ></li >
123- <li ><span >H</span ><span >OPEN HELP TAB</span ></li >
124- </ul >
125- <p class =" secret-hint" >SECRET: U U D D L R L R B A</p >
126- </section >
62+ <HelpTab />
12763 </div >
12864 </div >
12965
@@ -134,28 +70,6 @@ const iconSvgs = {
13470 <div class =" logo" >GAME BOY <span class =" advance" >ADVANCE</span ></div >
13571 </section >
13672
137- <div class =" right-controls" >
138- <div class =" action-buttons" >
139- <div class =" btn-group group-a" >
140- <button type =" button" class =" btn-a" id =" btn-a" >A</button >
141- </div >
142- <div class =" btn-group group-b" >
143- <button type =" button" class =" btn-b" id =" btn-b" >B</button >
144- </div >
145- </div >
146- </div >
147-
148- <div class =" meta-controls" >
149- <div class =" meta-group" >
150- <button class =" pill-btn select" id =" btn-select" aria-label =" Select" ></button >
151- <span class =" pill-label label-select" >SELECT</span >
152- </div >
153- <div class =" meta-group" >
154- <button class =" pill-btn start" id =" btn-start" aria-label =" Start" ></button >
155- <span class =" pill-label label-start" >START</span >
156- </div >
157- </div >
158-
15973 <div class =" speaker" aria-hidden =" true" >
16074 <span style =" view-transition-name: s1" ></span >
16175 <span style =" view-transition-name: s2" ></span >
@@ -166,15 +80,9 @@ const iconSvgs = {
16680 </div >
16781 </div >
16882</main >
169- <div class =" app-turn-control" >
170- <button type =" button" class =" app-btn turn" id =" btn-turn" aria-label =" Toggle Layout" >
171- <svg viewBox =" 0 0 24 24" class =" turn-icon" >
172- <path d =" M16.48 2.52c3.27 1.55 5.61 4.72 5.97 8.48h1.5C23.44 4.84 18.29 0 12 0l-1 0v6l5.48-3.48zM4.93 1.25c-2.72 1.96-4.59 5.05-4.86 8.75h1.52c.26-3.13 1.83-5.75 4.14-7.41l-0.8-1.34zM1.55 13H0.05C0.38 18.66 4.92 23.16 10.5 23.5L10.5 22c-4.75-0.3-8.58-4.14-8.95-9zM19.93 13h1.52c-0.27 3.7-2.14 6.79-4.86 8.75l-0.8-1.34c2.31-1.66 3.88-4.28 4.14-7.41zM2 13h2c0-4.42 3.58-8 8-8s8 3.58 8 8h2c0-5.52-4.48-10-10-10S2 7.48 2 13z" fill =" currentColor" />
173- </svg >
174- </button >
175- </div >
83+
84+ <TurnButton />
17685
17786<script >
17887 import '../lib/init';
179-
18088</script >
0 commit comments