From ff485fe506c79ef3ec3b43cf5dfd5da724bd98c0 Mon Sep 17 00:00:00 2001 From: "Martin Staiger (via Rool)" Date: Wed, 1 Jul 2026 22:33:58 +0000 Subject: [PATCH 1/4] feat: implement immersive Trailer Cinema Mode (ITIL V5 Evolution) --- docs/ITIL_V5_Evolution_2026-07-01.md | 15 +++++ src/components/TrailerOverlay.tsx | 89 ++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 docs/ITIL_V5_Evolution_2026-07-01.md create mode 100644 src/components/TrailerOverlay.tsx diff --git a/docs/ITIL_V5_Evolution_2026-07-01.md b/docs/ITIL_V5_Evolution_2026-07-01.md new file mode 100644 index 0000000..28fe2fd --- /dev/null +++ b/docs/ITIL_V5_Evolution_2026-07-01.md @@ -0,0 +1,15 @@ +# ITIL V5 Service Evolution - Milestone: Trailer Cinema Mode + +## 1. Service Design & Strategy +**Service Name:** CineLog Trailer Immersive Experience +**Objective:** Provide an immersive, high-fidelity movie trailer viewing experience within the PWA. +**Value Proposition:** Emotional connection through cinematic presentation. + +## 2. Configuration Item (CI) Update +- New Component: `TrailerOverlay.tsx` +- Modified: `MovieDetailModal.tsx` +- Theme Integration: Semantic variables used for Dark/Light mode support. + +## 3. Risk & Rollback +**Risk:** UX interference with existing navigation. +**Rollback:** git checkout main; delete feat/trailer-cinema-mode branch. diff --git a/src/components/TrailerOverlay.tsx b/src/components/TrailerOverlay.tsx new file mode 100644 index 0000000..93c68dc --- /dev/null +++ b/src/components/TrailerOverlay.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { motion } from 'framer-motion'; +import { X } from 'lucide-react'; + +interface TrailerOverlayProps { + title: string; + overview: string | null; + trailerKey: string; + onClose: () => void; + isVisible: boolean; +} + +/** + * TrailerOverlay - Ein immersives Glasmorphism-Overlay für den Video-Player. + * Nutzt die Theme-Tokens der CineLog App für nahtlose Integration. + */ +export const TrailerOverlay: React.FC = ({ + title, + overview, + trailerKey, + onClose, + isVisible +}) => { + if (!isVisible) return null; + + return ( + + {/* Close Button */} + + + {/* Video Container (Background) */} +
+ +
+
+ + {/* Content Card */} + +
+ + Cinema Mode + + + Powered by CineLog AI + +
+ +

+ {title} +

+ +

+ {overview} +

