Commit Graph

404 Commits

Author SHA1 Message Date
Alexander 1e62621bd9 feat: add AI-powered alt text generation for recipe images
- 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
Alexander 33718f0788 feat: improve accessibility and update color scheme based on PageSpeed insights
- 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
Alexander e010f7045f fix: use correct short_name for base recipe links in English and edit pages
- 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
Alexander 524efda272 fix: enable nested base recipe references to display correctly
- Add recursive population for nested base recipe references (up to 3 levels deep) in API endpoints
- Implement recursive mapping of baseRecipeRef to resolvedRecipe for all nesting levels
- Add recursive flattening functions in frontend components to handle nested references
- Fix TranslationApproval to use short_name instead of ObjectId for base recipe lookups
- Add circular reference detection to prevent infinite loops

This ensures that when Recipe A references Recipe B as a base, and Recipe B references Recipe C, all three recipes' content is properly displayed.
2026-01-04 23:41:53 +01:00
Alexander 2ba3044d15 feat: add comprehensive base recipe translation support
- Add language prop to CreateIngredientList and CreateStepList components
  - Support both 'de' and 'en' with translation dictionaries
  - All UI labels now respect the lang prop

- Implement syncBaseRecipeReferences() in TranslationApproval
  - Always runs on component mount (not just for new translations)
  - Fetches English names for base recipe references
  - Merges German structure with existing English translations
  - Preserves existing translations while adding new base recipe refs

- Enhance partial translation in translation.ts
  - Handle base recipe reference fields (itemsBefore/itemsAfter, stepsBefore/stepsAfter)
  - Detect changes using JSON comparison
  - Only re-translate fields that changed
  - Ensures additional items/steps in base recipe refs are preserved during updates
2026-01-04 22:25:34 +01:00
Alexander 6986661718 refactor: use CreateIngredientList and CreateStepList in translation approval UI
Replaced the plain EditableIngredients and EditableInstructions components
with the styled CreateIngredientList and CreateStepList components to match
the German recipe editing UI above:

- Now displays English translation with same styling as German recipe
- Ingredients and instructions shown in familiar two-column layout
- Timing fields (preparation, baking, fermentation, cooking, total_time)
  integrated into CreateStepList component instead of separate fields
- Added getters/setters for add_info object to enable two-way binding
  between CreateStepList edits and editableEnglish data
- Removed redundant field editors for baking/fermentation since they're
  now part of CreateStepList

Translation approval UI now has consistent styling with the rest of the
edit page for a more cohesive user experience.
2026-01-04 21:35:55 +01:00
Alexander 902a02b74b 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
Alexander 286fc7a3ca feat: add translation editing support for base recipe reference fields
Enhanced translation approval UI to allow editing translated text in base
recipe references:

- EditableIngredients: Added support for editing labelOverride, itemsBefore,
  and itemsAfter fields with visual distinction for base recipe references
- EditableInstructions: Added support for editing labelOverride, stepsBefore,
  and stepsAfter fields with organized sections
- TranslationApproval: Updated German side to display base recipe reference
  fields (labelOverride, items/steps before/after) in read-only view

Users can now edit all auto-translated fields in base recipe references
including additional ingredients/instructions added before or after the
base recipe content.
2026-01-04 20:54:49 +01:00
Alexander 4521111406 fix: ensure base recipe references display correctly in English and auto-translate
Fixed three issues with base recipe translation support:

1. Base recipe content not loading in English - English API endpoint now
   populates baseRecipeRef fields to resolve base recipe data
2. itemsBefore/itemsAfter and stepsBefore/stepsAfter not being detected as
   changed - enhanced change detection to properly track all base recipe
   reference fields for re-translation
3. Base recipe name labels showing German text in English view - display
   components now use translated base recipe names as label fallback
2026-01-04 20:45:52 +01:00
Alexander 26e1a4a2f7 fix: ensure edit modals close properly on Enter and Escape
- Add setTimeout to defer modal.close() to next tick for proper Svelte binding updates
- Add HTMLDialogElement type casting with null checks for modal elements
- Add cancel event handlers to reset state when Escape is pressed
- Ensures modals close reliably when Enter is pressed to submit
- Prevents orphaned state when modals are dismissed with Escape
2026-01-04 15:28:46 +01:00
Alexander 327aa6824b 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
Alexander f11bb1dcf5 feat: add lock icons to restricted links on homepage
Add small lock icons in the top right corner of links that require authentication (streaming, family photos, cloud, shopping, family tree, transmission, documents, and audiobooks). The icons use SVG symbol references for efficient reuse and adapt to dark mode automatically.
2026-01-03 22:49:22 +01:00
Alexander ee387159ba feat: add untranslated recipes page for recipe admins
Add new page at /rezepte/untranslated for recipe admins to view and manage recipes without approved English translations. Includes translation status tracking, statistics dashboard, and visual badges.

