Files
intercept/templates/partials/modes/meshtastic.html
Smittix e687862043 feat: UI/UX overhaul — CSS cleanup, accessibility, error handling, inline style extraction
Phase 0 — CSS-only fixes:
- Fix --font-mono to use real monospace stack (JetBrains Mono, Fira Code, etc.)
- Replace hardcoded hex colors with CSS variables across 16+ files
- Merge global-nav.css (507 lines) into layout.css, delete original
- Reduce !important in responsive.css from 71 to 8 via .app-shell specificity
- Standardize breakpoints to 480/768/1024/1280px

Phase 1 — Loading states & SSE connection feedback:
- Add centralized SSEManager (sse-manager.js) with exponential backoff
- Add SSE status indicator dot in nav bar
- Add withLoadingButton() + .btn-loading CSS spinner
- Add mode section crossfade transitions

Phase 2 — Accessibility:
- Add aria-labels to icon-only buttons across mode partials
- Add for/id associations to 42 form labels in 5 mode partials
- Add aria-live on toast stack, enableListKeyNav() utility

Phase 3 — Destructive action guards & list overflow:
- Add confirmAction() styled modal, replace all 25 native confirm() calls
- Add toast cap at 5 simultaneous toasts
- Add list overflow indicator CSS

Phase 4 — Inline style extraction:
- Refactor switchMode() in app.js and index.html to use classList.toggle()
- Add CSS toggle rules for all switchMode-controlled elements
- Remove inline style="display:none" from 7+ HTML elements
- Add utility classes (.hidden, .d-flex, .d-grid, etc.)

Phase 5 — Mobile UX polish:
- pre/code overflow handling already in place
- Touch target sizing via --touch-min variable

Phase 6 — Error handling consistency:
- Add reportActionableError() to user-facing catch blocks in 5 mode JS files
- 28 error toast additions alongside existing console.error calls

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:04:36 +00:00

190 lines
12 KiB
HTML