+ +
+ +
+
+ + ); +}; From 851339c3926d6ae619ab09774dd2bfc6a424d6e6 Mon Sep 17 00:00:00 2001 From: "Martin Staiger (via Rool)" Date: Wed, 1 Jul 2026 22:34:11 +0000 Subject: [PATCH 2/4] feat: integrate TrailerOverlay into MovieDetailModal and update UI triggers --- src/components/MovieDetailModal.tsx | 33 ++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/components/MovieDetailModal.tsx b/src/components/MovieDetailModal.tsx index 32d8d9b..284b497 100644 --- a/src/components/MovieDetailModal.tsx +++ b/src/components/MovieDetailModal.tsx @@ -1,3 +1,4 @@ +import { TrailerOverlay } from './TrailerOverlay'; import React, { useEffect, useState, useMemo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Movie, CastMember, WatchProvider } from '../types/domain'; @@ -45,6 +46,8 @@ export const MovieDetailModal = React.memo( onShowToast, }: MovieDetailModalProps) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + const isInLibrary = useMemo( () => libraryItems.some((m) => m.tmdbId === Number(movie.id) || m.id === movie.id), @@ -202,6 +205,8 @@ const ActionButtons = React.memo( onShowToast: (message: string, type: 'success' | 'error' | 'info') => void; }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + const [showListCreation, setShowListCreation] = useState(false); const handleAddToList = useCallback( @@ -296,6 +301,8 @@ const ListMenu = React.memo( onCreateNewList: () => void; }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + return (
@@ -341,6 +348,8 @@ ListMenu.displayName = 'ListMenu'; const PlotSection = React.memo(({ movie }: { movie: Movie }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + if (!movie.overview) return null; return (
@@ -356,6 +365,8 @@ PlotSection.displayName = 'PlotSection'; const MetadataGrid = React.memo(({ movie }: { movie: Movie }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + const items = useMemo( () => @@ -385,6 +396,8 @@ MetadataGrid.displayName = 'MetadataGrid'; const CastSection = React.memo(({ cast }: { cast?: CastMember[] }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + if (!cast?.length) return null; return (
@@ -420,6 +433,8 @@ CastSection.displayName = 'CastSection'; const WatchProvidersSection = React.memo( ({ watchProviders }: { watchProviders?: Movie['watchProviders'] }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + const flat = watchProviders?.flatrate || []; const rent = watchProviders?.rent || []; const buy = watchProviders?.buy || []; @@ -469,6 +484,8 @@ const RecommendationsSection = React.memo( onSelectMovie: (id: string) => void; }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + if (!recommendations?.length) return null; return (
@@ -516,6 +533,8 @@ const PersonalSection = React.memo( onShowToast: (message: string, type: 'success' | 'error' | 'info') => void; }) => { const { t } = useTranslation(); + const [showTrailerMode, setShowTrailerMode] = useState(false); + const [rating, setRating] = useState(movie.userRating ?? null); const { ratings, loading: ratingsLoading } = useExternalRatings( movie.tmdbId, @@ -715,7 +734,19 @@ const PersonalSection = React.memo( ? t('common.saving', 'Speichere…') : t('common.autoSaveHint', 'Wird beim Verlassen des Felds automatisch gespeichert.')}
-
+ + + {showTrailerMode && movie.trailerKey && ( + setShowTrailerMode(false)} + /> + )} + + {/* Tags - kollapsibel für mobile Usability */}
From c1f42215950ed5c2f7135915c2861414783b4737 Mon Sep 17 00:00:00 2001 From: "Martin Staiger (via Rool)" Date: Wed, 1 Jul 2026 22:34:16 +0000 Subject: [PATCH 3/4] feat: add Supabase migration for Vibe Engine v3.0 --- docs/ITIL_V5_Evolution_2026-07-01.md | 5 +++++ supabase/migrations/20260701_vibe_engine.sql | 21 ++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 supabase/migrations/20260701_vibe_engine.sql diff --git a/docs/ITIL_V5_Evolution_2026-07-01.md b/docs/ITIL_V5_Evolution_2026-07-01.md index 28fe2fd..a9075fc 100644 --- a/docs/ITIL_V5_Evolution_2026-07-01.md +++ b/docs/ITIL_V5_Evolution_2026-07-01.md @@ -13,3 +13,8 @@ ## 3. Risk & Rollback **Risk:** UX interference with existing navigation. **Rollback:** git checkout main; delete feat/trailer-cinema-mode branch. + +## 4. Milestone: Supabase Vibe Engine v3.0 (Data Layer) +**Service Component:** Persistence Layer +**Change:** New table `movie_vibes` for storing AI-generated mood profiles. +**Validation:** SQL Migration verification via Supabase API. diff --git a/supabase/migrations/20260701_vibe_engine.sql b/supabase/migrations/20260701_vibe_engine.sql new file mode 100644 index 0000000..64a0419 --- /dev/null +++ b/supabase/migrations/20260701_vibe_engine.sql @@ -0,0 +1,21 @@ +-- ITIL V5 Milestone: Vibe Engine v3.0 Base Schema +-- Service: Movie Recommendation Engine + +create table if not exists public.movie_vibes ( + id uuid default uuid_generate_v4() primary key, + movie_id uuid not null, -- references movies(id) - assuming id is uuid + vibe_name text not null, + intensity float check (intensity >= 0 and intensity <= 1), + ai_metadata jsonb, + created_at timestamp with time zone default now() +); + +-- Indices for performance +create index if not exists idx_movie_vibes_name on public.movie_vibes(vibe_name); +create index if not exists idx_movie_vibes_movie_id on public.movie_vibes(movie_id); + +-- Simple view for vibe exploration +create or replace view public.vibe_explorer as +select v.vibe_name, count(*) as frequency, avg(v.intensity) as avg_intensity +from public.movie_vibes v +group by v.vibe_name; From 2967d46f9162ac4871167531b3c646743088ff54 Mon Sep 17 00:00:00 2001 From: "Martin Staiger (via Rool)" Date: Wed, 1 Jul 2026 22:44:35 +0000 Subject: [PATCH 4/4] fix: syntax errors in TrailerOverlay (escaped template literals) --- src/components/TrailerOverlay.tsx | 33 ++++++++++++------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/components/TrailerOverlay.tsx b/src/components/TrailerOverlay.tsx index 93c68dc..12f4608 100644 --- a/src/components/TrailerOverlay.tsx +++ b/src/components/TrailerOverlay.tsx @@ -10,10 +10,6 @@ interface TrailerOverlayProps { isVisible: boolean; } -/** - * TrailerOverlay - Ein immersives Glasmorphism-Overlay für den Video-Player. - * Nutzt die Theme-Tokens der CineLog App für nahtlose Integration. - */ export const TrailerOverlay: React.FC = ({ title, overview, @@ -30,7 +26,6 @@ export const TrailerOverlay: React.FC = ({ exit={{ opacity: 0, backdropFilter: "blur(0px)" }} className="fixed inset-0 z-[100] flex flex-col justify-end bg-black/60 p-6 md:p-12" > - {/* Close Button */} - {/* Video Container (Background) */} -
+
-
+
- {/* Content Card */} -
- +
+ Cinema Mode - + Powered by CineLog AI
-

+

{title}

-

+

{overview}

-
+