Changes:
- Add API endpoint to fetch recipes without approved translations
- Create untranslated recipes page with auth checks for rezepte_users group
- Add translation status badges to Card component (pending, needs_update, none)
- Add database index on translations.en.translationStatus for performance
- Create layout for /rezepte route with header navigation
- Add "Unübersetzt" link to navigation for authorized users
2026-01-03 20:03:36 +01:00
Alexander 7e2fda3dca fix: sync language selector with browser back/forward navigation
Replace non-reactive window.location.pathname with SvelteKit's reactive $page store to ensure language selector updates when navigating via browser back/forward buttons.
2026-01-03 19:39:15 +01:00
Alexander e2dab562bd fix: mark favorites with isFavorite flag to prevent filter from hiding them 2026-01-03 16:16:33 +01:00
Alexander 6fb0c3b68a fix: ensure recipe deletion removes database entries, images, and favorites
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
Alexander b88c432f47 fix: prevent empty recipe notes from displaying
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
Alexander 9581d6727b fix: prevent input field overflow on mobile by ensuring equal margins
Adds box-sizing: border-box to all filter inputs after 'all: unset' to ensure padding is included within the 100% width calculation, preventing horizontal overflow and ensuring equal left/right margins on small screens.
2026-01-03 12:58:40 +01:00
Alexander cfcd913b0f fix: update deployment workflow to use dist directory
Changes deployment process to build in default 'build' directory, then safely deploy to 'dist' directory by stopping the service first, ensuring clean deployment without serving partial builds.
2026-01-02 22:12:46 +01:00
Alexander 8e18c15593 fix: restore vertical filter layout on mobile and enhance dropdown shadows
Fixed CSS specificity issue where filter-panel classes were preventing vertical stacking on small screens. Also added drop-shadow to all filter dropdowns for improved visual depth.
2026-01-02 22:03:29 +01:00
Alexander a75936fdd6 fix: correct deployment workflow to force sync with remote
Fix the deployment script to properly force the remote server to always
match the git repository state, regardless of local changes.

Changes:
- Replace invalid `git pull --force` with proper fetch and reset
- Add `git remote set-url origin` to ensure correct URL with auth token
- Use `git fetch origin` to download latest changes
- Use `git reset --hard origin/master` to force match remote state

This ensures clean deployments even if there are local modifications or
conflicts on the remote server, while preserving untracked files like .env.
2026-01-02 21:46:37 +01:00
Alexander 6f4507492a feat: add graceful degradation and conditional favorites filter
Add progressive enhancement to hide filter panel when JavaScript is
disabled, and conditionally render favorites filter based on login status.

