- Add POST /api/n8n/hardcover/sync-books — n8n calls this after detecting
finished books in Hardcover. Authenticates via N8N_SECRET_TOKEN/N8N_API_KEY,
deduplicates by hardcover_id, creates new book_reviews entries in Directus.
- Add getBookReviewByHardcoverId() + createBookReview() to lib/directus.ts.
Check uses GraphQL filter; create uses Directus REST POST /items/book_reviews.
- ReadBooks: replace silent return null with a visible empty state so the
section stays visible with a hint until the n8n sync populates it.
- Projects: add "No projects yet." placeholder instead of blank grid when
both Directus and PostgreSQL return no data.
- Add home.about.readBooks.empty i18n key (EN + DE).
n8n workflow setup:
Schedule → HTTP Hardcover GraphQL (books_read) → Code (transform) →
POST /api/n8n/hardcover/sync-books with array of { hardcover_id, title,
author, image, rating, finished_at }
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace ShaderGradientBackground WebGL shader (3 static spheres) with pure
CSS radial-gradient divs — moves from ClientProviders (deferred JS) to
app/layout.tsx as a server component rendered in initial HTML. Eliminates
@shadergradient/react, three, @react-three/fiber from the JS bundle.
Removes chunks/7001 (~20s CPU eval) and the 39s main thread block.
- Remove optimizeCss/critters: it was converting <link rel="stylesheet"> to a
JS-deferred preload, which PageSpeed read as a 410ms sequential CSS chain.
Both CSS files now load as parallel <link> tags from initial HTML (~150ms).
- Update browserslist safari >= 15 → 15.4 (Array.prototype.at, Object.hasOwn
are native in 15.4+; eliminates unnecessary SWC compatibility transforms).
- Delete orphaned app/styles/ghostContent.css (never imported anywhere, 3.7KB).
- Add .claude/ dev team setup: 5 subagents (frontend-dev, backend-dev, tester,
code-reviewer, debugger), 3 skills (/add-section, /review-changes,
/check-quality), 3 path-scoped rules, settings.json with auto-lint hook.
- Update CLAUDE.md with server/client orchestrator pattern, SSR animation
safety rules, API route conventions, and improved command reference.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
requestIdleCallback is unavailable in Safari < 16.4, causing GatedProviders
to crash during hydration and blank the entire page. Added setTimeout fallback.
Also added IntersectionObserver fallback in ScrollFadeIn for safety.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ScrollFadeIn uses IntersectionObserver + CSS transitions instead of
Framer Motion's initial prop. Key difference: no inline style in SSR
HTML, so content is visible by default. Animation only activates
after client hydration (hasMounted check).
Wraps About, Projects, Contact, Footer in HomePageServer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Framer Motion's initial={{ opacity: 0 }} was rendered as inline
style='opacity:0' in SSR HTML. If client-side JS failed to hydrate
properly, sections stayed permanently invisible.
Removed whileInView scroll animations from About, Projects, Contact.
Modal animations (AnimatePresence) kept as they only render on interaction.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dynamic() caused Framer Motion's initial opacity:0 to be baked into
SSR HTML, but client-side hydration never triggered the animations.
Direct imports ensure Framer Motion properly takes over on hydration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ssr:false caused sections to only render client-side, making them
invisible if any JS error occurred. Keep dynamic() for code-splitting
but allow server-side rendering.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove @sentry/nextjs and all related files since it was never actively used.
- Delete sentry.server.config.ts, sentry.edge.config.ts
- Delete sentry-example-page and sentry-example-api routes
- Clean up instrumentation.ts, global-error.tsx, middleware.ts
- Remove Sentry env vars from env.example and docs
- Update CLAUDE.md, copilot-instructions.md, PRODUCTION_READINESS.md
Middleware bundle reduced from 86KB to 34.8KB (-51KB).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove framer-motion from Hero.tsx and HeaderClient.tsx, replace with CSS animations/transitions
- Replace lucide-react icons (Menu, X, Mail) with inline SVGs in HeaderClient.tsx
- Lazy-load About, Projects, Contact, Footer via dynamic() imports in ClientWrappers.tsx
- Defer ShaderGradient/BackgroundBlobs loading via requestIdleCallback in ClientProviders.tsx
- Remove AnimatePresence page wrapper that caused full re-renders
- Enable experimental.optimizeCss (critters) for critical CSS inlining
- Add fadeIn keyframe to Tailwind config for CSS-based animations
Homepage JS reduced from 563KB to 438KB (-125KB).
Eliminates ~39s main thread work from WebGL init and layout thrashing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add WebGL support detection in ShaderGradientBackground to prevent console errors
- Add .catch() fallback to ShaderGradientBackground dynamic import
- Remove hardcoded aria-label from consent banner minimize button (fixes label-content-name-mismatch)
- Use rewrite instead of redirect for root locale routing (eliminates one redirect hop)
- Change n8n API cache headers from no-store to no-cache (enables bfcache)
- Add three and @react-three/fiber to optimizePackageImports for better tree-shaking
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Disable Sentry in all 3 configs (client/server/edge) - replayIntegration
was recording every DOM mutation causing overhead in Chrome
- Remove grain-overlay div and its CSS (SVG feTurbulence + mix-blend-mode:overlay
forces software compositing in Chrome on every frame)
- Remove mix-blend-multiply from BackgroundBlobs (prevents Chrome GPU compositing)
- Delete unused Grain.tsx, ShaderGradientBackground.tsx and its client wrapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ShaderGradientBackground used 3 full-screen Three.js WebGL canvases
with a blur(150px) CSS filter, crashing Lighthouse and causing severe
lag in Chrome. BackgroundBlobs also had 7 elements with blur(100-120px)
and per-frame mouse spring tracking compounding the issue.
- Remove ShaderGradientBackground from layout (WebGL not needed for a blur effect)
- Reduce BackgroundBlobs blur from 100-120px to 60px
- Remove mouse tracking spring animations from BackgroundBlobs
- Reduce to 4 blobs (remove 3 least visible)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
next/dynamic with ssr:false is not allowed in Server Components.
Follows existing BackgroundBlobsClient pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `prefix` param to checkRateLimit/getRateLimitHeaders so each endpoint
has its own bucket (previously all shared `admin_${ip}`, causing 429s when
analytics/track incremented past n8n endpoints' lower limits)
- n8n/hardcover/currently-reading → prefix 'n8n-reading'
- n8n/status → prefix 'n8n-status'
- analytics/track → prefix 'analytics-track'
- Remove custom analytics system (AnalyticsProvider, lib/analytics,
lib/useWebVitals, all /api/analytics/* routes) — was causing 500s in
production due to missing PostgreSQL PageView table
- Remove analytics consent toggle from ConsentBanner/ConsentProvider
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove hardcoded Dennis Konkol idle quote from rotation
- Double quote pool (5 → 12 quotes per locale)
- Start at a random quote on page load
- Cycle to a random non-repeating quote every 10s instead of sequential
- Fix dev-deploy.yml: postgres:15-alpine → postgres:16-alpine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hero: smoother font scaling (text-[2.75rem] -> sm -> md -> lg), smaller
photo on mobile, reduced gaps and padding
- About: responsive bento grid with smaller border-radius, compact hobbies
grid (2-col on mobile), hidden descriptions on small screens
- Projects: wider aspect ratio on mobile (16/10), show tags from sm:,
smoother title scaling
- Contact: compact form inputs, responsive connect links, smaller gaps
- Footer: reduced top padding and gap on mobile
- HomePage: smaller wave separators (h-12 on mobile)
- 404: compact card padding and button sizing
- ActivityFeed: smaller quote text and min-height on mobile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused imports, replace `any` types with proper interfaces in directus.ts
and i18n-loader.ts, exclude scripts/ and coverage/ from ESLint, and fix
unused variable warnings across the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed map parentheses syntax errors, resolved missing ActivityFeedClient imports, and corrected ActivityFeed prop types for idleQuote support. All systems green.
Added a shimmering Skeleton component. Integrated loading states for Hero, About (Bento Grid), Reading Log, Projects Archive, and Library pages for a premium UX.
Removed aggressive background text in footer. Implemented intelligent back button for projects. Redesigned project archive page. Stabilized idle quote logic in activity feed.
Automated CMS content seeding, integrated interactive AI Chat into Bento grid, implemented intelligent idle quote logic, and unified editorial styling across all sub-pages.
Centralized UI labels in Directus, integrated AI Chat and Status into Bento grid, created standalone Books page, and redesigned project sub-pages for consistent high-end aesthetic.
Moved profile photo to Hero for immediate visibility. Rewrote DE/EN translations to be more personal and focused on self-hosting/student identity. Refined Bento grid for better content flow.
Refactored About section to use a responsive Bento Grid layout. Redesigned Hero for stronger visual impact. Implemented floating Island navigation. Updated Project cards for cleaner aesthetic.
Adds a new "Read Books" section below "Currently Reading" in the About
page. Book reviews with star ratings and comments are fetched from a
Directus CMS collection (book_reviews) and displayed with the existing
liquid design system. Includes i18n support (EN/DE), show more/less
toggle, and graceful fallback when the CMS collection does not exist yet.
https://claude.ai/code/session_017E8W9CcHFM5WQVHw74JP34