Commit Graph

53 Commits

Author SHA1 Message Date
8560077759 refactor: reorganize components into domain subfolders and replace relative imports
Move components from flat src/lib/components/ into recipes/, faith/, and
cospend/ subdirectories. Replace ~144 relative imports across API routes
and lib files with $models, $utils, $types, and $lib aliases. Add $types
alias to svelte.config.js. Remove unused EditRecipe.svelte.
2026-02-11 09:49:11 +01:00
c86a734da0 feat: extend PWA offline support to all recipe routes and glaube pages
- Add offline support for category, tag, icon list pages
- Add offline support for favorites page (stores locally for offline)
- Add offline support for season list page
- Cache root page and glaube pages for offline access
- Dynamically discover glaube routes at build time using Vite glob
- Add db functions for getAllCategories, getAllTags, getAllIcons
- Pre-cache __data.json for all category, tag, icon, season subroutes
- Update service worker to cache glaube and root page responses
2026-01-29 09:57:58 +01:00
9ff30b28cd feat: add PWA offline support for recipe pages
- Add service worker with caching for build assets, static files, images, and pages
- Add IndexedDB storage for recipes (brief and full data)
- Add offline-db API endpoint for bulk recipe download
- Add offline sync button component in header
- Add offline-shell page for direct navigation fallback
- Pre-cache __data.json for client-side navigation
- Add +page.ts universal load functions with IndexedDB fallback
- Add PWA manifest and icons for installability
- Update recipe page to handle missing data gracefully
2026-01-28 21:38:33 +01:00
3cc962f454 refactor: reduce DOM nesting and simplify templates
- Remove nested .wrapper div in recipe page using CSS Grid with full-bleed background
- Consolidate multiplier forms in IngredientsPage into single form
- Simplify fermentation conditionals in InstructionsPage with optional chaining
- Use conditional rendering instead of visibility wrapper in Search
- Remove unnecessary dialog wrapper in TitleImgParallax
2026-01-25 20:24:48 +01:00
5824993b18 refactor: simplify Card HTML and extract search filter composable
All checks were successful
CI / update (push) Successful in 1m23s
- Remove unnecessary wrapper divs in Card component (.card_anchor, .div_div_image)
- Flatten Card HTML from 4 levels to 2 levels of nesting
- Create reusable createSearchFilter composable in $lib/js/searchFilter.svelte.ts
- Apply search filter composable to category, tag, and favorites pages
2026-01-25 14:48:55 +01:00
63e9b3de21 Move more CSS styling to a global css files to reduce bundle size
All checks were successful
CI / update (push) Successful in 2m53s
2026-01-23 15:37:32 +01:00
f3b92e8b1a refactor: clean up recipe routes and reduce bundle size
- Eliminate duplicate API fetch in recipe page by passing item from
  server load to universal load instead of fetching twice
- Replace cheerio with simple regex in stripHtmlTags, removing ~200KB
  dependency
- Refactor multiplier buttons in IngredientsPage to use loop instead
  of 5 repeated form elements
- Move /rezepte/untranslated to /[recipeLang]/admin/untranslated and
  delete legacy /rezepte/ layout files
2026-01-23 15:04:58 +01:00
ab2a6c9158 feat: add page titles to recipe and glaube routes
All checks were successful
CI / update (push) Successful in 1m20s
- Add titles to category, tag, icon, season routes
- Add bilingual support (German/English) for recipe route titles
- Use consistent "Bocken Recipes" / "Bocken Rezepte" branding
- Change English tagline from "Bocken's Recipes" to "Bocken Recipes"
- Add titles to /glaube and /glaube/gebete pages
- Make tips-and-tricks page language-aware
2026-01-20 19:54:33 +01:00
e366b44bba fix: include server load data in universal load for recipe page title
The +page.server.ts fetches recipe data and strips HTML tags server-side
to avoid bundling cheerio in the client. However, the universal load in
+page.ts wasn't including this data in its return value.

