feat: gamepad navigation for Mesh tab — zone-based panel nav
- Peer rows: tabindex + role=button + Enter handler for D-pad selection - Zone attributes: mesh-left, mesh-chat, mesh-tools for cross-panel nav - Actions row: data-controller-container for Up from peers - Right from peers → chat input, Right from chat → tools tabs (wide) - Down from tabs → panel fields/buttons in grid fashion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -344,7 +344,7 @@ function truncatePubkey(hex: string | null): string {
|
||||
<!-- Responsive column layout -->
|
||||
<div class="mesh-columns" :class="{ 'mesh-columns-wide': isWideDesktop }">
|
||||
<!-- LEFT COLUMN: Status + Peers -->
|
||||
<div class="mesh-left" :class="{ 'mobile-hidden': mobileShowChat }">
|
||||
<div class="mesh-left" data-controller-zone="mesh-left" :class="{ 'mobile-hidden': mobileShowChat }">
|
||||
<!-- Device Status -->
|
||||
<div data-controller-container tabindex="0" class="glass-card mesh-status-card">
|
||||
<div class="mesh-status-header">
|
||||
@@ -410,7 +410,7 @@ function truncatePubkey(hex: string | null): string {
|
||||
</div>
|
||||
|
||||
<!-- Actions row -->
|
||||
<div class="mesh-actions">
|
||||
<div class="mesh-actions" data-controller-container tabindex="0">
|
||||
<button class="glass-button mesh-action-btn" :disabled="configuring" @click="handleToggleEnabled">
|
||||
{{ mesh.status?.enabled ? 'Disable' : 'Enable' }}
|
||||
</button>
|
||||
@@ -441,7 +441,10 @@ function truncatePubkey(hex: string | null): string {
|
||||
<div
|
||||
class="mesh-peer-row is-channel"
|
||||
:class="{ active: archChannelActive }"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@click="openArchChannel"
|
||||
@keydown.enter="openArchChannel"
|
||||
>
|
||||
<div class="mesh-peer-avatar channel" style="background: rgba(251,146,60,0.2); color: #fb923c;">A</div>
|
||||
<div class="mesh-peer-info">
|
||||
@@ -454,7 +457,10 @@ function truncatePubkey(hex: string | null): string {
|
||||
<div
|
||||
class="mesh-peer-row is-channel"
|
||||
:class="{ active: activeChatChannel?.index === 0 }"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@click="openChannelChat(publicChannel)"
|
||||
@keydown.enter="openChannelChat(publicChannel)"
|
||||
>
|
||||
<div class="mesh-peer-avatar channel">#</div>
|
||||
<div class="mesh-peer-info">
|
||||
@@ -466,7 +472,10 @@ function truncatePubkey(hex: string | null): string {
|
||||
v-for="peer in sortedPeers" :key="peer.contact_id"
|
||||
class="mesh-peer-row"
|
||||
:class="{ active: activeChatPeer?.contact_id === peer.contact_id, 'is-archy': isArchyNode(peer) }"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@click="openChat(peer)"
|
||||
@keydown.enter="openChat(peer)"
|
||||
>
|
||||
<div class="mesh-peer-avatar" :class="{ archy: isArchyNode(peer) }">
|
||||
<AnimatedLogo v-if="isArchyNode(peer)" size="sm" />
|
||||
@@ -493,7 +502,7 @@ function truncatePubkey(hex: string | null): string {
|
||||
</div>
|
||||
|
||||
<!-- RIGHT COLUMN: Tabbed panels -->
|
||||
<div class="mesh-right" :class="{ 'mobile-hidden': !mobileShowChat }">
|
||||
<div class="mesh-right" data-controller-zone="mesh-chat" :class="{ 'mobile-hidden': !mobileShowChat }">
|
||||
<!-- Tab bar (medium desktop only) -->
|
||||
<div v-if="showTabBar" class="mesh-tab-bar">
|
||||
<button class="mesh-tab" :class="{ active: activeTab === 'chat' }" @click="activeTab = 'chat'">Chat</button>
|
||||
@@ -614,8 +623,8 @@ function truncatePubkey(hex: string | null): string {
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Tools panels -->
|
||||
<div class="mesh-tools-wrapper">
|
||||
<!-- Tools panels (3rd column on wide screens) -->
|
||||
<div class="mesh-tools-wrapper" data-controller-zone="mesh-tools">
|
||||
<!-- Tools tab bar (wide desktop only) -->
|
||||
<div v-if="isWideDesktop" class="mesh-tools-tab-bar">
|
||||
<button class="mesh-tab" :class="{ active: toolsTab === 'bitcoin' }" @click="toolsTab = 'bitcoin'">
|
||||
|
||||
Reference in New Issue
Block a user