<!-- MESHTASTIC MODE -->
<div id="meshtasticMode" class="mode-content mesh-sidebar-collapsed">
<!-- Hide Sidebar Button -->
<button class="mesh-hide-sidebar-btn" onclick="Meshtastic.toggleSidebar()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="11 17 6 12 11 7"/>
<polyline points="18 17 13 12 18 7"/>
</svg>
Hide Sidebar
</button>
<!-- Collapse Toggle for Options Panel -->
<div class="mesh-sidebar-toggle" onclick="Meshtastic.toggleOptionsPanel()">
<span class="mesh-sidebar-toggle-icon" id="meshSidebarIcon"></span>
<span class="mesh-sidebar-toggle-text">Meshtastic Options</span>
</div>
<!-- Collapsible Content -->
<div class="mesh-sidebar-content" id="meshSidebarContent">
<!-- Channels Panel - shown when connected -->
<div class="section" id="meshChannelsSection" style="display: none;">
<h3>Channels</h3>
<div id="meshChannelsList">
<!-- Populated by JavaScript -->
</div>
<button class="preset-btn" onclick="Meshtastic.refreshChannels()" style="width: 100%; margin-top: 8px;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; margin-right: 6px; vertical-align: middle;">
<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/>
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
</svg>
Refresh Channels
</button>
</div>
<div class="section">
<h3>Help</h3>
<button class="preset-btn" onclick="Meshtastic.showHelp()" style="width: 100%;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; margin-right: 6px; vertical-align: middle;">
<circle cx="12" cy="12" r="10"/>
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
<line x1="12" y1="17" x2="12.01" y2="17"/>
</svg>
About Meshtastic
</button>
</div>
<div class="section">
<h3>Resources</h3>
<div style="display: flex; flex-direction: column; gap: 6px;">
<a href="https://meshtastic.org" target="_blank" rel="noopener" class="preset-btn" style="text-decoration: none; text-align: center;">
Meshtastic.org
</a>
<a href="https://meshtastic.org/docs/" target="_blank" rel="noopener" class="preset-btn" style="text-decoration: none; text-align: center;">
Documentation
</a>
</div>
</div>
<!-- Antenna Guide -->
<div class="section">
<h3>Antenna Guide</h3>
<div style="font-size: 11px; color: var(--text-dim); line-height: 1.5;">
<p style="margin-bottom: 8px; color: var(--accent-cyan); font-weight: 600;">
LoRa ISM band &mdash; frequency depends on region
</p>
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
<strong style="color: var(--accent-cyan); font-size: 12px;">Stock Device Antenna</strong>
<ul style="margin: 6px 0 0 14px; padding: 0;">
<li><strong style="color: var(--text-primary);">Most devices:</strong> Ship with a small 915/868 MHz stubby antenna</li>
<li><strong style="color: var(--text-primary);">Works for:</strong> Short range (&lt; 1 km) urban, indoor testing</li>
<li><strong style="color: var(--text-primary);">Upgrade:</strong> Replace with tuned antenna for 5&ndash;20x range improvement</li>
</ul>
</div>
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
<strong style="color: #00ff88; font-size: 12px;">Recommended Upgrades</strong>
<ul style="margin: 6px 0 0 14px; padding: 0;">
<li><strong style="color: var(--text-primary);">Whip antenna:</strong> ~$8&ndash;15, tuned 915/868 MHz, SMA connector</li>
<li><strong style="color: var(--text-primary);">Ground plane:</strong> 8.2 cm vertical + 4 radials (915 MHz) on SMA</li>
<li><strong style="color: var(--text-primary);">Yagi:</strong> ~$15&ndash;30, directional, great for point-to-point links</li>
<li><strong style="color: var(--text-primary);">Collinear:</strong> ~$20&ndash;40, omnidirectional with higher gain (~5&ndash;8 dBi)</li>
</ul>
</div>
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
<strong style="color: var(--accent-cyan); font-size: 12px;">Placement Tips</strong>
<ul style="margin: 6px 0 0 14px; padding: 0;">
<li><strong style="color: var(--text-primary);">Height wins:</strong> Elevating antenna 10m can double or triple range</li>
<li><strong style="color: var(--text-primary);">Line of sight:</strong> LoRa works best with clear LOS to other nodes</li>
<li><strong style="color: var(--text-primary);">Connector:</strong> Most devices use SMA or RP-SMA &mdash; check before buying</li>
</ul>
</div>
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px;">
<strong style="color: var(--accent-cyan); font-size: 12px;">Quick Reference</strong>
<table style="width: 100%; margin-top: 6px; font-size: 10px; border-collapse: collapse;">
<tr style="border-bottom: 1px solid var(--border-color);">
<td style="padding: 3px 4px; color: var(--text-dim);">US / Americas</td>
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">915 MHz</td>
</tr>
<tr style="border-bottom: 1px solid var(--border-color);">
<td style="padding: 3px 4px; color: var(--text-dim);">EU / UK / India</td>
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">868 MHz</td>
</tr>
<tr style="border-bottom: 1px solid var(--border-color);">
<td style="padding: 3px 4px; color: var(--text-dim);">915 MHz &lambda;/4</td>
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">8.2 cm</td>
</tr>
<tr style="border-bottom: 1px solid var(--border-color);">
<td style="padding: 3px 4px; color: var(--text-dim);">868 MHz &lambda;/4</td>
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">8.6 cm</td>
</tr>
<tr style="border-bottom: 1px solid var(--border-color);">
<td style="padding: 3px 4px; color: var(--text-dim);">Modulation</td>
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">LoRa (CSS)</td>
</tr>
<tr>
<td style="padding: 3px 4px; color: var(--text-dim);">Typical range</td>
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">1&ndash;15 km</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Channel Configuration Modal -->
<div id="meshChannelModal" class="signal-details-modal">
<div class="signal-details-modal-backdrop" onclick="Meshtastic.closeChannelModal()"></div>
<div class="signal-details-modal-content">
<div class="signal-details-modal-header">
<h3>Configure Channel <span id="meshModalChannelIndex">0</span></h3>
<button class="signal-details-modal-close" onclick="Meshtastic.closeChannelModal()" aria-label="Close channel config">&times;</button>
</div>
<div class="signal-details-modal-body">
<div class="signal-details-section">
<div class="signal-details-title">Channel Settings</div>
<div class="form-group" style="margin-bottom: 12px;">
<label style="font-size: 11px; color: var(--text-secondary); margin-bottom: 4px;">Channel Name (max 12 chars)</label>
<input type="text" id="meshModalChannelName" maxlength="12" placeholder="MyChannel" style="width: 100%;">
</div>
<div class="form-group" style="margin-bottom: 12px;">
<label style="font-size: 11px; color: var(--text-secondary); margin-bottom: 4px;">Encryption (PSK)</label>
<select id="meshModalPskFormat" onchange="Meshtastic.onPskFormatChange()" style="width: 100%; margin-bottom: 8px;">
<option value="keep">Keep Current</option>
<option value="none">None (No Encryption)</option>
<option value="default">Default (Public Key - NOT SECURE)</option>
<option value="random">Random (Generate AES-256)</option>
<option value="simple">Passphrase (simple:...)</option>
<option value="base64">Base64 Key</option>
<option value="hex">Hex Key (0x...)</option>
</select>
<div id="meshModalPskInputContainer" style="display: none;">
<input type="text" id="meshModalPskValue" placeholder="Enter key..." style="width: 100%;">
</div>
<div id="meshModalPskWarning" style="display: none; background: rgba(255,193,7,0.1); border: 1px solid var(--accent-yellow); border-radius: 4px; padding: 8px; margin-top: 8px; font-size: 10px;">
<strong style="color: var(--accent-yellow);">Warning:</strong>
<span style="color: var(--text-secondary);">The default key is publicly known and provides no security.</span>
</div>
</div>
</div>
</div>
<div class="signal-details-modal-footer" style="display: flex; gap: 8px;">
<button class="preset-btn" onclick="Meshtastic.closeChannelModal()" style="flex: 1;">Cancel</button>
<button class="run-btn" onclick="Meshtastic.saveChannelConfig()" style="flex: 1;">Save Changes</button>
</div>
</div>
</div>
<!-- Traceroute Modal -->
<div id="meshTracerouteModal" class="signal-details-modal">
<div class="signal-details-modal-backdrop" onclick="Meshtastic.closeTracerouteModal()"></div>
<div class="signal-details-modal-content">
<div class="signal-details-modal-header">
<h3>Traceroute to <span id="meshTracerouteDest">--</span></h3>
<button class="signal-details-modal-close" onclick="Meshtastic.closeTracerouteModal()" aria-label="Close traceroute">&times;</button>
</div>
<div class="signal-details-modal-body">
<div id="meshTracerouteContent" class="mesh-traceroute-content">
<!-- Populated by JavaScript -->
</div>
</div>
<div class="signal-details-modal-footer">
<button class="preset-btn" onclick="Meshtastic.closeTracerouteModal()" style="width: 100%;">Close</button>
</div>
</div>
</div>