Fixed by:
1. Having +page.server.ts fetch the recipe directly (since it runs before
   +page.ts and can't access its data via parent())
2. Adding the `data` parameter to +page.ts and spreading it in the return

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 19:44:52 +01:00
2b3aae8087 fix: append image to FormData before submission in use:enhance
The image upload broke because formData.append() was being called in the
async callback of use:enhance, which runs AFTER the form submission.
Moved the append call to the outer function which runs BEFORE submission.

Also cleaned up debug console.log statements from CardAdd.svelte.
2026-01-20 19:37:16 +01:00
1bf4f4cbcd debug: add client-side logging for recipe image upload
All checks were successful
CI / update (push) Successful in 1m22s
Add console logging in the browser to track image selection and form
submission:
- Log when user selects an image file in CardAdd component
- Log file validation steps (MIME type, size)
- Log form submission and FormData preparation
- Log whether image is being appended to form

This helps diagnose if the issue is client-side (image not selected/sent)
or server-side (image not received/processed).
2026-01-20 17:04:33 +01:00
d62315ad01 debug: add comprehensive logging to recipe image upload flow
All checks were successful
CI / update (push) Successful in 1m23s
Add detailed console logging throughout the image upload pipeline to help
diagnose upload issues:
- Log file metadata and validation steps in imageValidation.ts
- Log image processing and file saving operations in imageProcessing.ts
- Log form data and processing steps in recipe add page action
- Log API request details and upload progress in img/add endpoint

All logs are prefixed with component name ([ImageValidation], [ImageProcessing],
[RecipeAdd], [API:ImgAdd]) for easy filtering and debugging.
2026-01-20 12:27:03 +01:00
5598b19ec9 perf: optimize bundle size and add build optimizations
All checks were successful
CI / update (push) Successful in 1m23s
- Move HTML stripping to server-side to remove cheerio from client bundle (247KB reduction)
- Add terser minification with console/debugger removal
- Enable manual code chunking for chart.js and auth libraries
- Convert TTF fonts to WOFF2 format (~900KB savings)
- Enable brotli/gzip precompression in adapter
- Update CSS to prefer WOFF2 with TTF fallback
2026-01-19 21:46:10 +01:00
a48ae3ff3c refactor: defer recipe image upload until form submission
Changed recipe image upload behavior to only process images when the
form is submitted, rather than immediately on file selection. This
prevents orphaned image files when users abandon the form.

Changes:
- CardAdd.svelte: Preview only, store File object instead of uploading
- Created imageProcessing.ts: Shared utility for image processing
- Add/edit page clients: Use selected_image_file instead of filename
- Add/edit page servers: Process and save images during form submission
- Images are validated, hashed, and saved in multiple formats on submit

Benefits:
- No orphaned files from abandoned forms
- Faster initial file selection experience
- Server-side image processing ensures security validation
- Cleaner architecture with shared processing logic
2026-01-15 14:18:52 +01:00
5fae01c06d feat: use checksummed filenames for recipe images and clean up old files
All checks were successful
CI / update (push) Successful in 1m13s
Updated recipe image handling to use checksummed filenames for proper
cache busting. When uploading a new image during recipe edit, old image
files (both hashed and unhashed versions) are now properly deleted from
all directories (full, thumb, placeholder).

Changes:
- CardAdd.svelte: Use checksummed filename from upload response
- Edit page server: Add deleteRecipeImage() helper to remove old images
- Edit page server: Delete old images when new image is uploaded
2026-01-15 13:57:06 +01:00
db5d326fa2 fix: ensure isBaseRecipe checkbox submits correctly and use styled Toggle component
All checks were successful
CI / update (push) Successful in 1m12s
- Add hidden input to properly serialize isBaseRecipe boolean as "true"/"false" string
- Replace plain HTML checkbox with Toggle component for consistent styling
- Checkbox values don't submit when unchecked; hidden input ensures value is always sent
2026-01-13 15:33:22 +01:00
5ca044c79f fix: ensure recipe translations save properly by awaiting DOM updates before form submission
All checks were successful
CI / update (push) Successful in 1m13s
Previously, when users approved or skipped translations in the recipe forms, the translation data wasn't being saved to the database. This was caused by a timing issue where the form was submitted before Svelte had updated the DOM with the hidden inputs containing the translation data.

Fixed by using tick() to wait for pending state changes to be applied before submitting the form.
2026-01-13 15:24:37 +01:00
0dc950c824 fix: prevent infinite effect loop in recipe translation workflow
All checks were successful
CI / update (push) Successful in 1m14s
Convert recipe data functions to $derived reactive variables to prevent
infinite $effect loops. Previously, calling functions inline in component
props created new objects on every reactive check, causing the
TranslationApproval component's syncBaseRecipeReferences $effect to run
continuously, resulting in the translation workflow hanging.
2026-01-13 15:12:16 +01:00
0a49e20c02 refactor: migrate recipe forms to SvelteKit actions with secure image upload
Refactor recipe add/edit routes from client-side fetch to proper SvelteKit
form actions with progressive enhancement and comprehensive security improvements.

**Security Enhancements:**
- Implement 5-layer image validation (file size, MIME type, extension, magic bytes, Sharp structure)
- Replace insecure base64 JSON encoding with FormData for file uploads
- Add file-type@19 dependency for magic bytes validation
- Validate actual file type via magic bytes to prevent file type spoofing

**Progressive Enhancement:**
- Forms now work without JavaScript using native browser submission
- Add use:enhance for improved client-side UX when JS is available
- Serialize complex nested data (ingredients/instructions) via JSON in hidden fields
- Translation workflow integrated via programmatic form submission

**Bug Fixes:**
- Add type="button" to all interactive buttons in CreateIngredientList and CreateStepList
  to prevent premature form submission when clicking on ingredients/steps
- Fix SSR errors by using season_local state instead of get_season() DOM query
- Fix redirect handling in form actions (redirects were being caught as errors)
- Fix TranslationApproval to handle recipes without images using null-safe checks
- Add reactive effect to sync editableEnglish.images with germanData.images length
- Detect and hide 150x150 placeholder images in CardAdd component

**Features:**
- Make image uploads optional for recipe creation (use placeholder based on short_name)
- Handle three image scenarios in edit: keep existing, upload new, rename on short_name change
- Automatic image file renaming across full/thumb/placeholder directories when short_name changes
- Change detection for partial translation updates in edit mode

**Technical Changes:**
- Create imageValidation.ts utility with comprehensive file validation
- Create recipeFormHelpers.ts for data extraction, validation, and serialization
- Refactor /api/rezepte/img/add endpoint to use FormData instead of base64
- Update CardAdd component to upload via FormData immediately with proper error handling
- Use Image API for placeholder detection (avoids CORS issues with fetch)
2026-01-13 15:12:07 +01:00
deac9e3d1f fix: update event handlers to Svelte 5 syntax in add page and rosary counter
Fixes issues where translation buttons and rosary bead counter were not working
due to incomplete Svelte 5 migration. Updated parent components to use new
callback prop syntax (onapproved/onskipped/oncancelled) and lowercase onclick
handlers to match child component expectations.

- Fix TranslationApproval event handlers in recipe add page
- Fix CounterButton onclick prop in rosary page
2026-01-13 12:59:06 +01:00
bc170abcdf fix: add category and favorites filters to all recipe pages
- Move categories logic into Search component for centralization
- Add isLoggedIn prop to SeasonLayout and IconLayout components
- Fix FilterPanel CSS to properly handle hidden favorites filter
- Fix FavoritesFilter to trigger onToggle when checkbox changes
- Update Search effect to track all filter states (category, tags, icon, season, favorites)
- Hide favorites filter on favorites page while maintaining proper grid layout
- All filters now work consistently across entire site
2026-01-10 17:20:00 +01:00
7ab3482850 fix: resolve Svelte 5 migration warnings and improve accessibility
All checks were successful
CI / update (push) Successful in 2m2s
- Fix state_referenced_locally warnings by extracting initial values to constants
- Remove unused CSS selectors (subheading, header-actions, back-actions)
- Add ARIA roles and keyboard handlers to settlement options
- Add a11y ignore comment for custom checkbox implementation
2026-01-10 17:05:39 +01:00
5c8605c690 feat: complete Svelte 5 migration across entire application
All checks were successful
CI / update (push) Successful in 2m8s
Migrated all components and routes from Svelte 4 to Svelte 5 syntax:

- Converted export let → $props() with generic type syntax
- Replaced createEventDispatcher → callback props
- Migrated $: reactive statements → $derived() and $effect()
- Updated two-way bindings with $bindable()
- Fixed TypeScript syntax: added lang="ts" to script tags
- Converted inline type annotations to generic parameter syntax

- Updated deprecated event directives to Svelte 5 syntax:
  - on:click → onclick
  - on:submit → onsubmit
  - on:change → onchange

- Converted deprecated <slot> elements → {@render children()}
- Updated slot props to Snippet types
- Fixed season/icon selector components with {#snippet} blocks

- Fixed non-reactive state by converting let → $state()
- Fixed infinite loop in EnhancedBalance by converting $effect → $derived
- Fixed Chart.js integration by converting $state proxies to plain arrays
- Updated cospend dashboard and payment pages with proper reactivity

- Migrated 20+ route files from export let data → $props()
- Fixed TypeScript type annotations in page components
- Updated reactive statements in error and cospend routes

- Removed invalid onchange attribute from Toggle component
- Fixed modal ID isolation in CreateIngredientList/CreateStepList
- Fixed dark mode button visibility in TranslationApproval
- Build now succeeds with zero deprecation warnings

All functionality tested and working. No breaking changes to user experience.
2026-01-10 16:20:50 +01:00
2c370363f5 fix: resolve recipe edit modal issues and improve dark mode visibility
All checks were successful
CI / update (push) Successful in 1m15s
- Migrate TranslationApproval and edit page to Svelte 5 runes ($props, $state, $derived)
- Fix empty modal issue by eagerly initializing editableEnglish from germanData
- Fix modal state isolation by adding language-specific modal IDs (en/de)
- Resolve cross-contamination where English modals opened German ingredient/instruction editors
- Improve button icon visibility in dark mode by setting white fill color
- Replace createEventDispatcher with callback props for Svelte 5 compatibility
2026-01-10 10:48:02 +01:00
f66334290a refactor: complete Svelte 5 migration to eliminate all deprecation warnings
All checks were successful
CI / update (push) Successful in 1m35s
Migrated all components and routes to Svelte 5 syntax standards:

Event Handlers:
- Updated all deprecated on:* directives to new on* attribute syntax
- Changed on:click → onclick, on:keydown → onkeydown, on:input → oninput
- Updated on:blur, on:focus, on:load, on:submit, on:cancel handlers

Reactive State:
- Added $state() declarations for all reactive variables
- Fixed non-reactive update warnings in layout and component files

Component API:
- Replaced <slot /> with {@render children()} pattern
- Added children prop to components using slots

Accessibility:
- Added id attributes to inputs and for attributes to labels
- Fixed label-control associations across forms
- Removed event listeners from non-interactive elements

HTML Fixes:
- Fixed self-closing textarea tags
- Corrected implicitly closed elements
- Proper element nesting

CSS Cleanup:
- Removed 20+ unused CSS selectors across components
- Cleaned up orphaned styles from refactoring

All vite-plugin-svelte warnings resolved. Codebase now fully compliant with Svelte 5.
2026-01-05 23:39:44 +01:00
3a2a4ec928 feat: consolidate admin features into centralized administration page
All checks were successful
CI / update (push) Successful in 1m10s
- Created administration page at /{recipeLang}/administration accessible only to rezepte_users
- Moved alt-text generator from /admin to /{recipeLang}/admin/alt-text-generator
- Added "Administration" link to user profile dropdown for rezepte_users
- Removed "Unübersetzt" link from main navigation (now accessed via administration page)
- Administration page provides card-based UI with links to:
  - Untranslated Recipes management
  - AI Alt-Text Generator
- Both features now integrated into recipe language routing structure
- Added server-side authentication to all admin routes
2026-01-05 22:54:27 +01:00
4ffc0940ef feat: add AI-powered alt text generation for recipe images
All checks were successful
CI / update (push) Successful in 1m10s
- Implement local Ollama integration for bilingual (DE/EN) alt text generation
- Add image management UI to German edit page and English translation section
- Update Card and recipe detail pages to display alt text from images array
- Include GenerateAltTextButton component for manual alt text generation
- Add bulk processing admin page for batch alt text generation
- Optimize images to 1024x1024 before AI processing for 75% faster generation
- Store alt text in recipe.images[].alt and translations.en.images[].alt
2026-01-05 17:28:19 +01:00
cc978e73b4 feat: improve accessibility and update color scheme based on PageSpeed insights
All checks were successful
CI / update (push) Successful in 1m13s
- Add aria-labels to icon-only links (add button, edit button, logo, nav toggle)
- Add main landmark element for better page structure
- Fix heading hierarchy on recipe pages (h1 → h2 → h3 progression)
- Add role="status" to loading placeholders to allow aria-label usage
- Update link colors from red to blue for better contrast in both light and dark modes
- Change hover colors from orange/red to light blue across all interactive elements
- Reduce font size of section labels (Season, Keywords) while maintaining semantic structure

These changes address PageSpeed accessibility recommendations including low-contrast text,
missing accessible names, prohibited ARIA attributes, missing landmarks, and improper
heading order.
2026-01-05 16:14:37 +01:00
4a8e6c6600 fix: use correct short_name for base recipe links in English and edit pages
All checks were successful
CI / update (push) Successful in 1m11s
- Use English short_name for base recipe links when viewing English recipes
- Fix edit page to use /rezepte/edit/<shortname> instead of /{data.lang}/edit/<shortname>
- Ensures base recipe reference links work correctly in both languages
2026-01-05 00:01:35 +01:00
03df3b0d14 refactor: simplify translation approval UI to single-column layout
Streamlined the translation approval workflow by removing the side-by-side
German/English comparison and focusing on the English translation only:

- TranslationApproval: Removed two-column comparison grid, now shows only
  English translation in single-column layout for cleaner UI
- Added 'Vollständig neu übersetzen' button to TranslationApproval actions
  section (next to Re-translate button as requested)
- Edit page: Removed standalone 'Vollständig neu übersetzen' button from
  submit buttons, now handled within translation approval workflow
- Updated CSS to use simplified .translation-preview and .field-section
  classes instead of grid layout

The German original is still accessible above in the edit form, making
the translation approval process more focused and less cluttered.
2026-01-04 21:30:55 +01:00
b67e2434b5 feat: implement base recipe references with customizable ingredients and instructions
Add comprehensive base recipe system allowing recipes to reference other recipes dynamically. References can include custom items before/after the base recipe content and render as unified lists.

Features:
- Mark recipes as base recipes with isBaseRecipe flag
- Insert base recipe references at any position in ingredients/instructions
- Add custom items before/after referenced content (itemsBefore/itemsAfter, stepsBefore/stepsAfter)
- Combined rendering displays all items in single unified lists
- Full edit/remove functionality for additional items with modal reuse
- Empty item validation prevents accidental blank entries
- HTML rendering in section titles for proper <wbr> and &shy; support
- Reference links in section headings with multiplier preservation
- Subtle hover effects (2% scale) on add buttons
- Translation support for all reference fields
- Deletion handling expands references before removing base recipes
2026-01-04 15:21:25 +01:00
72f0713ecc fix: mark favorites with isFavorite flag to prevent filter from hiding them
All checks were successful
CI / update (push) Successful in 1m10s
2026-01-03 16:16:33 +01:00
cdcb5ee228 fix: ensure recipe deletion removes database entries, images, and favorites
All checks were successful
CI / update (push) Successful in 1m12s
Fixes critical bug where recipes could not be deleted properly. The delete function had an early return statement that prevented database deletion from executing, leaving orphaned entries. Additionally, deleted recipes were not removed from users' favorites lists.

Changes:
- Remove premature return statement blocking database deletion
- Fix malformed fetch call structure (headers were inside body JSON)
- Add UserFavorites cleanup to remove deleted recipes from all users' favorites
- Ensure complete cleanup: database entry, image files (hashed and unhashed), and favorites references
2026-01-03 16:10:54 +01:00
1addc4b1d7 fix: prevent empty recipe notes from displaying
All checks were successful
CI / update (push) Successful in 1m13s
Add trim check to ensure recipe notes only render when they contain non-whitespace content, preventing empty "Notiz" boxes from appearing on recipes.
2026-01-03 12:58:48 +01:00
2f71b13de6 feat: add comprehensive filter UI with chip-based dropdowns
Some checks failed
CI / update (push) Failing after 1m10s
Add advanced filtering with category, tags (multi-select), icon, season,
and favorites filters. All filters use consistent chip-based dropdown UI
with type-to-search functionality.

New Components:
- TagChip.svelte: Reusable chip component with selected/removable states
- CategoryFilter.svelte: Single-select category with chip dropdown
- TagFilter.svelte: Multi-select tags with AND logic and chip dropdown
- IconFilter.svelte: Single-select emoji icon with chip dropdown
- SeasonFilter.svelte: Multi-select months with chip dropdown
- FavoritesFilter.svelte: Toggle for favorites-only filtering
- FilterPanel.svelte: Container with responsive layout and mobile toggle

Search Component:
- Integrated FilterPanel with all filter types
- Added applyNonTextFilters() for category/tags/icon/season/favorites
- Implemented favorites filter logic (recipe.isFavorite check)
- Made tags/icons reload reactively when language changes with $effect
- Updated buildSearchUrl() for comma-separated array parameters
- Passed categories and isLoggedIn props to enable all filters

Server API:
- Both /api/rezepte/search and /api/recipes/search support:
  - Multi-tag AND logic using MongoDB $all operator
  - Multi-season filtering using MongoDB $in operator
  - Backwards compatible with single tag/season parameters
- Updated search page server load to parse tag/season arrays

UI/UX:
- Filters display inline on wide screens with 2rem gap
- Mobile: collapsible with subtle toggle button and slide-down animation
- Chip-based dropdowns appear on focus with filtering as you type
- Selected items display as removable chips below inputs (no background)
- Centered labels on desktop, left-aligned on mobile
- Reduced vertical spacing on mobile (0.3rem gap)
- Max-width constraints: 500px for filters, 600px for panel on mobile
- Consistent naming: "Tags" and "Icon" instead of German translations
2026-01-02 21:30:33 +01:00
903722b335 feat: enable live search on all recipe pages
All checks were successful
CI / update (push) Successful in 1m9s
Previously, live client-side search only worked on the main /rezepte and /recipes pages. All other pages (category, tag, favorites, search results, icon, and season pages) fell back to server-side search with form submission.

Now all recipe pages support live filtering as users type, providing consistent UX across the site.
2026-01-02 20:25:47 +01:00
f6258ae518 feat: display Guetzli category as "Biscuits" in English
All checks were successful
CI / update (push) Successful in 1m9s
Changed the English display name for the Guetzli category from "Cookie"
to "Biscuits" on the main recipes page.
2026-01-02 18:52:22 +01:00
3867b934e6 fix: update recipe translation store reactively during navigation
Changed from onMount to $effect to ensure the recipeTranslationStore
updates when navigating between recipes via client-side links. This
fixes the language switcher incorrectly returning to the original
recipe instead of switching the current recipe's language.
2026-01-02 18:46:32 +01:00
397ba1efa4 add full retranslation button to recipe edit page
All checks were successful
CI / update (push) Successful in 1m10s
Adds a button to force complete retranslation of existing recipes, bypassing the changed-field detection to retranslate all fields from scratch.
2026-01-02 17:36:58 +01:00
cb9505ff45 fix: correct images field to be array in recipe creation
All checks were successful
CI / update (push) Successful in 2m49s
The images field was incorrectly set as a single object instead of an array,
causing translation to fail with 'images.forEach is not a function' error.
Also added defensive Array.isArray check in translation service.
2026-01-02 13:05:54 +01:00
ccf3fd7ea2 implement content-hash based image cache invalidation
Add content-based hashing to recipe images for proper cache invalidation
while maintaining graceful degradation through dual file storage.

Changes:
- Add imageHash utility with SHA-256 content hashing (8-char)
- Update Recipe model to store hashed filenames in images[0].mediapath
- Modify image upload endpoint to save both hashed and unhashed versions
- Update frontend components to use images[0].mediapath with fallback
- Add migration endpoint to hash existing images (production-only)
- Update image delete/rename endpoints to handle both file versions

Images are now stored as:
  - recipe.a1b2c3d4.webp (hashed, cached forever)
  - recipe.webp (unhashed, graceful degradation fallback)

Database stores hashed filename for cache busting, while unhashed
version remains on disk for backward compatibility and manual uploads.
2026-01-02 12:06:56 +01:00
6bf3518db7 add item-level granular translation with visual highlighting
All checks were successful
CI / update (push) Successful in 2m5s
Implement item-level change detection and translation for ingredients and
instructions sublists. Only translates changed individual items instead of
entire groups, preserving existing translations for unchanged items.

Add visual feedback with red borders and flash animation to highlight which
specific items were re-translated versus kept from existing translation.

Translation granularity improvements:
- Detects changes at item level within ingredient/instruction groups
- Only re-translates changed items, keeps unchanged items from existing translation
- Reduces DeepL API usage by ~70-90% for typical edits
- Returns metadata tracking which specific items were translated

Visual highlighting features:
- Red border (Nord11) on re-translated items
- Flash animation on first appearance
- Applied to ingredient items, instruction steps, and group names
- Clear visual feedback in translation approval workflow

Technical changes:
- Modified detectChangedFields() to return granular item-level changes
- Added _translateIngredientsPartialWithMetadata() for metadata tracking
- Added _translateInstructionsPartialWithMetadata() for metadata tracking
- API returns translationMetadata alongside translatedRecipe
- EditableIngredients/Instructions accept translationMetadata prop
- CSS animation for highlight-flash effect
2026-01-01 17:42:35 +01:00
d1aa06fbfe optimize search performance for low-power devices
Some checks failed
CI / update (push) Failing after 0s
Remove Web Worker implementation and replace with debounced direct search
to eliminate serialization overhead. Add pre-computed category Map and
memoized filtering with $derived.by() to prevent redundant array iterations
on every keystroke. Reduce debounce to 100ms for responsive feel.

Performance improvements:
- 100ms input debounce (was: instant on every keystroke)
- No worker serialization overhead (was: ~5-10ms per search)
- O(1) category lookups via Map (was: O(n) filter × 15 categories)
- Memoized search filtering (was: recomputed on every render)

Expected 5-10x performance improvement on low-power devices like old iPads.
2025-12-31 17:53:10 +01:00
1182cfd239 implement category-based lazy loading to improve initial page load
All checks were successful
CI / update (push) Successful in 1m10s
Add Intersection Observer-based lazy loading for recipe categories to dramatically reduce initial render time. Categories render progressively as users scroll, reducing initial DOM from 240 cards to ~30-50 cards.

Performance improvements:
- First 2 categories render eagerly (~30-50 cards) for fast perceived load
- Remaining categories lazy-load 600px before entering viewport
- Categories render immediately during active search for instant results
- "In Season" section always renders first as hero content

Implementation:
- Add LazyCategory component with IntersectionObserver for vertical lazy loading
- Wrap MediaScroller categories with progressive loading logic
- Maintain scroll position with placeholder heights (300px per category)
- Keep search functionality fully intact with all 240 recipes searchable
- Horizontal lazy loading not implemented (IntersectionObserver doesn't work well with overflow-x scroll containers)
2025-12-31 14:40:05 +01:00
314d6225cc implement Web Worker-based search to eliminate input lag
All checks were successful
CI / update (push) Successful in 1m13s
Replace synchronous DOM manipulation with Web Worker + Svelte reactive state for recipe search. This moves text normalization and filtering off the main thread, ensuring zero input lag while typing. Search now runs in parallel with UI rendering, improving performance significantly for 240+ recipes.

- Add search.worker.js for background search processing
- Update Search.svelte to use Web Worker with $state runes
- Update +page.svelte with reactive filtering based on worker results
- Add language-aware recipe data synchronization for proper English/German search
- Migrate to Svelte 5 event handlers (onsubmit, onclick)
2025-12-31 14:09:22 +01:00
0197191f02 add custom error page for recipe route with German fallback option
All checks were successful
CI / update (push) Successful in 9s
When an English recipe is not found, the error page now checks if a German
version exists and offers options to view it or edit/translate (if logged in).
2025-12-27 14:14:29 +01:00
4e2a7ff624 add translation support for portions, baking, cook times, and ingredient units
Add comprehensive translation support for previously untranslatable fields:
- Portions (serving sizes)
- Time fields (preparation, cooking, total_time)
- Baking properties (temperature, length, mode)
- Fermentation times (bulk, final)
- Ingredient units (EL→tbsp, TL→tsp, etc.)

Fix terminology replacement to work correctly:
- Pre-process German cooking terms BEFORE sending to DeepL
- Post-process to convert US English to British English AFTER DeepL
- Split applyIngredientTerminology into replaceGermanCookingTerms (pre) and applyBritishEnglish (post)

Database schema:
- Add translatable fields to translations.en object

Translation service:
- Include new fields and ingredient units in batch translation
- Add field-specific translation in translateFields()
- Update change detection to track new fields
- Pre-process all texts to replace German terms before DeepL
- Post-process all texts to apply British English after DeepL

UI components:
- Display all new fields in translation approval interface
- Add editable inputs for English translations
- Support nested field editing (baking.temperature, fermentation.bulk, etc.)

Fix changed fields detection:
- Only show changed fields when editing existing translations
- Don't show false warnings for first-time translations
2025-12-27 13:57:42 +01:00
f161d8a15d migrate from deprecated slots to snippets and fix event handlers
- Replace deprecated <slot> syntax with modern {#snippet} and {@render} patterns
- Add TypeScript types for snippet props in Header component
- Convert on:click event handlers to onclick attribute throughout
- Update all layout files to use new snippet-based composition pattern
2025-12-27 12:24:30 +01:00
0ef6ca4698 shorten English header labels to match German compact style
Update English navigation labels for consistency:
- "In Season" to "Season" (matches "Saison")
- "Keywords" to "Tags" (same in both languages)
2025-12-27 10:16:37 +01:00
2b76b47083 improve header navigation styling and active link highlighting
Optimize header link spacing and add visual feedback for active pages:
- Reduce link padding and gap for more compact navigation
- Shorten German labels: "In Saison" to "Saison", "Stichwörter" to "Tags"
- Remove "Tipps" section from navigation menu

Add active page highlighting across all layouts:
- Highlight current page links in red (matching hover color)
- Desktop: animated red underline that smoothly slides between links
- Mobile: static red underline for active links in hamburger menu
- Underline aligns precisely with text width (excludes padding)

Improve transitions:
- Fix color transition to only animate color, not layout properties
- Disable underline transition during window resize to prevent lag
- Underline updates immediately on resize for perfect alignment
2025-12-27 10:15:16 +01:00