Search Component:
- Added showFilters state (default false)
- Set showFilters to true in onMount when JS is enabled
- Wrapped FilterPanel in {#if showFilters} for graceful degradation
- Filters hidden without JavaScript, visible with JS

FilterPanel:
- Split grid layout into two variants:
  - with-favorites: 5 columns (120px 120px 1fr 160px 90px)
  - without-favorites: 4 columns (120px 120px 1fr 160px)
- Conditionally render FavoritesFilter only when isLoggedIn
- Apply appropriate class based on login status

FavoritesFilter:
- Simplified template (no internal login check)
- Only rendered when user is logged in via FilterPanel

UX:
- Non-JS browsers: Simple search only, filters gracefully hidden
- Not logged in: 4-column layout without favorites filter
- Logged in: 5-column layout with favorites filter
2026-01-02 21:41:24 +01:00
Alexander b98d4b7007 feat: add comprehensive filter UI with chip-based dropdowns
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
Alexander 402cb015df feat: enable live search on all recipe pages
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
Alexander 18fb52170b feat: display Guetzli category as "Biscuits" in English
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
Alexander 03f3c1d5e6 feat: reset to selection page when switching language on category/tag pages
When switching languages on specific category or tag pages, redirect to
the selection page instead of trying to maintain the same category/tag,
since category and tag names differ between languages. Icon pages continue
to swap directly since icons are consistent across languages.
2026-01-02 18:52:08 +01:00
Alexander 242868394b chore: add hard reset to deployment workflow
Ensures deployment always matches remote state by performing a hard
reset before building.
2026-01-02 18:46:42 +01:00
Alexander ee64904caf 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
Alexander e1082e27b7 add full retranslation button to recipe edit page
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
Alexander d290c2266c fix: filter English API endpoints to only return approved translations
Previously, all English recipe API endpoints were returning any recipe with
a translations.en object, regardless of approval status. This caused 218
recipes to appear instead of only approved ones.

Updated all 9 English API endpoints to filter for translationStatus='approved':
- /api/recipes/items/all_brief
- /api/recipes/items/in_season/[month]
- /api/recipes/items/category and /api/recipes/items/category/[category]
- /api/recipes/items/tag and /api/recipes/items/tag/[tag]
- /api/recipes/items/icon/[icon]
- /api/recipes/search
- /api/recipes/favorites/recipes
2026-01-02 17:36:44 +01:00
Alexander 1032add435 fix: correct images field to be array in recipe creation
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
Alexander 61d31957de chore: remove migration scripts and endpoint after successful migration
Migration completed successfully. Removing one-time migration files:
- Migration endpoint (api/admin/migrate-image-hashes)
- Migration shell script
- Migration documentation

Core image hashing functionality remains in place for all future uploads.
2026-01-02 12:37:22 +01:00
Alexander 18cdcf00cb fix: correct IMAGE_DIR path to /var/www/static
Change production path check from /var/lib/www to /var/www/static
to match actual production environment configuration.

Updated migration endpoint and all documentation references.
2026-01-02 12:25:17 +01:00
Alexander 4c74d107d6 fix: use correct dbConnect export name in migration endpoint 2026-01-02 12:17:39 +01:00
Alexander f56003c442 add admin token authentication for migration script
Allow migration to run without browser session by using ADMIN_SECRET_TOKEN
environment variable. This enables running the migration directly on the
server via SSH.

Changes:
- Add ADMIN_SECRET_TOKEN support to migration endpoint
- Update shell script to read token from environment
- Improve script with better error handling and token validation
- Update documentation with admin token setup instructions

The endpoint now accepts authentication via either:
  - Valid user session (browser-based)
  - ADMIN_SECRET_TOKEN from environment (server-based)

Usage on server:
  source .env && ./scripts/migrate-image-hashes.sh
2026-01-02 12:13:43 +01:00
Alexander fc24f46145 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
Alexander b56ba6fab2 add item-level granular translation with visual highlighting
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
Alexander b61943a920 optimize search performance for low-power devices
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
Alexander c6af6f57b3 fix duplicate image IDs and migrate TitleImgParallax to Svelte 5 runes
Replace id="image" with class="image" in both Card and TitleImgParallax
components to prevent duplicate IDs when multiple instances appear on the
same page. Update TitleImgParallax to use Svelte 5 $props() and $state()
runes instead of legacy export let syntax, and modernize event handlers
to use onload/onclick attributes.
2025-12-31 17:53:03 +01:00
Alexander b09b70f5ea implement category-based lazy loading to improve initial page load
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
Alexander 229d2fca33 implement Web Worker-based search to eliminate input lag
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
Alexander bcf2ce3c39 use English translations for portions and timing fields in recipe API
Fixes issue where English recipes always displayed German portions and timing metadata. The API now prioritizes English translations for portions, baking, preparation, fermentation, cooking, and total_time fields, falling back to German when translations aren't available.
2025-12-27 16:16:27 +01:00
Alexander a21c233c52 convert legacy reactive statement to $effect in cospend layout
Replace `$:` reactive statement with `$effect` rune to fix build error in Svelte 5 runes mode.
2025-12-27 16:05:49 +01:00
Alexander 56a4400a2b add custom error page for recipe route with German fallback option
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
Alexander f92bd9944d fix partial field translation overwriting entire translation
When re-translating only changed fields (e.g., just ingredients), the partial
result was replacing the entire English translation, causing name, short_name,
description, and category to be lost.

Now merge partial translations with existing translation data to preserve
unchanged fields while updating only the modified ones.
2025-12-27 14:00:18 +01:00
Alexander ace23e09b0 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
Alexander c6d342c30a improve translation with hardcoded terminology dictionary and British English
Add ingredient terminology dictionaries to override DeepL translations for consistent cooking terminology:
- German cooking terms (EL→tbsp, TL→tsp, Ei→egg, etc.)
- US to British English conversions (zucchini→courgette, eggplant→aubergine, etc.)

Change DeepL target language from EN to EN-GB to force British English translations.

Apply post-processing to all translated text to ensure terminology consistency.
2025-12-27 13:43:24 +01:00
Alexander 917d7aa6f3 scope ul font-size rule to prevent global CSS pollution
The global 'ul' selector was affecting all unordered lists across the app after visiting /glaube/rosenkranz. This caused layout issues with action buttons on recipe pages where the internal symbols would shift to the top instead of being centered.

Fixed by scoping the rule to only apply to ul elements within .gebet containers.
2025-12-27 13:31:40 +01:00
Alexander 1825976248 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
Alexander 596afcb4da move language selection to store to ensure alignment between different language selectors (mobile and desktop) 2025-12-27 12:11:53 +01:00