From abb59f46a6e8fe55643b5f4d9978148d0129a6da Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Thu, 23 Apr 2026 14:52:39 +0200 Subject: [PATCH] perf: Lucide subpath imports to split 748 KB icon chunk Barrel `from '@lucide/svelte'` imports pulled every referenced icon into one shared 748 KB client chunk. Switch every call site to per-icon subpaths (`@lucide/svelte/icons/`) so Vite tree-shakes each icon independently. Also logs the TODO list for the perf audit so we don't lose track. - 46 files, 106 unique icons - single `Minus as MinusIcon` alias preserved - Lucide-internal aliases (`AlertTriangle`, `BarChart3`) resolve through Lucide's own re-export shims; no behavioral change --- TODO.md | 18 + package.json | 2 +- src/lib/components/DatePicker.svelte | 5 +- src/lib/components/ErrorView.svelte | 7 +- src/lib/components/ThemeToggle.svelte | 4 +- src/lib/components/Toast.svelte | 2 +- .../faith/AngelusStreakCounter.svelte | 4 +- .../components/fitness/ExercisePicker.svelte | 9 +- src/lib/components/fitness/FoodSearch.svelte | 5 +- .../components/fitness/MacroBreakdown.svelte | 4 +- .../components/fitness/MealTypePicker.svelte | 5 +- .../components/fitness/PeriodTracker.svelte | 8 +- .../components/fitness/RoundOffCard.svelte | 7 +- src/lib/components/fitness/SessionCard.svelte | 7 +- src/lib/components/fitness/SetTable.svelte | 5 +- .../components/fitness/SyncIndicator.svelte | 6 +- .../components/fitness/TemplateCard.svelte | 3 +- .../components/fitness/VideoOverlay.svelte | 3 +- src/lib/components/fitness/WorkoutFab.svelte | 3 +- .../recipes/AddToFoodLogButton.svelte | 3 +- .../components/recipes/CreateStepList.svelte | 8 +- .../recipes/InstructionsPage.svelte | 7 +- .../components/tasks/StickerCalendar.svelte | 3 +- src/lib/components/tasks/TaskForm.svelte | 16 +- .../[cospendRoot=cospendRoot]/+layout.svelte | 5 +- .../list/+page.svelte | 25 +- .../[rosary=rosaryLang]/+page.svelte | 2 +- .../katechese/zehn-gebote/+page.svelte | 3 +- .../[recipeLang=recipeLang]/+layout.svelte | 7 +- src/routes/fitness/+layout.svelte | 7 +- .../[checkin=fitnessCheckIn]/+page.svelte | 14 +- .../body-parts/+page.svelte | 11 +- .../edit/[id]/+page.svelte | 2 +- .../[exercises=fitnessExercises]/+page.svelte | 10 +- .../[id]/+page.svelte | 3 +- .../[[month=fitnessMonth]]/+page.svelte | 3 +- .../[id]/+page.svelte | 16 +- .../[[date=fitnessDate]]/+page.svelte | 24 +- .../food/[source]/[id]/+page.svelte | 7 +- .../meals/+page.svelte | 6 +- .../fitness/[stats=fitnessStats]/+page.svelte | 12 +- .../[part]/+page.svelte | 6 +- .../[workout=fitnessWorkout]/+page.svelte | 16 +- .../[active=fitnessActive]/+page.svelte | 20 +- src/routes/measure-mock/+page.svelte | 1267 +++++++++++++++++ src/routes/tasks/+layout.svelte | 4 +- src/routes/tasks/+page.svelte | 23 +- src/routes/tasks/rewards/+page.svelte | 2 +- 48 files changed, 1582 insertions(+), 57 deletions(-) create mode 100644 src/routes/measure-mock/+page.svelte diff --git a/TODO.md b/TODO.md index 6ed80774..2ca1aef3 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,21 @@ # TODO +## Perf (audit 2026-04-23) + +Order = impact. Font items + app.html preload intentionally skipped. + +- [x] 1. Lucide subpath imports — convert `from '@lucide/svelte'` barrel imports to `@lucide/svelte/icons/` 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 `all_brief` endpoint — drop `JSON.parse(JSON.stringify(...))`, move shuffle client-side, enable caching +- [ ] 4. Favorites page — drop unnecessary `all_brief` fetch (verify consumer first) +- [ ] 5. Replace redundant `locals.auth()` with `locals.session` across recipe/calendar/fitness loaders +- [ ] 6. Stream fitness stats loader — return promises for slow panels +- [ ] 7. Overview endpoint — add `.select(...)` projection, cap timeseries window +- [ ] 8. Calendar payload trim — drop `name` from `yearDays`, pre-filter `feastDots` server-side +- [ ] 9. History sessions endpoint — slim exercise payload for list view +- [ ] 10. `Cache-Control` headers on stable API endpoints (all_brief, calendar, exercises metadata) +- [ ] 11. Search — debounce 200 ms + server-side pre-normalized `_searchKey` + ## 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. @@ -8,6 +24,8 @@ [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?) +[ ] Make the Period ended button a lot more prominent +[ ] swap heart emoji on recipe favorites to lucide icon ## Refactor Recipe Search Component diff --git a/package.json b/package.json index 0e669c9d..050a3e03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.46.11", + "version": "1.46.12", "private": true, "type": "module", "scripts": { diff --git a/src/lib/components/DatePicker.svelte b/src/lib/components/DatePicker.svelte index ffd8e9f6..1468969d 100644 --- a/src/lib/components/DatePicker.svelte +++ b/src/lib/components/DatePicker.svelte @@ -1,6 +1,7 @@ diff --git a/src/routes/tasks/+layout.svelte b/src/routes/tasks/+layout.svelte index 6dcc5a33..77efb065 100644 --- a/src/routes/tasks/+layout.svelte +++ b/src/routes/tasks/+layout.svelte @@ -2,8 +2,8 @@ import { page } from '$app/stores'; import Header from '$lib/components/Header.svelte'; import UserHeader from '$lib/components/UserHeader.svelte'; - import { ClipboardList, Trophy } from '@lucide/svelte'; - + import ClipboardList from '@lucide/svelte/icons/clipboard-list'; + import Trophy from '@lucide/svelte/icons/trophy'; let { data, children } = $props(); let user = $derived(data.session?.user); diff --git a/src/routes/tasks/+page.svelte b/src/routes/tasks/+page.svelte index ca74e466..3ae7f6df 100644 --- a/src/routes/tasks/+page.svelte +++ b/src/routes/tasks/+page.svelte @@ -3,9 +3,26 @@ import { confirm } from '$lib/js/confirmDialog.svelte'; import { formatDistanceToNow, isPast, isToday, differenceInDays, format } from 'date-fns'; import { de } from 'date-fns/locale'; - import { Plus, Check, Pencil, Trash2, Tag, Users, RotateCcw, Calendar, - Sparkles, Wind, Bath, UtensilsCrossed, CookingPot, WashingMachine, - Flower2, Droplets, Leaf, ShoppingCart, Shirt, Brush } from '@lucide/svelte'; + import Plus from '@lucide/svelte/icons/plus'; + import Check from '@lucide/svelte/icons/check'; + import Pencil from '@lucide/svelte/icons/pencil'; + import Trash2 from '@lucide/svelte/icons/trash-2'; + import Tag from '@lucide/svelte/icons/tag'; + import Users from '@lucide/svelte/icons/users'; + import RotateCcw from '@lucide/svelte/icons/rotate-ccw'; + import Calendar from '@lucide/svelte/icons/calendar'; + import Sparkles from '@lucide/svelte/icons/sparkles'; + import Wind from '@lucide/svelte/icons/wind'; + import Bath from '@lucide/svelte/icons/bath'; + import UtensilsCrossed from '@lucide/svelte/icons/utensils-crossed'; + import CookingPot from '@lucide/svelte/icons/cooking-pot'; + import WashingMachine from '@lucide/svelte/icons/washing-machine'; + import Flower2 from '@lucide/svelte/icons/flower-2'; + import Droplets from '@lucide/svelte/icons/droplets'; + import Leaf from '@lucide/svelte/icons/leaf'; + import ShoppingCart from '@lucide/svelte/icons/shopping-cart'; + import Shirt from '@lucide/svelte/icons/shirt'; + import Brush from '@lucide/svelte/icons/brush'; import { fly, scale } from 'svelte/transition'; import { flip } from 'svelte/animate'; import TaskForm from '$lib/components/tasks/TaskForm.svelte'; diff --git a/src/routes/tasks/rewards/+page.svelte b/src/routes/tasks/rewards/+page.svelte index 9c3acbb2..3ee187b1 100644 --- a/src/routes/tasks/rewards/+page.svelte +++ b/src/routes/tasks/rewards/+page.svelte @@ -6,7 +6,7 @@ import { de } from 'date-fns/locale'; import { scale } from 'svelte/transition'; import { flip } from 'svelte/animate'; - import { Trash2 } from '@lucide/svelte'; + import Trash2 from '@lucide/svelte/icons/trash-2'; import StickerCalendar from '$lib/components/tasks/StickerCalendar.svelte'; import StickerPopup from '$lib/components/tasks/StickerPopup.svelte';