Phase 0: demolition — net −14k lines, one theme, fixed layout#8
Conversation
…rage) passport/session stack, uppy upload suite, and Google Cloud packages were imported nowhere in the codebase. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…HLS pipeline Postgres/Drizzle existed only to back the in-memory search/metadata caches; the transcode, disk-cache, and HLS routes never ran on the Vercel deploy and had no live client consumers. Caches are now memory-only LRU with TTL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CoreSoundboards, EasterEgg + hook, AsciiModeIndicator, ASCII terminal mode, Blondie geometry mode, and Hulkster mode. Learning-era chrome per PRD §6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er-motion Deletes LayoutDemonstrator, ResponsiveLayoutHints(+Simple), breakpoint indicator/manager, LayoutControls, LayoutAnimationWrapper, DockingGuides, and all three onboarding flows. AnimatedTransitions is now a CSS-only shim with the same API, dropping framer-motion from the bundle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Only 22 of 48 ui primitives were reachable from app code. Removes recharts, embla-carousel, input-otp, react-day-picker, vaul, react-resizable-panels, react-hook-form, and 13 unused radix packages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BrandSkin is now 'ebn'. theme-utils returns constants; a TS-API codemod folded all 162 brandSkin comparisons and 17 switch dispatches across 25 files; ThemeSelector/Switcher/Explanations/BrandSkinToggle deleted; index.css drops from 1,340 to 660 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…zone layout Home is now Crate (search results) | Signal (player + transport) | Timeline (queue + collapsible Effects/Geometry/Trim/Emergency Mix). Deletes FloatingPanel(sManager), MainToolbar, ToolsPanel, LiveVideoWindow, DevicePrompt and all panel-position state; store drops to 338 lines. Verified live: search, results grid, and playback all work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ron/Capacitor) Web-first Phase 1; Electron shell in Phase 1.5 for Syphon/Spout/NDI and the offline set cache; Capacitor for iPad in Phase 4. Native rewrite rejected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request dramatically simplifies the application by consolidating it into a single-identity theme (EBN Hijack) and replacing the complex floating panel workspace with a fixed three-zone layout (Crate, Signal, and Timeline). It removes multiple visual themes, easter eggs, database migrations, and several unused UI components, while also replacing Framer Motion with CSS-only transitions. The code review feedback highlights numerous opportunities to clean up the codebase further by removing unused variables (such as brandSkin) and simplifying redundant helper functions that now always return static values due to the theme consolidation.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const { brandSkin } = useStore(); | ||
| const handleLuckyDipResults = (results: any) => { | ||
| onLuckyDipResults?.(results); | ||
| }; | ||
| const getThemeClasses = () => { | ||
| { | ||
| return 'bg-gray-900/60 border-lime-500/30'; | ||
| } | ||
| }; | ||
| return (<div className={`inline-flex items-center gap-2 px-3 py-2 rounded-lg border backdrop-blur-sm ${getThemeClasses()}`}> |
There was a problem hiding this comment.
The brandSkin variable is destructured from useStore() but is no longer used in this component. Additionally, the getThemeClasses function contains a redundant nested block and always returns a static string. We can simplify this by removing the unused variable and function, and inlining the class string directly.
const handleLuckyDipResults = (results: any) => {
onLuckyDipResults?.(results);
};
return (<div className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border backdrop-blur-sm bg-gray-900/60 border-lime-500/30">
| const getPerformanceBarBg = () => { | ||
| { | ||
| return 'bg-gray-900'; | ||
| } | ||
| }; | ||
|
|
||
| // Initial connection check | ||
| updateConnection(); | ||
|
|
||
| // Listen for online/offline events | ||
| window.addEventListener('online', updateConnection); | ||
| window.addEventListener('offline', updateConnection); | ||
|
|
||
| // Listen for connection changes if supported | ||
| const connection = (navigator as any).connection; | ||
| if (connection) { | ||
| connection.addEventListener('change', updateConnection); | ||
| } | ||
|
|
||
| return () => { | ||
| window.removeEventListener('online', updateConnection); | ||
| window.removeEventListener('offline', updateConnection); | ||
| if (connection) { | ||
| connection.removeEventListener('change', updateConnection); | ||
| } | ||
| }; | ||
| }, []); | ||
|
|
||
| // Real-time clock | ||
| useEffect(() => { | ||
| const interval = setInterval(() => { | ||
| setCurrentTime(new Date()); | ||
| }, 1000); | ||
|
|
||
| return () => clearInterval(interval); | ||
| }, []); | ||
|
|
||
| // Format time for display | ||
| const formatTime = (date: Date) => { | ||
| return date.toLocaleTimeString('en-US', { | ||
| hour12: false, | ||
| hour: '2-digit', | ||
| minute: '2-digit', | ||
| second: '2-digit' | ||
| }); | ||
| }; | ||
|
|
||
| // Get connection icon based on type and status | ||
| const getConnectionIcon = () => { | ||
| if (!isOnline) return WifiOff; | ||
|
|
||
| switch (connectionType) { | ||
| case 'slow-2g': | ||
| case '2g': | ||
| case '3g': | ||
| return Smartphone; | ||
| case '4g': | ||
| case '5g': | ||
| return Smartphone; | ||
| case 'wifi': | ||
| return Wifi; | ||
| default: | ||
| return isOnline ? Wifi : WifiOff; | ||
| } | ||
| }; | ||
|
|
||
| const ConnectionIcon = getConnectionIcon(); | ||
|
|
||
| // Get theme-specific darker background for contrast | ||
| const getPerformanceBarBg = () => { | ||
| switch (brandSkin) { | ||
| case 'ebn': | ||
| return 'bg-gray-900'; | ||
| case 'waffle': | ||
| return 'bg-yellow-900'; | ||
| case 'ozzy': | ||
| return 'bg-red-900'; | ||
| case 'hogan': | ||
| return 'bg-gray-900'; | ||
| case 'dx': | ||
| return 'bg-gray-900'; | ||
| case 'mario': | ||
| return 'bg-red-900'; | ||
| case 'dakota': | ||
| return 'bg-gray-900'; | ||
| case 'blondie': | ||
| return 'bg-amber-900'; | ||
| case 'testcard': | ||
| return 'bg-slate-900'; | ||
| default: | ||
| return 'bg-gray-900'; | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div | ||
| className={`fixed bottom-6 left-0 right-0 ${getPerformanceBarBg()} ${themeClasses.border} border-t z-40 h-8`} | ||
| data-testid="bottom-hud" | ||
| > | ||
| return (<div className={`fixed bottom-6 left-0 right-0 ${getPerformanceBarBg()} ${themeClasses.border} border-t z-40 h-8`} data-testid="bottom-hud"> |
There was a problem hiding this comment.
The getPerformanceBarBg function contains a redundant nested block and always returns a static string. We can simplify this by removing the function and inlining the background class directly in the JSX.
return (<div className={`fixed bottom-6 left-0 right-0 bg-gray-900 ${themeClasses.border} border-t z-40 h-8`} data-testid="bottom-hud">
| const { brandSkin } = useStore(); | ||
| const mode = 'ebn'; |
There was a problem hiding this comment.
| const getThemeClasses = () => { | ||
| { | ||
| return { | ||
| accent: 'text-lime-400 border-lime-400', | ||
| button: 'bg-lime-600 hover:bg-lime-700 text-white', | ||
| recording: 'bg-red-600 hover:bg-red-700 text-white animate-pulse', | ||
| badge: 'bg-lime-100 text-lime-800' | ||
| }; | ||
| } | ||
| }; | ||
| const theme = getThemeClasses(); |
There was a problem hiding this comment.
The getThemeClasses function contains a redundant nested block and always returns a static theme configuration object. We can simplify this by defining the theme object directly.
const theme = {
accent: 'text-lime-400 border-lime-400',
button: 'bg-lime-600 hover:bg-lime-700 text-white',
recording: 'bg-red-600 hover:bg-red-700 text-white animate-pulse',
badge: 'bg-lime-100 text-lime-800'
};
|
|
||
| return ( | ||
| <div className="flex items-center space-x-1"> | ||
| const { searchResults, queueItems, setQueueItems, brandSkin } = useStore(); |
There was a problem hiding this comment.
The brandSkin and queueItems variables are destructured from useStore() but are not used anywhere in this component. We can remove them to clean up the destructuring.
| const { searchResults, queueItems, setQueueItems, brandSkin } = useStore(); | |
| const { searchResults, setQueueItems } = useStore(); |
| const { brandSkin } = useStore(); | ||
| const getThemeClasses = () => { | ||
| { | ||
| return 'bg-gray-200/30'; | ||
| } | ||
| }; |
There was a problem hiding this comment.
|
|
||
| return ( | ||
| <div className="flex flex-wrap gap-1 sm:gap-2 mb-3"> | ||
| const { brandSkin, searchState } = useStore(); |
| const getThemeClasses = () => { | ||
| { | ||
| return { | ||
| chip: 'bg-lime-900/50 text-lime-400 border-lime-500/30 hover:bg-lime-900/70', | ||
| active: 'bg-lime-500/30 text-lime-100' | ||
| }; | ||
| } | ||
| }; | ||
| const theme = getThemeClasses(); |
There was a problem hiding this comment.
The getThemeClasses function contains a redundant nested block and always returns a static theme configuration object. We can simplify this by defining the theme object directly.
const theme = {
chip: 'bg-lime-900/50 text-lime-400 border-lime-500/30 hover:bg-lime-900/70',
active: 'bg-lime-500/30 text-lime-100'
};
| export function SourceToggles({ onSourcesChange }: SourceTogglesProps) { | ||
| const { searchState, setSearchState, brandSkin } = useStore(); | ||
| const [sources, setSources] = useState<SearchSource[]>(() => { | ||
| // Initialize sources with state from store or defaults | ||
| const currentSources = searchState.sources || ['prelinger', 'fedflix']; | ||
| return DEFAULT_SOURCES.map(source => ({ | ||
| ...source, | ||
| enabled: currentSources.includes(source.id) | ||
| })); | ||
| }); | ||
|
|
||
| const theme = getThemeClasses(brandSkin); | ||
|
|
||
| const getSourceIcon = (sourceId: string) => { | ||
| switch (sourceId) { | ||
| case 'nasa': | ||
| return <Rocket size={12} className="animate-pulse" />; | ||
| case 'loc': | ||
| return <Building size={12} />; | ||
| case 'wikimedia': | ||
| return <Camera size={12} />; | ||
| case 'prelinger': | ||
| return <Film size={12} />; | ||
| case 'fedflix': | ||
| return <Tv size={12} />; | ||
| default: | ||
| return <Archive size={12} />; | ||
| } | ||
| }; | ||
|
|
||
| const handleSourceToggle = (sourceId: string, checked: boolean) => { | ||
| const updatedSources = sources.map(source => | ||
| source.id === sourceId ? { ...source, enabled: checked } : source | ||
| ); | ||
| setSources(updatedSources); | ||
|
|
||
| const enabledSources = updatedSources | ||
| .filter(source => source.enabled) | ||
| .map(source => source.id); | ||
|
|
||
| setSearchState({ sources: enabledSources, page: 1 }); | ||
| onSourcesChange(); | ||
| }; | ||
|
|
||
| const enabledCount = sources.filter(s => s.enabled).length; | ||
|
|
||
| return ( | ||
| <div className={`inline-flex items-center gap-1 px-2 py-1 rounded-lg border-2 ${ | ||
| brandSkin === 'mario' ? 'bg-gradient-to-r from-red-500/20 via-yellow-500/20 to-green-500/20 border-yellow-400/50 shadow-[0_0_20px_rgba(255,215,0,0.3)]' : | ||
| brandSkin === 'hogan' ? 'bg-gradient-to-r from-red-600/20 to-yellow-500/20 border-yellow-400/50' : | ||
| brandSkin === 'dx' ? 'bg-gradient-to-r from-green-600/20 to-black/40 border-green-500/50' : | ||
| brandSkin === 'waffle' ? 'bg-gradient-to-r from-amber-400/20 to-yellow-300/20 border-amber-400/50' : | ||
| brandSkin === 'ebn' ? 'bg-gradient-to-r from-lime-500/20 to-green-600/20 border-lime-500/50' : | ||
| brandSkin === 'ozzy' ? 'bg-gradient-to-r from-red-900/30 to-purple-900/30 border-red-500/50' : | ||
| brandSkin === 'maxheadroom' ? 'bg-gradient-to-r from-cyan-500/20 to-pink-500/20 border-cyan-400/50 animate-pulse' : | ||
| brandSkin === 'dakota' ? 'bg-gradient-to-r from-purple-500/20 to-pink-500/20 border-purple-400/50' : | ||
| brandSkin === 'blondie' ? 'bg-gradient-to-r from-amber-500/20 to-orange-500/20 border-amber-400/50' : | ||
| 'bg-gradient-to-r from-gray-700/30 to-gray-800/30 border-gray-500/50' | ||
| } backdrop-blur-sm transition-all duration-300 hover:scale-105`}> | ||
| const { searchState, setSearchState, brandSkin } = useStore(); |
There was a problem hiding this comment.
The brandSkin variable is destructured from useStore() but is no longer used in this component since the container classes are hardcoded. We can remove this unused destructuring.
| export function SourceToggles({ onSourcesChange }: SourceTogglesProps) { | |
| const { searchState, setSearchState, brandSkin } = useStore(); | |
| const [sources, setSources] = useState<SearchSource[]>(() => { | |
| // Initialize sources with state from store or defaults | |
| const currentSources = searchState.sources || ['prelinger', 'fedflix']; | |
| return DEFAULT_SOURCES.map(source => ({ | |
| ...source, | |
| enabled: currentSources.includes(source.id) | |
| })); | |
| }); | |
| const theme = getThemeClasses(brandSkin); | |
| const getSourceIcon = (sourceId: string) => { | |
| switch (sourceId) { | |
| case 'nasa': | |
| return <Rocket size={12} className="animate-pulse" />; | |
| case 'loc': | |
| return <Building size={12} />; | |
| case 'wikimedia': | |
| return <Camera size={12} />; | |
| case 'prelinger': | |
| return <Film size={12} />; | |
| case 'fedflix': | |
| return <Tv size={12} />; | |
| default: | |
| return <Archive size={12} />; | |
| } | |
| }; | |
| const handleSourceToggle = (sourceId: string, checked: boolean) => { | |
| const updatedSources = sources.map(source => | |
| source.id === sourceId ? { ...source, enabled: checked } : source | |
| ); | |
| setSources(updatedSources); | |
| const enabledSources = updatedSources | |
| .filter(source => source.enabled) | |
| .map(source => source.id); | |
| setSearchState({ sources: enabledSources, page: 1 }); | |
| onSourcesChange(); | |
| }; | |
| const enabledCount = sources.filter(s => s.enabled).length; | |
| return ( | |
| <div className={`inline-flex items-center gap-1 px-2 py-1 rounded-lg border-2 ${ | |
| brandSkin === 'mario' ? 'bg-gradient-to-r from-red-500/20 via-yellow-500/20 to-green-500/20 border-yellow-400/50 shadow-[0_0_20px_rgba(255,215,0,0.3)]' : | |
| brandSkin === 'hogan' ? 'bg-gradient-to-r from-red-600/20 to-yellow-500/20 border-yellow-400/50' : | |
| brandSkin === 'dx' ? 'bg-gradient-to-r from-green-600/20 to-black/40 border-green-500/50' : | |
| brandSkin === 'waffle' ? 'bg-gradient-to-r from-amber-400/20 to-yellow-300/20 border-amber-400/50' : | |
| brandSkin === 'ebn' ? 'bg-gradient-to-r from-lime-500/20 to-green-600/20 border-lime-500/50' : | |
| brandSkin === 'ozzy' ? 'bg-gradient-to-r from-red-900/30 to-purple-900/30 border-red-500/50' : | |
| brandSkin === 'maxheadroom' ? 'bg-gradient-to-r from-cyan-500/20 to-pink-500/20 border-cyan-400/50 animate-pulse' : | |
| brandSkin === 'dakota' ? 'bg-gradient-to-r from-purple-500/20 to-pink-500/20 border-purple-400/50' : | |
| brandSkin === 'blondie' ? 'bg-gradient-to-r from-amber-500/20 to-orange-500/20 border-amber-400/50' : | |
| 'bg-gradient-to-r from-gray-700/30 to-gray-800/30 border-gray-500/50' | |
| } backdrop-blur-sm transition-all duration-300 hover:scale-105`}> | |
| const { searchState, setSearchState, brandSkin } = useStore(); | |
| export function SourceToggles({ onSourcesChange }: SourceTogglesProps) { | |
| const { searchState, setSearchState } = useStore(); |
| export function StatusBar() { | ||
| const { | ||
| brandSkin, | ||
| queueItems, | ||
| searchResults, | ||
| videoEffects, | ||
| isPlaying, | ||
| floatingPanelStates | ||
| } = useStore(); | ||
|
|
||
| const [fps, setFps] = useState(30); | ||
| const [resolution, setResolution] = useState('1080p'); | ||
| const [audioLevel, setAudioLevel] = useState(0.6); | ||
|
|
||
| // Simulate live data updates | ||
| useEffect(() => { | ||
| const interval = setInterval(() => { | ||
| // Simulate FPS fluctuation (25-60 fps) | ||
| setFps(Math.floor(25 + Math.random() * 35)); | ||
|
|
||
| // Simulate audio levels (VU meter bounce) | ||
| setAudioLevel(0.2 + Math.random() * 0.6); | ||
| }, 100); | ||
|
|
||
| return () => clearInterval(interval); | ||
| }, []); | ||
|
|
||
| const themeClasses = getThemeClasses(brandSkin); | ||
|
|
||
| // Count visible panels | ||
| const visiblePanels = Object.values(floatingPanelStates).filter(panel => panel.visible).length; | ||
|
|
||
| // Get current FX status | ||
| const fxActive = videoEffects.glitchIntensity > 0 || | ||
| videoEffects.chromaticAberration > 0 || | ||
| videoEffects.blur > 0 || | ||
| videoEffects.sepia > 0; | ||
|
|
||
| return ( | ||
| <div | ||
| className={`fixed bottom-0 left-0 right-0 h-6 ${themeClasses.bgSecondary} ${themeClasses.border} border-t z-50 flex items-center justify-between px-3 text-xs ${themeClasses.textSecondary}`} | ||
| data-testid="status-bar" | ||
| > | ||
| const { brandSkin, queueItems, searchResults, videoEffects, isPlaying } = useStore(); |
There was a problem hiding this comment.
The brandSkin variable is destructured from useStore() but is no longer used in this component since the status bar classes and text are hardcoded. We can remove this unused destructuring.
| export function StatusBar() { | |
| const { | |
| brandSkin, | |
| queueItems, | |
| searchResults, | |
| videoEffects, | |
| isPlaying, | |
| floatingPanelStates | |
| } = useStore(); | |
| const [fps, setFps] = useState(30); | |
| const [resolution, setResolution] = useState('1080p'); | |
| const [audioLevel, setAudioLevel] = useState(0.6); | |
| // Simulate live data updates | |
| useEffect(() => { | |
| const interval = setInterval(() => { | |
| // Simulate FPS fluctuation (25-60 fps) | |
| setFps(Math.floor(25 + Math.random() * 35)); | |
| // Simulate audio levels (VU meter bounce) | |
| setAudioLevel(0.2 + Math.random() * 0.6); | |
| }, 100); | |
| return () => clearInterval(interval); | |
| }, []); | |
| const themeClasses = getThemeClasses(brandSkin); | |
| // Count visible panels | |
| const visiblePanels = Object.values(floatingPanelStates).filter(panel => panel.visible).length; | |
| // Get current FX status | |
| const fxActive = videoEffects.glitchIntensity > 0 || | |
| videoEffects.chromaticAberration > 0 || | |
| videoEffects.blur > 0 || | |
| videoEffects.sepia > 0; | |
| return ( | |
| <div | |
| className={`fixed bottom-0 left-0 right-0 h-6 ${themeClasses.bgSecondary} ${themeClasses.border} border-t z-50 flex items-center justify-between px-3 text-xs ${themeClasses.textSecondary}`} | |
| data-testid="status-bar" | |
| > | |
| const { brandSkin, queueItems, searchResults, videoEffects, isPlaying } = useStore(); | |
| export function StatusBar() { | |
| const { queueItems, searchResults, videoEffects, isPlaying } = useStore(); |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
What
Executes Phase 0 of the modernization PRD (§6 cut list) in 8 checkpointed commits, plus a new platform strategy section in the PRD. Net −16,595 / +2,649 lines (excluding lockfile). Typecheck and both builds pass; app verified live in the browser (search → results → playback all working).
The cuts
brandSkincomparisons and 17 switch dispatches across 25 files;index.css1,340 → 660 lines.store.ts1,064 → 338 lines; all panel x/y/z-index state gone.Platform strategy (new PRD §5.5)
Decision wrapped into this PR: one engine, three shells.
Reviewer notes
AnimatedTransitions.tsxkeeps the old API as a CSS shim so 8 consumers didn't churn; it shrinks further as those components are replaced in Phase 1/2.🤖 Generated with Claude Code