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',