feat(navi): GPS origin + place detail panel + basic actions

Adds synthetic "Your location" stop A when GPS granted; place
detail panel slides in on search result click with Directions /
Add stop / Save (stub) / Share actions; elevation via Valhalla
/height; react-hot-toast for feedback; pendingDestination state
for GPS-denied Directions flow.

Phase 3 Step 5 C1 of Navi.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-04-20 20:59:18 +00:00
commit 02f2b25db3
18 changed files with 1207 additions and 274 deletions

45
src/hooks/useTheme.js Normal file
View file

@ -0,0 +1,45 @@
import { useEffect } from 'react'
import { useStore } from '../store'
/**
* Initializes and manages the theme system.
* Call once in App it handles:
* - Reading localStorage override on mount
* - Listening to system prefers-color-scheme
* - Applying data-theme to <html>
* - Updating store.theme (resolved value)
*/
export function useTheme() {
const setTheme = useStore((s) => s.setTheme)
const themeOverride = useStore((s) => s.themeOverride)
// Initialize override from localStorage on first mount
useEffect(() => {
const stored = localStorage.getItem('navi-theme-override')
if (stored === 'dark' || stored === 'light') {
useStore.getState().setThemeOverride(stored)
}
}, [])
// Resolve and apply theme
useEffect(() => {
function resolve() {
if (themeOverride) return themeOverride
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
function apply() {
const resolved = resolve()
document.documentElement.setAttribute('data-theme', resolved)
setTheme(resolved)
}
apply()
// Listen for system changes (only matters when no override)
const mq = window.matchMedia('(prefers-color-scheme: dark)')
const handler = () => { if (!themeOverride) apply() }
mq.addEventListener('change', handler)
return () => mq.removeEventListener('change', handler)
}, [themeOverride, setTheme])
}