diff --git a/.cursor/debug.log b/.cursor/debug.log new file mode 100644 index 0000000..e257028 --- /dev/null +++ b/.cursor/debug.log @@ -0,0 +1,28 @@ +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959674202,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959674202,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"black","size":"small","className":"w-12 h-12"},"timestamp":1766959682790,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959682805,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_black_small","fallbackPath":"/media/logo_black.png","processedPath":"/media/logos/logo_black_small@1x.png"},"timestamp":1766959682790,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959682790,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959682790,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959682805,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"black","size":"small","className":"w-12 h-12"},"timestamp":1766959682796,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_black_small","fallbackPath":"/media/logo_black.png","processedPath":"/media/logos/logo_black_small@1x.png"},"timestamp":1766959682796,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"black","size":"small","className":"w-12 h-12"},"timestamp":1766959683932,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_black_small","fallbackPath":"/media/logo_black.png","processedPath":"/media/logos/logo_black_small@1x.png"},"timestamp":1766959683933,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959683934,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959683934,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"black","size":"small","className":"w-12 h-12"},"timestamp":1766959684064,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_black_small","fallbackPath":"/media/logo_black.png","processedPath":"/media/logos/logo_black_small@1x.png"},"timestamp":1766959684064,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959684073,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959684073,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"black","size":"small","className":"w-12 h-12"},"timestamp":1766959685074,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_black_small","fallbackPath":"/media/logo_black.png","processedPath":"/media/logos/logo_black_small@1x.png"},"timestamp":1766959685074,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959685074,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959685075,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:35","message":"Logo image load error","data":{"variant":"black","size":"small","attemptedPath":"/media/logo_black.png"},"timestamp":1766959685082,"sessionId":"debug-session","runId":"run1","hypothesisId":"H"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"black","size":"small","className":"w-12 h-12"},"timestamp":1766959685083,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_black_small","fallbackPath":"/media/logo_black.png","processedPath":"/media/logos/logo_black_small@1x.png"},"timestamp":1766959685084,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:15","message":"Logo component render","data":{"variant":"white","size":"medium","className":"w-16 h-16 md:w-24 md:h-24"},"timestamp":1766959685084,"sessionId":"debug-session","runId":"run1","hypothesisId":"E"} +{"location":"Logo.tsx:22","message":"Logo path construction","data":{"basePath":"/media/logos/logo_white_medium","fallbackPath":"/media/logo_white.png","processedPath":"/media/logos/logo_white_medium@1x.png"},"timestamp":1766959685084,"sessionId":"debug-session","runId":"run1","hypothesisId":"F"} +{"location":"Logo.tsx:35","message":"Logo image load error","data":{"variant":"white","size":"medium","attemptedPath":"/media/logo_white.png"},"timestamp":1766959685090,"sessionId":"debug-session","runId":"run1","hypothesisId":"H"} diff --git a/.cursor/plans/convert_vite_react_app_to_astro_2308e4b5.plan.md b/.cursor/plans/convert_vite_react_app_to_astro_2308e4b5.plan.md new file mode 100644 index 0000000..6e6b533 --- /dev/null +++ b/.cursor/plans/convert_vite_react_app_to_astro_2308e4b5.plan.md @@ -0,0 +1,326 @@ +--- +name: Convert Vite React App to Astro +overview: "" +todos: [] +--- + +# Convert Vite React App to Astro + +Convert the Vite React SPA in `dev/generated/` to an Astro application following Astro conventions, preserving functionality and design while leveraging Astro's file-based routing and island architecture. + +## Architecture Overview + +The conversion will transform the single-page React app into an Astro multi-page application with: + +- File-based routing: `/` (home) and `/publications` (publications page) +- Astro layouts for shared structure (header, footer) +- React islands for interactive components (language switcher, filters, scroll animations) +- Client-side language switching preserved +- Design system integrated via TailwindCSS configuration + +**Key Principle: Reuse Existing Code** + +- Copy React components from `dev/generated/components/` with minimal modifications +- Extract header/footer logic from `App.tsx` rather than rewriting +- Preserve all scroll animations, state management, and interactivity as-is +- Only adapt what's necessary for Astro integration (imports, props passing) + +## Implementation Steps + +### 1. Setup and Configuration + +**Files to create/modify:** + +- `tailwind.config.mjs` - Configure TailwindCSS with brand colors, fonts, and animations from `design.json` +- `src/styles/global.css` - Update with brand colors, fonts, and custom animations +- `astro.config.mjs` - Ensure React integration is configured (already present) + +**Key changes:** + +- Extract Tailwind config from `index.html` to proper Tailwind config file +- Add brand color palette, font families (Inter, Oswald, Permanent Marker), and custom animations (blob, float, marquee) +- Configure font loading in BaseLayout + +### 2. Content Structure + +**Files to copy (reuse as-is):** + +- `src/content/content.ts` - **Copy** `dev/generated/content.ts` here (bilingual content structure) +- `src/types.ts` - **Copy** `dev/generated/types.ts` here (TypeScript types) + +**Key changes:** + +- Keep content structure as TypeScript module (not content collections) since it's structured data, not markdown +- Preserve bilingual `CONTENT` object structure exactly as it exists +- No modifications needed - these are pure data/type files + +### 2.5. Logo Image Processing + +**Files to create:** + +- `src/utils/process-logos.js` - Script to process logo images with ffmpeg + +**Process logo images:** + +- Source images: `public/media/logo_black.png`, `public/media/logo_col.png`, `public/media/logo_white.png` (786x1334px) +- Preserve aspect ratio: ~0.589 (786/1334) +- Generate multiple sizes for different use cases: +- Small (header): 28x48px (1x), 56x96px (2x) +- Medium (footer): 57x96px (1x), 114x192px (2x) +- Large (hero/featured): 118x200px (1x), 236x400px (2x) +- Formats: WebP (modern), AVIF (best compression), PNG (fallback) +- Output location: `public/media/logos/` with naming: `logo_{variant}_{size}@{density}.{format}` + +**ffmpeg commands:** + +- Resize preserving aspect ratio +- Convert to WebP and AVIF formats +- Maintain quality while optimizing file size + +### 3. Component Conversion + +**Strategy: Copy and Adapt (Minimal Changes)Pure Astro components (new, replacing SVG):** + +- `src/components/Logo.astro` - **NEW** Astro component using processed logo images +- Accept props: `variant` ('black' | 'col' | 'white'), `size` ('small' | 'medium' | 'large'), `className` +- Use Astro's `` component for optimization +- Provide srcset for responsive images (1x, 2x) +- Fallback to PNG if WebP/AVIF not supported +- **Replaces** `dev/generated/components/Logo.tsx` (SVG version) + +**React Components - Copy from `dev/generated/components/` with minimal changes:** + +- `src/components/Reveal.tsx` - **COPY** `dev/generated/components/Reveal.tsx` as-is +- No changes needed - pure React component with Intersection Observer +- Use as React island (client:load) +- `src/components/Button.tsx` - **COPY** `dev/generated/components/Button.tsx` as-is +- No changes needed - reusable button component +- Use as React island (client:load) +- `src/components/ArticleCard.tsx` - **COPY** `dev/generated/components/ArticleCard.tsx` as-is +- No changes needed - article card component +- Use as React island (client:load) if interactive +- `src/components/Home.tsx` - **COPY** `dev/generated/components/Home.tsx` with minimal adaptions +- Keep all scroll animations, parallax effects, and state management exactly as-is +- Only change: Update Logo import to use new Logo.astro (or pass as prop) +- Update import paths if needed (content.ts, types.ts) +- Use as React island (client:load) +- `src/components/Publications.tsx` - **COPY** `dev/generated/components/Publications.tsx` with minimal adaptions +- Keep all filter logic, state management exactly as-is +- Only change: Update import paths if needed (types.ts, Reveal.tsx) +- Use as React island (client:load) + +**Header/Footer - Extract from App.tsx:** + +- `src/components/Header.tsx` - **EXTRACT** header JSX from `App.tsx` (lines 54-130) +- Extract the `