diff --git a/src/screens/PluginTesterScreen.tsx b/src/screens/PluginTesterScreen.tsx index e038bff..9872b2f 100644 --- a/src/screens/PluginTesterScreen.tsx +++ b/src/screens/PluginTesterScreen.tsx @@ -6,14 +6,15 @@ import { useTheme } from '../contexts/ThemeContext'; import { RepoTester } from './plugin-tester/RepoTester'; import { IndividualTester } from './plugin-tester/IndividualTester'; import { Header, MainTabBar } from './plugin-tester/components'; -import { getPluginTesterStyles } from './plugin-tester/styles'; +import { getPluginTesterStyles, useIsLargeScreen } from './plugin-tester/styles'; const PluginTesterScreen = () => { const [mainTab, setMainTab] = useState<'individual' | 'repo'>('individual'); const { currentTheme } = useTheme(); const insets = useSafeAreaInsets(); const navigation = useNavigation(); - const styles = getPluginTesterStyles(currentTheme); + const isLargeScreen = useIsLargeScreen(); + const styles = getPluginTesterStyles(currentTheme, isLargeScreen); if (mainTab === 'individual') { return ; diff --git a/src/screens/plugin-tester/IndividualTester.tsx b/src/screens/plugin-tester/IndividualTester.tsx index 6ad67fa..36c9c16 100644 --- a/src/screens/plugin-tester/IndividualTester.tsx +++ b/src/screens/plugin-tester/IndividualTester.tsx @@ -16,7 +16,7 @@ import { useNavigation } from '@react-navigation/native'; import { useTheme } from '../../contexts/ThemeContext'; import { pluginService } from '../../services/pluginService'; import axios from 'axios'; -import { getPluginTesterStyles } from './styles'; +import { getPluginTesterStyles, useIsLargeScreen } from './styles'; import { Header, MainTabBar } from './components'; interface IndividualTesterProps { @@ -27,7 +27,8 @@ export const IndividualTester = ({ onSwitchTab }: IndividualTesterProps) => { const navigation = useNavigation(); const insets = useSafeAreaInsets(); const { currentTheme } = useTheme(); - const styles = getPluginTesterStyles(currentTheme); + const isLargeScreen = useIsLargeScreen(); + const styles = getPluginTesterStyles(currentTheme, isLargeScreen); // State const [code, setCode] = useState(''); @@ -171,141 +172,361 @@ export const IndividualTester = ({ onSwitchTab }: IndividualTesterProps) => { } }; - const renderCodeTab = () => ( - - - - - Load from URL - + const renderCodeTab = () => { + // On large screens, show code + logs/results side by side + if (isLargeScreen) { + return ( + + + + + + + Load from URL + + + + Paste a raw GitHub URL or local IP and tap download. + + + + + + + + + + + + Plugin Code + + setIsEditorFocused(true)} + accessibilityLabel="Focus code editor" + > + + + + + + + + + {/* Test parameters on large screen */} + + + Test Parameters + + + + + setMediaType('movie')} + > + + Movie + + setMediaType('tv')} + > + + TV + + + + + + TMDB ID + + + + {mediaType === 'tv' && ( + <> + + Season + + + + Episode + + + + )} + + + + {isRunning ? ( + + ) : ( + + )} + {isRunning ? 'Running…' : 'Run Test'} + + + + + + + {/* Right side: Logs and Results */} + + + setActiveTab('logs')} + > + Logs + + setActiveTab('results')} + > + Results ({streams.length}) + + + + {activeTab === 'logs' || activeTab === 'code' ? ( + (logsScrollRef.current = r)} + style={[styles.logContainer, { flex: 1, minHeight: 400 }]} + contentContainerStyle={{ paddingBottom: 20 }} + onContentSizeChange={() => { + logsScrollRef.current?.scrollToEnd({ animated: true }); + }} + > + {logs.length === 0 ? ( + + + No logs yet. Run a test to see output. + + ) : ( + logs.map((log, i) => { + let style = styles.logItem; + if (log.includes('[ERROR]') || log.includes('[FATAL')) style = { ...style, ...styles.logError }; + else if (log.includes('[WARN]')) style = { ...style, ...styles.logWarn }; + else if (log.includes('[INFO]')) style = { ...style, ...styles.logInfo }; + else if (log.includes('[DEBUG]')) style = { ...style, ...styles.logDebug }; + + return ( + + {log} + + ); + }) + )} + + ) : ( + + {streams.length === 0 ? ( + + + No streams found yet. + + ) : ( + streams.map((stream, i) => ( + + {stream.title || stream.name} + Quality: {stream.quality || 'Unknown'} + Size: {stream.description || 'Unknown'} + URL: {stream.url} + + )) + )} + + )} + + - - Paste a raw GitHub URL or local IP and tap download. - - + + ); + } + + // Original mobile layout + return ( + + + + + Load from URL + + + + Paste a raw GitHub URL or local IP and tap download. + + + + + + + + + + + + Plugin Code + + setIsEditorFocused(true)} + accessibilityLabel="Focus code editor" + > + + + + + - - - - + - - - Plugin Code - + + + + Test Parameters + + + + setIsEditorFocused(true)} - accessibilityLabel="Focus code editor" + style={[styles.segmentItem, mediaType === 'movie' && styles.segmentItemActive]} + onPress={() => setMediaType('movie')} > - + + Movie + + setMediaType('tv')} + > + + TV - - - - - - - - - - - Test Parameters - - - - - setMediaType('movie')} - > - - Movie - - setMediaType('tv')} - > - - TV - - - - - - TMDB ID - - {mediaType === 'tv' && ( - <> - - Season - - - - Episode - - - + + + TMDB ID + + + + {mediaType === 'tv' && ( + <> + + Season + + + + Episode + + + + )} + + + + {isRunning ? ( + + ) : ( + )} - + {isRunning ? 'Running…' : 'Run Test'} + - - {isRunning ? ( - - ) : ( - - )} - {isRunning ? 'Running…' : 'Run Test'} - - - - ); + + ); + }; const renderLogsTab = () => ( { @@ -96,7 +96,8 @@ const buildScraperCandidates = (baseRepoUrl: string, filename: string) => { export const RepoTester = () => { const { currentTheme } = useTheme(); - const styles = getPluginTesterStyles(currentTheme); + const isLargeScreen = useIsLargeScreen(); + const styles = getPluginTesterStyles(currentTheme, isLargeScreen); // Repo tester state const [repoUrl, setRepoUrl] = useState(''); diff --git a/src/screens/plugin-tester/components.tsx b/src/screens/plugin-tester/components.tsx index 6648010..64a3f57 100644 --- a/src/screens/plugin-tester/components.tsx +++ b/src/screens/plugin-tester/components.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTheme } from '../../contexts/ThemeContext'; -import { getPluginTesterStyles } from './styles'; +import { getPluginTesterStyles, useIsLargeScreen } from './styles'; interface HeaderProps { title: string; @@ -14,7 +14,8 @@ interface HeaderProps { export const Header = ({ title, subtitle, onBack, backIcon = 'arrow-back', rightElement }: HeaderProps) => { const { currentTheme } = useTheme(); - const styles = getPluginTesterStyles(currentTheme); + const isLargeScreen = useIsLargeScreen(); + const styles = getPluginTesterStyles(currentTheme, isLargeScreen); return ( @@ -39,7 +40,8 @@ interface MainTabBarProps { export const MainTabBar = ({ activeTab, onTabChange }: MainTabBarProps) => { const { currentTheme } = useTheme(); - const styles = getPluginTesterStyles(currentTheme); + const isLargeScreen = useIsLargeScreen(); + const styles = getPluginTesterStyles(currentTheme, isLargeScreen); return ( diff --git a/src/screens/plugin-tester/styles.ts b/src/screens/plugin-tester/styles.ts index 481f8ac..53382ed 100644 --- a/src/screens/plugin-tester/styles.ts +++ b/src/screens/plugin-tester/styles.ts @@ -1,10 +1,37 @@ -import { StyleSheet, Platform } from 'react-native'; +import { StyleSheet, Platform, useWindowDimensions } from 'react-native'; -export const getPluginTesterStyles = (theme: any) => StyleSheet.create({ +// Breakpoint for larger screens (tablets, iPads) +export const LARGE_SCREEN_BREAKPOINT = 768; + +export const useIsLargeScreen = () => { + const { width } = useWindowDimensions(); + return width >= LARGE_SCREEN_BREAKPOINT; +}; + +export const getPluginTesterStyles = (theme: any, isLargeScreen: boolean = false) => StyleSheet.create({ container: { flex: 1, backgroundColor: theme.colors.darkBackground, }, + // Large screen wrapper for centering content + largeScreenWrapper: { + flex: 1, + maxWidth: isLargeScreen ? 900 : undefined, + alignSelf: isLargeScreen ? 'center' : undefined, + width: isLargeScreen ? '100%' : undefined, + paddingHorizontal: isLargeScreen ? 24 : 0, + }, + // Two-column layout for large screens + twoColumnContainer: { + flexDirection: isLargeScreen ? 'row' : 'column', + gap: isLargeScreen ? 16 : 0, + }, + leftColumn: { + flex: isLargeScreen ? 1 : undefined, + }, + rightColumn: { + flex: isLargeScreen ? 1 : undefined, + }, header: { flexDirection: 'row', alignItems: 'center',