Files
TREK/client/src/main.tsx
T
Maurice ea9e997338 feat(tours): show the known SAC grades along a hike (MAP-03)
A hike in the planner, being edited or viewed as an import, now has an
"SAC grades" card. Switched on, it asks the router which OpenStreetMap
ways lie under the line (matched on foot, every grade of path open) and
the server reads their sac_scale through the Overpass mirrors, the same
trail-grades read the mountain bike grades use. The grades are drawn
along the route on the planner map in one ramp of a single hue, T1 to
T6, and listed by share and length; a row draws its class alone. Each
row names its source in a tooltip: the SAC hiking scale and the
sac_scale value OpenStreetMap wrote.

What is not known stays unknown and says why, never T1: a way without
a grade, a grade that names no single step (handed on as written), a
stretch with no way clearly under the tour's line, or a way that could
not be asked about. For imported tracks and stretches drawn by hand a
matched way only counts where it keeps within 25 m of the tour's own
line, so the original line is kept and nothing is guessed. The router's
own reading of the scale is not asked for, since it may be normalised.

The routing limit of a planned hike sits apart as a setting with its
own badge, and a known step above it is marked. Nothing about the tour,
its places or its days changes; switched off, the map is as it was.
No regional colour categories and no grade derived from slope, height
or trail markings.
2026-10-20 15:17:23 +02:00

68 lines
3.3 KiB
TypeScript

import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router'
import App from './App'
// Self-hosted Poppins (bundled, same-origin) so the app font can't be blocked by
// ad/tracker blockers the way the Google Fonts CDN can.
import '@fontsource/poppins/300.css'
import '@fontsource/poppins/400.css'
import '@fontsource/poppins/500.css'
import '@fontsource/poppins/600.css'
import '@fontsource/poppins/700.css'
// MuseoModerno for the login wordmark — same reasoning, it used to come from the
// Google Fonts CDN via a render-blocking <link> in index.html.
import '@fontsource/museomoderno/400.css'
import '@fontsource/museomoderno/700.css'
import '@fontsource/museomoderno/800.css'
// Geist Sans (self-hosted too) — used only for secondary "subtext" via --font-subtext.
import '@fontsource/geist-sans/400.css'
import '@fontsource/geist-sans/500.css'
import '@fontsource/geist-sans/600.css'
// Leaflet CSS bundled from node_modules instead of unpkg: the service worker
// cached the CDN stylesheet as an opaque response, which the browser then
// rejected, breaking the Atlas/trip maps (#1497). Bundling keeps it same-origin
// and precached with the app shell. Must stay above index.css, which overrides
// several .leaflet-* styles.
import 'leaflet/dist/leaflet.css'
import './index.css'
import './styles/ferratas.css'
import './styles/paddle.css'
import './styles/bike.css'
import './styles/horse.css'
import './styles/hike.css'
// Native HTML5 drag-and-drop never fires on touch input, so the planner's place /
// day reordering was dead on Android and iOS. The `drag-drop-touch` polyfill synthesises
// the standard drag events from touch gestures over draggable elements (#1265). It is
// loaded only on non-mobile viewports: reorder DnD is disabled on mobile (#1432), where
// the polyfill's synthetic click/dblclick otherwise turned quick one-finger map pans into
// double-tap zooms (#1440). See utils/touchDragPolyfill.ts.
import { maybeInstallTouchDragPolyfill } from './utils/touchDragPolyfill'
import { startConnectivityProbe } from './sync/connectivity'
import { requestPersistentStorage } from './sync/persistentStorage'
import ErrorBoundary, { RootErrorFallback } from './components/shared/ErrorBoundary'
import { installGlobalErrorHandlers } from './utils/globalErrorHandlers'
import { schedulePrecacheRepair } from './utils/serviceWorkerShell'
void maybeInstallTouchDragPolyfill()
startConnectivityProbe()
// Keep offline data (map tiles, file blobs, IndexedDB) exempt from eviction.
void requestPersistentStorage()
// Event handlers and async code never reach a boundary; this is where they land.
installGlobalErrorHandlers()
// A page the precache keeps under a stylesheet's or a script's name would be
// handed on to every later release (#2524).
schedulePrecacheRepair()
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
{/* Outermost net: catches App itself and TranslationProvider, which every
boundary further in sits below. Its fallback is untranslated on purpose
— if the provider is the thing that broke, t() would echo raw keys. */}
<ErrorBoundary boundaryId="root" level="root" fallback={s => <RootErrorFallback {...s} />}>
<App />
</ErrorBoundary>
</BrowserRouter>
</React.StrictMode>,
)