Skip to content

Commit c49e3b6

Browse files
committed
Refactor gameboy UI and harden power-off controls
1 parent 28ecce9 commit c49e3b6

29 files changed

Lines changed: 900 additions & 688 deletions

src/components/GameBoy.astro

Lines changed: 19 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,30 @@
11
---
22
import { 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';
311
import '../styles/hit-areas.css';
412
import '../styles/console-layout.css';
513
import '../styles/view-transitions.css';
614
import '../styles/screen.css';
715
import '../styles/controls.css';
816
import '../styles/responsive.css';
917
10-
// Fetch Data
1118
const allLinks = await getCollection('links');
1219
const sortedLinks = allLinks.sort((a, b) => a.data.order - b.data.order);
1320
const 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>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<div class="left-controls">
2+
<div class="dpad-area">
3+
<div class="dpad">
4+
<button id="up" class="dpad-btn dpad-up" aria-label="Up"></button>
5+
<button id="right" class="dpad-btn dpad-right" aria-label="Right"></button>
6+
<button id="down" class="dpad-btn dpad-down" aria-label="Down"></button>
7+
<button id="left" class="dpad-btn dpad-left" aria-label="Left"></button>
8+
<div class="dpad-center"></div>
9+
</div>
10+
</div>
11+
</div>
12+
13+
<div class="right-controls">
14+
<div class="action-buttons">
15+
<div class="btn-group group-a">
16+
<button type="button" class="btn-a" id="btn-a">A</button>
17+
</div>
18+
<div class="btn-group group-b">
19+
<button type="button" class="btn-b" id="btn-b">B</button>
20+
</div>
21+
</div>
22+
</div>
23+
24+
<div class="meta-controls">
25+
<div class="meta-group">
26+
<button class="pill-btn select" id="btn-select" aria-label="Select"></button>
27+
<span class="pill-label label-select">SELECT</span>
28+
</div>
29+
<div class="meta-group">
30+
<button class="pill-btn start" id="btn-start" aria-label="Start"></button>
31+
<span class="pill-label label-start">START</span>
32+
</div>
33+
</div>

src/components/gameboy/Hud.astro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div class="top-hud" id="top-hud" aria-label="Status HUD">
2+
<div class="hud-item" id="hud-time">--:--</div>
3+
<div class="hud-item" id="hud-date">-- ---</div>
4+
<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">
5+
<svg viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em" style="display:block;margin:auto;">
6+
<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"/>
7+
</svg>
8+
</div>
9+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div class="power-switch-container">
2+
<div class="switch-track" id="power-switch" role="switch" aria-checked="true" tabindex="0">
3+
<div class="switch-handle"></div>
4+
</div>
5+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div class="tabs-header" role="tablist" aria-label="Profile sections">
2+
<button type="button" class="tab-indicator active" id="tab-ind-0" role="tab" aria-selected="true" aria-controls="tab-0">LINKS</button>
3+
<button type="button" class="tab-indicator" id="tab-ind-1" role="tab" aria-selected="false" aria-controls="tab-1">ABOUT</button>
4+
<button type="button" class="tab-indicator" id="tab-ind-2" role="tab" aria-selected="false" aria-controls="tab-2">HELP</button>
5+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="app-turn-control">
2+
<button type="button" class="app-btn turn" id="btn-turn" aria-label="Toggle Layout">
3+
<svg viewBox="0 0 24 24" class="turn-icon">
4+
<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"/>
5+
</svg>
6+
</button>
7+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
const { aboutText } = Astro.props as { aboutText: string };
3+
---
4+
5+
<div class="about-text" id="about-text-content" data-text={aboutText}>
6+
<!-- Text will be typed here by JS -->
7+
</div>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<section class="help-panel" aria-label="Controls and secrets">
2+
<h3>KEYMAP</h3>
3+
<ul>
4+
<li><span>ARROWS / WASD</span><span>NAVIGATE</span></li>
5+
<li><span>A / ENTER / Z</span><span>OPEN LINK</span></li>
6+
<li><span>B / X</span><span>SWITCH THEME</span></li>
7+
<li><span>SELECT / Q</span><span>SWITCH THEME</span></li>
8+
<li><span>START</span><span>OPEN HELP TAB</span></li>
9+
<li><span>LEFT / RIGHT</span><span>CHANGE TAB</span></li>
10+
<li><span>M</span><span>MUTE AUDIO</span></li>
11+
<li><span>H</span><span>OPEN HELP TAB</span></li>
12+
</ul>
13+
<p class="secret-hint">SECRET: U U D D L R L R B A</p>
14+
</section>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
import { iconSvgs, type IconName } from '../../../lib/icons';
3+
4+
interface LinkItem {
5+
data: {
6+
title: string;
7+
url: string;
8+
icon: IconName;
9+
};
10+
}
11+
12+
const { links } = Astro.props as { links: LinkItem[] };
13+
---
14+
15+
<nav class="links" aria-label="Social links">
16+
{links.map((link, index) => (
17+
<a
18+
href={link.data.url}
19+
target={link.data.url.startsWith('mailto') ? '_self' : '_blank'}
20+
rel={link.data.url.startsWith('mailto') ? '' : 'noopener noreferrer'}
21+
class={`social-link ${link.data.icon} ${index === 0 ? 'active' : ''}`}
22+
data-index={index}
23+
>
24+
<Fragment set:html={iconSvgs[link.data.icon] || ''} />
25+
<span class="link-text">{link.data.title}</span>
26+
</a>
27+
))}
28+
</nav>

src/lib/bootstrap.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { initExtras } from './extras';
2+
import { initHudClock } from './hud-clock';
3+
import { initNavigation, updateTabUI } from './navigation';
4+
import { initPageVisibilityFeedback } from './page-visibility';
5+
import { initPowerSwitch } from './power-switch';
6+
import { updateMuteIcon } from './sound-engine';
7+
import { initTheme } from './theme-manager';
8+
9+
export function bootstrapApp() {
10+
initExtras();
11+
initPowerSwitch();
12+
updateMuteIcon();
13+
initTheme();
14+
initPageVisibilityFeedback();
15+
initNavigation();
16+
updateTabUI();
17+
initHudClock();
18+
}

0 commit comments

Comments
 (0)