Full UI redesign pass complete: persistent tab shell with an always-visible background map, Modern Professional Dark theme, monochrome dark map tiles, offline skeleton map, a shared GlassPanel/FloatingPill component kit, customizable HUD telemetry widgets, and the Map HUD / Plan & Route Planning / Rides History screen rebuilds. 374 tests passing, up from 316. The APK is a fresh release build (debug-signed, no release signing config exists yet). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
317 lines
17 KiB
HTML
317 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html class="dark" lang="en" style=""><head></head><body class="antialiased selection:bg-primary-container selection:text-on-primary-container flex flex-col h-screen relative text-on-surface bg-surface"><svg aria-hidden="true" class="inline-defs-container" style="position:absolute;width:0;height:0;overflow:hidden"></svg>
|
|
<meta charset="utf-8"/>
|
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
|
<title>RIPPR - Record Activity</title>
|
|
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
|
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Sora:wght@400;600;700&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
|
<script id="tailwind-config">
|
|
tailwind.config = {
|
|
darkMode: "class",
|
|
theme: {
|
|
extend: {
|
|
"colors": {
|
|
"secondary-fixed-dim": "#aec7f6",
|
|
"primary-fixed": "#d6e3ff",
|
|
"tertiary-fixed-dim": "#ffb68c",
|
|
"inverse-surface": "#e5e2e1",
|
|
"on-surface-muted": "#9ca3af",
|
|
"error": "#ffb4ab",
|
|
"inverse-primary": "#005db8",
|
|
"on-primary-fixed-variant": "#00458d",
|
|
"secondary": "#aec7f6",
|
|
"primary-fixed-dim": "#aac7ff",
|
|
"surface-container": "#201f1f",
|
|
"primary": "#aac7ff",
|
|
"tertiary-container": "#e3711f",
|
|
"surface-variant": "#353534",
|
|
"on-background": "#e5e2e1",
|
|
"on-secondary-fixed-variant": "#2e476f",
|
|
"primary-container": "#4090fe",
|
|
"outline-variant": "#414753",
|
|
"background": "#131313",
|
|
"surface": "#131313",
|
|
"on-tertiary": "#532200",
|
|
"on-tertiary-fixed": "#321200",
|
|
"on-surface": "#e5e2e1",
|
|
"surface-container-low": "#1c1b1b",
|
|
"on-primary-container": "#002958",
|
|
"secondary-fixed": "#d6e3ff",
|
|
"accent-error": "#ffb4ab",
|
|
"on-primary-fixed": "#001b3e",
|
|
"surface-container-lowest": "#0e0e0e",
|
|
"surface-tint": "#aac7ff",
|
|
"error-container": "#93000a",
|
|
"secondary-container": "#2e476f",
|
|
"surface-main": "#0a0a0c",
|
|
"tertiary-fixed": "#ffdbc9",
|
|
"surface-container-high": "#2a2a2a",
|
|
"on-primary": "#002f64",
|
|
"on-surface-bright": "#ffffff",
|
|
"on-tertiary-container": "#481d00",
|
|
"on-error": "#690005",
|
|
"surface-card": "#16161a",
|
|
"surface-container-highest": "#353534",
|
|
"surface-bright": "#393939",
|
|
"on-secondary-container": "#9db6e4",
|
|
"on-tertiary-fixed-variant": "#763400",
|
|
"on-error-container": "#ffdad6",
|
|
"on-secondary-fixed": "#001b3d",
|
|
"tertiary": "#ffb68c",
|
|
"outline": "#8b919f",
|
|
"surface-dim": "#131313",
|
|
"on-secondary": "#143057",
|
|
"inverse-on-surface": "#313030",
|
|
"on-surface-variant": "#c1c6d5"
|
|
},
|
|
"borderRadius": {
|
|
"DEFAULT": "0.25rem",
|
|
"lg": "0.5rem",
|
|
"xl": "0.75rem",
|
|
"full": "9999px"
|
|
},
|
|
"spacing": {
|
|
"margin-mobile": "16px",
|
|
"gutter": "24px",
|
|
"margin-desktop": "32px",
|
|
"base": "4px",
|
|
"max-width": "1280px"
|
|
},
|
|
"fontFamily": {
|
|
"headline-md": [
|
|
"Inter"
|
|
],
|
|
"label-sm": [
|
|
"Inter"
|
|
],
|
|
"body-lg": [
|
|
"Inter"
|
|
],
|
|
"body-md": [
|
|
"Inter"
|
|
],
|
|
"headline-sm": [
|
|
"Inter"
|
|
],
|
|
"headline-lg": [
|
|
"Inter"
|
|
],
|
|
"headline-lg-mobile": [
|
|
"Inter"
|
|
],
|
|
"label-md": [
|
|
"Inter"
|
|
],
|
|
"display-telemetry": ["Inter"],
|
|
"label-caps": ["Inter"]
|
|
},
|
|
"fontSize": {
|
|
"headline-md": [
|
|
"24px",
|
|
{
|
|
"lineHeight": "32px",
|
|
"fontWeight": "600"
|
|
}
|
|
],
|
|
"label-sm": [
|
|
"11px",
|
|
{
|
|
"lineHeight": "16px",
|
|
"letterSpacing": "0.5px",
|
|
"fontWeight": "500"
|
|
}
|
|
],
|
|
"body-lg": [
|
|
"16px",
|
|
{
|
|
"lineHeight": "24px",
|
|
"fontWeight": "400"
|
|
}
|
|
],
|
|
"body-md": [
|
|
"14px",
|
|
{
|
|
"lineHeight": "20px",
|
|
"fontWeight": "400"
|
|
}
|
|
],
|
|
"headline-sm": [
|
|
"20px",
|
|
{
|
|
"lineHeight": "28px",
|
|
"fontWeight": "600"
|
|
}
|
|
],
|
|
"headline-lg": [
|
|
"32px",
|
|
{
|
|
"lineHeight": "40px",
|
|
"letterSpacing": "-0.02em",
|
|
"fontWeight": "700"
|
|
}
|
|
],
|
|
"headline-lg-mobile": [
|
|
"28px",
|
|
{
|
|
"lineHeight": "36px",
|
|
"letterSpacing": "-0.01em",
|
|
"fontWeight": "700"
|
|
}
|
|
],
|
|
"label-md": [
|
|
"12px",
|
|
{
|
|
"lineHeight": "16px",
|
|
"letterSpacing": "0.5px",
|
|
"fontWeight": "500"
|
|
}
|
|
],
|
|
"display-telemetry": ["64px", { "lineHeight": "64px", "letterSpacing": "-0.04em", "fontWeight": "700" }],
|
|
"label-caps": ["12px", { "lineHeight": "16px", "letterSpacing": "0.1em", "fontWeight": "500" }]
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style>
|
|
body { margin: 0; overflow: hidden; background-color: #131313; color: #e5e2e1; min-height: max(884px, 100dvh); }
|
|
.hud-panel { background: rgba(32, 31, 31, 0.8); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); }
|
|
.glass-btn { background: rgba(32, 31, 31, 0.9); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); }
|
|
</style>
|
|
<!-- Background Map Placeholder -->
|
|
<div class="absolute inset-0 z-0 bg-surface">
|
|
<div class="w-full h-full bg-cover bg-center opacity-40 mix-blend-luminosity" data-alt="A light mode stylized city map view for a fitness tracking application. Minimalist aesthetic with high contrast blue routes highlighting paths against light gray blocks representing buildings and terrain. The map appears like a clean interface overlay." data-location="Tokyo" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuDr5_s-KgoDdw3V0jHBFYtHVB3aikGStl0j7TM7kPJnauzs3U1eRnVMaMh4ywSZTMLAo_LxEZnu9rTzBPq-EGev_TiD3t0VY9osDcW0xKKUZxL2rx7TPn0qh2lor7H-TT1FWRur2eVHGsIsu-YT3ht-zwG1VrgIArB7Dxx5IgNThsHCroZ6oFbBB_Uo1Y8rIjPn8NPkxuUVxmKjpNze6RAmNCuYj9V3ivWdnkUNS_Z6ybPWERqbvRA4')"></div>
|
|
<!-- Routes and Location Overlay -->
|
|
<svg class="absolute inset-0 w-full h-full pointer-events-none" preserveaspectratio="xMidYMid slice" viewbox="0 0 400 800">
|
|
<defs>
|
|
<lineargradient id="speedGradient" x1="10%" x2="50%" y1="90%" y2="50%">
|
|
<stop offset="0%" stop-color="#4090fe"></stop>
|
|
<stop offset="40%" stop-color="#aac7ff"></stop>
|
|
<stop offset="70%" stop-color="#ffb4ab"></stop>
|
|
<stop offset="100%" stop-color="#ffb4ab"></stop>
|
|
</lineargradient>
|
|
</defs>
|
|
<!-- Planned Route -->
|
|
<path d="M 200 400 C 250 250, 150 150, 300 50" fill="none" opacity="0.8" stroke="#8b919f" stroke-dasharray="10 10" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"></path>
|
|
<!-- Completed Route -->
|
|
<path d="M 100 750 C 150 600, 100 500, 200 400" fill="none" stroke="url(#speedGradient)" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"></path>
|
|
<!-- Pulsing Location Dot -->
|
|
<circle cx="200" cy="400" fill="#4090fe" r="8"></circle>
|
|
<circle cx="200" cy="400" fill="none" r="8" stroke="#4090fe" stroke-width="2">
|
|
<animate attributename="r" dur="1.5s" from="8" repeatcount="indefinite" to="24"></animate>
|
|
<animate attributename="opacity" dur="1.5s" from="1" repeatcount="indefinite" to="0"></animate>
|
|
</circle>
|
|
</svg>
|
|
</div>
|
|
<!-- TopAppBar JSON Component -->
|
|
<!-- Main Content Canvas (HUD Layout) -->
|
|
<main class="flex-1 relative z-10 flex flex-col pb-40 px-2 md:px-margin-desktop w-full max-w-7xl mx-auto md:ml-24 md:w-[calc(100%-6rem)] p-2">
|
|
<!-- Compact Telemetry Row -->
|
|
<div class="flex flex-row gap-1 w-full" id="telemetry-container">
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Speed</span>
|
|
<div class="metric-value font-display-telemetry text-base text-primary mt-1 transition-all duration-300 whitespace-nowrap">24.5 km/h</div>
|
|
</div>
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Avg Speed</span>
|
|
<div class="metric-value font-display-telemetry text-lg text-on-surface mt-1 transition-all duration-300">22.1</div>
|
|
</div>
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Dist</span>
|
|
<div class="metric-value font-display-telemetry text-base text-secondary mt-1 transition-all duration-300 whitespace-nowrap">12.4 km</div>
|
|
</div>
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Time</span>
|
|
<div class="metric-value font-display-telemetry text-lg text-on-surface mt-1 transition-all duration-300">45:12</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<!-- Full Width Recording Control Bar -->
|
|
<div class="fixed bottom-[80px] md:bottom-0 md:left-24 w-full md:w-[calc(100%-6rem)] z-40">
|
|
<div class="flex flex-row w-full h-16 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.5)]">
|
|
<button class="flex-1 bg-tertiary-container text-on-tertiary-container font-headline-md flex items-center justify-center transition-colors duration-300 border-r border-white/10"><span class="material-symbols-outlined text-[32px]" style='font-variation-settings: "FILL" 1;'>pause</span></button>
|
|
<button class="flex-1 bg-error-container text-on-error-container font-headline-md flex items-center justify-center transition-colors duration-300"><span class="material-symbols-outlined text-[32px]" style='font-variation-settings: "FILL" 1;'>stop</span></button>
|
|
</div>
|
|
</div>
|
|
<!-- BottomNavBar JSON Component -->
|
|
<nav class="bg-surface/80 backdrop-blur-xl fixed bottom-0 w-full z-50 pb-safe border-t border-white/10 md:hidden">
|
|
<div class="flex justify-around items-center h-20 px-margin-mobile font-label-caps text-label-caps font-display-telemetry"><button class="flex flex-col items-center justify-center text-primary relative after:content-[''] after:absolute after:-bottom-1 after:w-1 after:h-1 after:bg-primary after:rounded-full hover:text-primary/80 transition-transform duration-150 scale-95">
|
|
<span class="material-symbols-outlined" style='font-variation-settings: "FILL" 1;'>map</span>
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-transform duration-150"><span class="material-symbols-outlined" style='font-variation-settings: "FILL" 0;'>book</span></button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-transform duration-150">
|
|
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">route</span>
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-transform duration-150">
|
|
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">settings</span>
|
|
</button></div>
|
|
</nav>
|
|
<!-- Desktop Side Nav (Visible only on md+) -->
|
|
<aside class="hidden md:flex flex-col fixed left-0 top-16 bottom-0 w-24 bg-surface/80 backdrop-blur-xl border-r border-white/10 z-40 items-center py-8 gap-12">
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-colors w-full py-4 font-label-caps text-label-caps">
|
|
<span class="material-symbols-outlined mb-2 text-2xl">map</span>
|
|
Map
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-primary relative after:content-[''] after:absolute after:left-0 after:top-1/2 after:-translate-y-1/2 after:w-1 after:h-8 after:bg-primary after:rounded-r-full hover:text-primary/80 transition-colors w-full py-4 font-label-caps text-label-caps bg-white/5">
|
|
<span class="material-symbols-outlined mb-2 text-2xl" style="font-variation-settings: 'FILL' 1;">directions_bike</span>
|
|
Rides
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-colors w-full py-4 font-label-caps text-label-caps">
|
|
<span class="material-symbols-outlined mb-2 text-2xl">route</span>
|
|
Plan
|
|
</button>
|
|
</aside>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Recording Toggle Logic
|
|
const toggleBtn = document.getElementById('recordingToggle');
|
|
const toggleIcon = document.getElementById('toggleIcon');
|
|
const toggleText = document.getElementById('toggleText');
|
|
let isRecording = true;
|
|
|
|
toggleBtn?.addEventListener('click', () => {
|
|
isRecording = !isRecording;
|
|
if(isRecording) {
|
|
toggleBtn.classList.remove('bg-error-container', 'text-on-error-container');
|
|
toggleBtn.classList.add('bg-primary-container', 'text-on-primary-container');
|
|
toggleIcon.textContent = 'stop_circle';
|
|
toggleText.textContent = 'Stop Recording';
|
|
} else {
|
|
toggleBtn.classList.remove('bg-primary-container', 'text-on-primary-container');
|
|
toggleBtn.classList.add('bg-error-container', 'text-on-error-container');
|
|
toggleIcon.textContent = 'play_arrow';
|
|
toggleText.textContent = 'Start Recording';
|
|
}
|
|
});
|
|
|
|
// Telemetry Expansion Logic
|
|
const metrics = document.querySelectorAll('.metric-card');
|
|
|
|
metrics.forEach(metric => {
|
|
metric.addEventListener('click', () => {
|
|
const isExpanded = metric.classList.contains('flex-[2.5]');
|
|
|
|
// Reset all
|
|
metrics.forEach(m => {
|
|
m.classList.remove('flex-[2.5]', 'bg-surface-container-highest');
|
|
m.classList.add('flex-1');
|
|
m.querySelector('.metric-value').classList.remove('text-2xl', 'md:text-3xl');
|
|
m.querySelector('.metric-value').classList.add('text-lg');
|
|
});
|
|
|
|
// Expand if it wasn't already expanded
|
|
if (!isExpanded) {
|
|
metric.classList.remove('flex-1');
|
|
metric.classList.add('flex-[2.5]', 'bg-surface-container-highest');
|
|
metric.querySelector('.metric-value').classList.remove('text-lg');
|
|
metric.querySelector('.metric-value').classList.add('text-2xl', 'md:text-3xl');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
</body></html> |