Build pipeline (scripts/build-hikes.ts) parses per-hike GPX, encodes images via sharp, reverse-geocodes the centroid against Swisstopo and emits a typed manifest under src/lib/data/hikes.generated.ts (gitignored). Track JSON + image binaries live outside /static; served in dev by a small hike-images plugin in vite.config.ts, in prod by nginx (private/ images proxied through Node + X-Accel-Redirect for auth-gating). /hikes overview: full-bleed Swisstopo hero map (HikesOverviewMap) sits under the sticky nav, drawing one polyline per route coloured by SAC tier (T1 yellow Wegweiser, T2/T3 white-red-white, T4-T6 white-blue- white). Click navigates, hover thickens + tooltips. Layer toggle, recenter, GPS controls mirror the detail map (minus images toggle). Cards drop the trail SVG, gain a per-route icon + SAC marker pictogram on the cover, altitude range, season label, and "Neu" badge for recently-published hikes. Filter bar + totals strip recompute over the currently-visible set. /hikes/[slug]: hero map with elevation profile, photo strip with map sync, scroll-position pin, GPX download, SAC marker stats + min/max altitude + season. Route-builder (/hikes/route-builder): client-side draft persisted to localStorage, EXIF-driven image placement, snap-to-route via BRouter (OSRM + linear fallback) and Swisstopo profile.json elevation enrichment that handles degenerate same-coord segments via the height endpoint. Filter init switched from a script-time snapshot of data.hikes (which sporadically returned a one-hike subset during dev hydration and locked the page to that single hike) to a post-mount \$effect. Content under src/content/hikes/ intentionally not included (WIP).
7.6 KiB
TODO
Perf (audit 2026-04-23)
Order = impact. Font items + app.html preload intentionally skipped.
- 1. Lucide subpath imports — convert
from '@lucide/svelte'barrel imports to@lucide/svelte/icons/<kebab-name>so Vite tree-shakes per-icon (current 748 KB shared chunk) - 2. Chart.js dynamic import in
FitnessChart.svelte(drop 244 KB from non-stats fitness routes) - 3. Recipe API endpoints — drop
JSON.parse(JSON.stringify(...))double-serialize (9 endpoints). Client-side shuffle / cache headers deferred (would require rethinking hero preload + hydration) - 4. Favorites page — drop unnecessary
all_brieffetch (verified Search usesfavoritesOnlysoallRecipeswas redundant) - 5. Replace redundant
locals.auth()withlocals.sessionacross all routes (68 files, 107 sites — loaders, actions, API endpoints) - 6. Stream fitness stats loader — muscleHeatmap, nutritionStats, periods, sharedPeriods now stream via
{#await}.statsstill awaited (too many chart $deriveds depend on it) - 7. Muscle-heatmap endpoint — add projection + O(1) bucket math. Overview already had a projection; set-subfield narrowing was attempted but reverted (returned malformed sets). Timeseries cap not feasible: totals are lifetime-scoped.
- 8. Calendar payload trim —
yearDaysnarrowed to{iso, color}(needle lookup only), new pre-filteredfeastDotsarray carries feast-specific metadata. Also fixed a stray doublelocals.session ?? (locals.session ?? …)in both calendar page loaders. - 9. History sessions endpoint — projection narrowed to exactly what SessionCard reads (drops notes, templates, mode, endTime, session-level gpsPreview); added
.lean(). - 10.
Cache-Controlheaders: 8 h public on the shuffled recipe list endpoints (all_brief,category/[c],tag/[t],icon/[i],in_season/[m]) — rand_array is seeded per UTC day, safe to share. 1 h public on distinct-value lists (category,tag,icon). 5 min public on recipe detail.private 1hon fitness/exercises/filters. Calendar page skipped (session serialised into layout HTML). - 11. Search — debounce was already 100 ms. Instead of a server-side
_searchKey(would duplicate text over the wire), memoise per-recipe normalized string in aWeakMapon the client — built lazily, reused across every subsequent keystroke.
Features
[x] on /fitness/measure, fill "Past measurements" in SSR only for the last 10 measurements. anything further should be fetched client side on mount to decreae initial page load time. use a "show more" button and paginate measurments. [x] on /fitness/measure (resp. their associated logging API routes), consolidate measurements by day. If we want to log another measurement, overwriting an old one, show a warning to indicate this. disparate measurements (e.g., weight and bodyfat) should not show this warning but simply be merged into one log entry for that day. [x] on /fitness/measure in the past measurments tab, show more than "Body measurements only" if we don't have Bodyweight logged. we can be a bit more elaborate in our syntax here tbh. [x] add a button on /fitness/measure/body-parts for each measurement directly below to say "Same value", instead of having to hit +, then - to lock in same number [x] BF graph (with trend line like weight graph) on /fitness/stats page. Emphasize relative changes, not absolute numbers in design (as we cannot trust those) (e.g., use start day of overview as 0% and then show +/- x % on the graph) [x] Workshop better names than "Measure" for the /fitness/measure route. It's about body data points (i.e., non-food related). What's a better, short name than "Measure" to capture the logging of weight, body composition, body part measurements, and period tracking? [x] on /fitness/stats/histoy/ for body measurement graphs, make the range reasonable. e.g., if we have 1 cm change, do not fill the entire y-height with 1 cm. Use reasonable padding for low ranges (i think we do something like htis already on the weight graph?) [x] on /fitness/check-in, Make the Period ended button a lot more prominent in the period tracker component. [x] swap heart emoji on recipe favorites to lucide icon [x] coop and migros cards on shopping list for scanning [x] login icon from lucide in header [ ] Investigate self-hosting BRouter [ ] Use the same color swisstopo map both for light and dark mode (currentyl only light mode) [ ] pre-compute required map tiles for all tiles on the route (and adjacent enough to be visibile by default on sane screen sizes) and create a fetch instruction for the server. (separate step: create a swiss-topo caching service which smoothly interpolates with non-switzerland service tiles for spots outside of switzerland) [ ] expand compatibility outside of switzerland with non-swiss topo map [ ] align design better with swizterland mobility [ ] allow for difficulty cardio, difficulty technique and T1-T6 labelling [ ] allow for Switzerland Mobility like hike icons (with alpine blue white blue, red white red, and yellow hiking shields as a fallback alternative) [ ] Add smoothing distance for elevation calculations on GPS-tracled workouts (3 meters? more?)
Refactor Recipe Search Component
Refactor src/lib/components/Search.svelte to use the new SearchInput.svelte component for the visual input part. This will:
- Reduce code duplication between recipe search and prayer search
- Keep the visual styling consistent across the site
- Separate concerns: SearchInput handles the UI, Search.svelte handles recipe-specific filtering logic
Files involved:
-
src/lib/components/Search.svelte- refactor to use SearchInput -
src/lib/components/SearchInput.svelte- the reusable input component-
$app/stores → $app/state (biggest, most mechanical) Old: import { page } from '$app/stores' + $page.url.pathname New: import { page } from '
app/state' + page.url.pathname (no, it's a rune now). Runes-based, smaller bundle (no store wrapper), cleaner SSR. Codebase has dozens of $app/stores imports — same kind of codemod-able migration as hrefs. Available since 2.12. $app/stores is deprecated. -
Convert legacy stores to .svelte.ts rune state Files like $lib/stores/recipeTranslation.ts, $lib/stores/language.ts use writable(). Modern pattern: .svelte.ts files with
state() + exported getters/setters. Better TS inference, noprefix, no auto-subscription gotchas. -
Remote functions for new API code ($app/server, since 2.27) Replaces hand-rolled +server.ts + client fetch with type-safe server functions called like normal funcs. Major refactor for existing /api/** (lots of files), so probably only adopt for new endpoints — not worth churning the existing ~80 API routes.
-
prerender = true audit Static-ish pages (faith catechesis, latin prayers, apologetics arguments) are great candidates. Skip-SSR for static content = faster cold loads + cheaper hosting. Currently nothing's prerendered — quick win where applicable.
-
@sveltejs/enhanced-img Transparent image optimization (responsive srcset, AVIF/WebP, blur placeholders) at build time. Recipe hero images and saint-day cards would benefit visibly. Drop-in via <enhanced:img src="...">.
-
{@attach} over use: (Svelte 5 attachments) Newer API for DOM-lifecycle hooks. Supports spread + library composition use: can't. Low urgency; only matters when writing new lifecycle code.
-
Shallow routing for modals/galleries pushState + flow lets modals participate in history without full navigation. Useful if you ever add a recipe-image lightbox or apologetics-arg overlay. Net-new feature, not a migration.
-