830 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			830 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:flutter/material.dart';
 | 
						|
import 'package:rimworld_modman/mod_list.dart';
 | 
						|
import 'package:rimworld_modman/mod_list_troubleshooter.dart';
 | 
						|
 | 
						|
import 'main.dart';
 | 
						|
 | 
						|
/// A widget that provides a user interface for the mod troubleshooter functionality.
 | 
						|
///
 | 
						|
/// This allows users to:
 | 
						|
/// - Toggle between binary and linear search modes
 | 
						|
/// - Navigate forward and backward through mod sets
 | 
						|
/// - Adjust step size for linear navigation
 | 
						|
/// - Mark mods as checked/good or problematic
 | 
						|
/// - Find specific mods causing issues in their load order
 | 
						|
class ModTroubleshooterWidget extends StatefulWidget {
 | 
						|
  const ModTroubleshooterWidget({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  State<ModTroubleshooterWidget> createState() =>
 | 
						|
      _ModTroubleshooterWidgetState();
 | 
						|
}
 | 
						|
 | 
						|
class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
 | 
						|
  late ModListTroubleshooter _troubleshooter;
 | 
						|
 | 
						|
  bool _isInitialized = false;
 | 
						|
  bool _isBinaryMode = false;
 | 
						|
  int _stepSize = 10;
 | 
						|
 | 
						|
  // Set of mod IDs that have been checked and confirmed to be good
 | 
						|
  final Set<String> _checkedMods = {};
 | 
						|
 | 
						|
  // Set of mod IDs that are suspected to cause issues
 | 
						|
  final Set<String> _problemMods = {};
 | 
						|
 | 
						|
  // The currently selected mod IDs (for highlighting)
 | 
						|
  LoadOrder _loadOrder = LoadOrder();
 | 
						|
 | 
						|
  // The next potential set of mods (from move calculation)
 | 
						|
  Move? _nextForwardMove;
 | 
						|
  Move? _nextBackwardMove;
 | 
						|
 | 
						|
  // Controller for step size input
 | 
						|
  late TextEditingController _stepSizeController;
 | 
						|
 | 
						|
  @override
 | 
						|
  void initState() {
 | 
						|
    super.initState();
 | 
						|
    _stepSizeController = TextEditingController(text: _stepSize.toString());
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  void dispose() {
 | 
						|
    _stepSizeController.dispose();
 | 
						|
    super.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  void _initialize() {
 | 
						|
    if (_isInitialized) return;
 | 
						|
 | 
						|
    // Initialize the troubleshooter with the global mod manager
 | 
						|
    _troubleshooter = ModListTroubleshooter(modManager);
 | 
						|
 | 
						|
    // Set initial active mods for highlighting
 | 
						|
    if (modManager.activeMods.isNotEmpty) {
 | 
						|
      // Initially select all active mods
 | 
						|
      _loadOrder = LoadOrder(modManager.activeMods.values.toList());
 | 
						|
    }
 | 
						|
 | 
						|
    // Calculate initial moves
 | 
						|
    _updateNextMoves();
 | 
						|
 | 
						|
    setState(() {
 | 
						|
      _isInitialized = true;
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  void _updateNextMoves() {
 | 
						|
    if (_isBinaryMode) {
 | 
						|
      _nextForwardMove = _troubleshooter.binaryForwardMove();
 | 
						|
      _nextBackwardMove = _troubleshooter.binaryBackwardMove();
 | 
						|
    } else {
 | 
						|
      _nextForwardMove = _troubleshooter.linearForwardMove(stepSize: _stepSize);
 | 
						|
      _nextBackwardMove = _troubleshooter.linearBackwardMove(
 | 
						|
        stepSize: _stepSize,
 | 
						|
      );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void _navigateForward() {
 | 
						|
    ModList result;
 | 
						|
    if (_isBinaryMode) {
 | 
						|
      result = _troubleshooter.binaryForward();
 | 
						|
    } else {
 | 
						|
      result = _troubleshooter.linearForward(stepSize: _stepSize);
 | 
						|
    }
 | 
						|
 | 
						|
    // Load all required dependencies for the selected mods
 | 
						|
    final loadOrder = result.loadRequiredBaseGame();
 | 
						|
 | 
						|
    // Use the mods from the load order result
 | 
						|
    setState(() {
 | 
						|
      _loadOrder = loadOrder;
 | 
						|
      _updateNextMoves();
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  void _navigateBackward() {
 | 
						|
    ModList result;
 | 
						|
    if (_isBinaryMode) {
 | 
						|
      result = _troubleshooter.binaryBackward();
 | 
						|
    } else {
 | 
						|
      result = _troubleshooter.linearBackward(stepSize: _stepSize);
 | 
						|
    }
 | 
						|
 | 
						|
    // Load all required dependencies for the selected mods
 | 
						|
    final loadOrder = result.loadRequiredBaseGame();
 | 
						|
 | 
						|
    // Use the mods from the load order result
 | 
						|
    setState(() {
 | 
						|
      _loadOrder = loadOrder;
 | 
						|
      _updateNextMoves();
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  void _markAsGood(String modId) {
 | 
						|
    setState(() {
 | 
						|
      _checkedMods.add(modId);
 | 
						|
      _problemMods.remove(modId);
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  void _markAsProblem(String modId) {
 | 
						|
    setState(() {
 | 
						|
      _problemMods.add(modId);
 | 
						|
      _checkedMods.remove(modId);
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  void _clearMarks(String modId) {
 | 
						|
    setState(() {
 | 
						|
      _checkedMods.remove(modId);
 | 
						|
      _problemMods.remove(modId);
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  void _resetTroubleshooter() {
 | 
						|
    setState(() {
 | 
						|
      _checkedMods.clear();
 | 
						|
      _problemMods.clear();
 | 
						|
      _isInitialized = false;
 | 
						|
    });
 | 
						|
    _initialize();
 | 
						|
  }
 | 
						|
 | 
						|
  void _saveTroubleshootingConfig() {
 | 
						|
    // Only save if we have a valid selection
 | 
						|
    if (_loadOrder.order.isEmpty) {
 | 
						|
      ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
        const SnackBar(
 | 
						|
          content: Text('No mods selected to save'),
 | 
						|
          duration: Duration(seconds: 2),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    modManager.saveToConfig(_loadOrder);
 | 
						|
 | 
						|
    // Save the configuration (we don't have direct access to save method, so show a message)
 | 
						|
    ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
      SnackBar(
 | 
						|
        content: Text(
 | 
						|
          '${_loadOrder.order.length} mods have been successfully saved to the configuration.',
 | 
						|
        ),
 | 
						|
        backgroundColor: Colors.green,
 | 
						|
        duration: const Duration(seconds: 4),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  void _markSelectedAsGood() {
 | 
						|
    if (_loadOrder.order.isEmpty) {
 | 
						|
      ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
        const SnackBar(
 | 
						|
          content: Text('No mods selected to mark'),
 | 
						|
          duration: Duration(seconds: 2),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    setState(() {
 | 
						|
      for (final mod in _loadOrder.order) {
 | 
						|
        _checkedMods.add(mod.id);
 | 
						|
        _problemMods.remove(mod.id);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
      SnackBar(
 | 
						|
        content: Text('Marked ${_loadOrder.order.length} mods as good'),
 | 
						|
        backgroundColor: Colors.green,
 | 
						|
        duration: const Duration(seconds: 2),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  void _markSelectedAsProblem() {
 | 
						|
    if (_loadOrder.order.isEmpty) {
 | 
						|
      ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
        const SnackBar(
 | 
						|
          content: Text('No mods selected to mark'),
 | 
						|
          duration: Duration(seconds: 2),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    setState(() {
 | 
						|
      for (final mod in _loadOrder.order) {
 | 
						|
        _problemMods.add(mod.id);
 | 
						|
        _checkedMods.remove(mod.id);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
      SnackBar(
 | 
						|
        content: Text('Marked ${_loadOrder.order.length} mods as problematic'),
 | 
						|
        backgroundColor: Colors.orange,
 | 
						|
        duration: const Duration(seconds: 2),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    // Make sure we're initialized
 | 
						|
    if (!_isInitialized) {
 | 
						|
      _initialize();
 | 
						|
    }
 | 
						|
 | 
						|
    if (!_isInitialized || modManager.mods.isEmpty) {
 | 
						|
      return _buildEmptyState();
 | 
						|
    }
 | 
						|
 | 
						|
    return Column(
 | 
						|
      children: [_buildControlPanel(), Expanded(child: _buildModList())],
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildEmptyState() {
 | 
						|
    return Center(
 | 
						|
      child: Column(
 | 
						|
        mainAxisAlignment: MainAxisAlignment.center,
 | 
						|
        children: [
 | 
						|
          Icon(
 | 
						|
            Icons.build,
 | 
						|
            size: AppThemeExtension.of(context).iconSizeLarge * 2,
 | 
						|
          ),
 | 
						|
          const SizedBox(height: 16),
 | 
						|
          Text(
 | 
						|
            'Troubleshooting',
 | 
						|
            style: Theme.of(context).textTheme.headlineMedium,
 | 
						|
          ),
 | 
						|
          const SizedBox(height: 16),
 | 
						|
          Padding(
 | 
						|
            padding: const EdgeInsets.symmetric(horizontal: 32.0),
 | 
						|
            child: Text(
 | 
						|
              'Load mods first to use the troubleshooting tools.',
 | 
						|
              style: Theme.of(context).textTheme.bodyLarge,
 | 
						|
              textAlign: TextAlign.center,
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
          const SizedBox(height: 24),
 | 
						|
          ElevatedButton(
 | 
						|
            onPressed: () {
 | 
						|
              // No direct access to the ModManagerHomePage state, so just show a message
 | 
						|
              ScaffoldMessenger.of(context).showSnackBar(
 | 
						|
                const SnackBar(
 | 
						|
                  content: Text('Please go to the Mods tab to load mods first'),
 | 
						|
                  duration: Duration(seconds: 3),
 | 
						|
                ),
 | 
						|
              );
 | 
						|
            },
 | 
						|
            child: const Text('Go to Mod Manager'),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildControlPanel() {
 | 
						|
    return Card(
 | 
						|
      margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
 | 
						|
      child: Padding(
 | 
						|
        padding: AppThemeExtension.of(context).paddingSmall,
 | 
						|
        child: Column(
 | 
						|
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
          children: [
 | 
						|
            Row(
 | 
						|
              children: [
 | 
						|
                // Compact instruction
 | 
						|
                Expanded(
 | 
						|
                  child: Text(
 | 
						|
                    _loadOrder.order.isNotEmpty
 | 
						|
                        ? 'Testing ${_loadOrder.order.length} mods. Tap highlighted mods to navigate. Mark results below:'
 | 
						|
                        : 'Click highlighted mods to begin testing. Blue→forward, purple←backward.',
 | 
						|
                    style: TextStyle(
 | 
						|
                      fontSize: AppThemeExtension.of(context).textSizeRegular,
 | 
						|
                      fontStyle: FontStyle.italic,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                ),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
 | 
						|
            const SizedBox(height: 8),
 | 
						|
 | 
						|
            Row(
 | 
						|
              children: [
 | 
						|
                // Binary/Linear mode toggle
 | 
						|
                Text('Mode:', style: Theme.of(context).textTheme.bodyMedium),
 | 
						|
                const SizedBox(width: 8),
 | 
						|
                ToggleButtons(
 | 
						|
                  isSelected: [!_isBinaryMode, _isBinaryMode],
 | 
						|
                  onPressed: (index) {
 | 
						|
                    setState(() {
 | 
						|
                      _isBinaryMode = index == 1;
 | 
						|
                      _updateNextMoves();
 | 
						|
                    });
 | 
						|
                  },
 | 
						|
                  children: const [
 | 
						|
                    Padding(
 | 
						|
                      padding: EdgeInsets.symmetric(horizontal: 8.0),
 | 
						|
                      child: Text('Linear'),
 | 
						|
                    ),
 | 
						|
                    Padding(
 | 
						|
                      padding: EdgeInsets.symmetric(horizontal: 8.0),
 | 
						|
                      child: Text('Binary'),
 | 
						|
                    ),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
 | 
						|
                // Step size input field (only for linear mode)
 | 
						|
                if (!_isBinaryMode) ...[
 | 
						|
                  const SizedBox(width: 16),
 | 
						|
                  Text('Step:', style: Theme.of(context).textTheme.bodyMedium),
 | 
						|
                  const SizedBox(width: 4),
 | 
						|
                  SizedBox(
 | 
						|
                    width: 60,
 | 
						|
                    child: TextField(
 | 
						|
                      keyboardType: TextInputType.number,
 | 
						|
                      decoration: const InputDecoration(
 | 
						|
                        border: OutlineInputBorder(),
 | 
						|
                        contentPadding: EdgeInsets.symmetric(
 | 
						|
                          horizontal: 6,
 | 
						|
                          vertical: 6,
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                      controller: _stepSizeController,
 | 
						|
                      onChanged: (value) {
 | 
						|
                        final parsedValue = int.tryParse(value);
 | 
						|
                        if (parsedValue != null && parsedValue > 0) {
 | 
						|
                          setState(() {
 | 
						|
                            _stepSize = parsedValue;
 | 
						|
                            _updateNextMoves();
 | 
						|
                          });
 | 
						|
                        }
 | 
						|
                      },
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                ],
 | 
						|
 | 
						|
                const Spacer(),
 | 
						|
 | 
						|
                // Buttons to mark selected mods
 | 
						|
                if (_loadOrder.order.isNotEmpty) ...[
 | 
						|
                  OutlinedButton.icon(
 | 
						|
                    icon: Icon(
 | 
						|
                      Icons.error,
 | 
						|
                      color: Colors.red.shade300,
 | 
						|
                      size: 16,
 | 
						|
                    ),
 | 
						|
                    label: const Text('Problem'),
 | 
						|
                    style: OutlinedButton.styleFrom(
 | 
						|
                      padding: const EdgeInsets.symmetric(
 | 
						|
                        horizontal: 8,
 | 
						|
                        vertical: 0,
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                    onPressed: _markSelectedAsProblem,
 | 
						|
                  ),
 | 
						|
                  const SizedBox(width: 4),
 | 
						|
                  OutlinedButton.icon(
 | 
						|
                    icon: Icon(
 | 
						|
                      Icons.check_circle,
 | 
						|
                      color: Colors.green.shade300,
 | 
						|
                      size: 16,
 | 
						|
                    ),
 | 
						|
                    label: const Text('Good'),
 | 
						|
                    style: OutlinedButton.styleFrom(
 | 
						|
                      padding: const EdgeInsets.symmetric(
 | 
						|
                        horizontal: 8,
 | 
						|
                        vertical: 0,
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                    onPressed: _markSelectedAsGood,
 | 
						|
                  ),
 | 
						|
                  const SizedBox(width: 4),
 | 
						|
                ],
 | 
						|
 | 
						|
                // Reset button
 | 
						|
                OutlinedButton.icon(
 | 
						|
                  icon: const Icon(Icons.refresh, size: 16),
 | 
						|
                  label: const Text('Reset'),
 | 
						|
                  style: OutlinedButton.styleFrom(
 | 
						|
                    padding: const EdgeInsets.symmetric(
 | 
						|
                      horizontal: 8,
 | 
						|
                      vertical: 0,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  onPressed: _resetTroubleshooter,
 | 
						|
                ),
 | 
						|
 | 
						|
                if (_loadOrder.order.isNotEmpty) ...[
 | 
						|
                  const SizedBox(width: 4),
 | 
						|
                  // Save config button
 | 
						|
                  OutlinedButton.icon(
 | 
						|
                    icon: const Icon(Icons.save, size: 16),
 | 
						|
                    label: const Text('Save'),
 | 
						|
                    style: OutlinedButton.styleFrom(
 | 
						|
                      padding: const EdgeInsets.symmetric(
 | 
						|
                        horizontal: 8,
 | 
						|
                        vertical: 0,
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                    onPressed: _saveTroubleshootingConfig,
 | 
						|
                  ),
 | 
						|
                ],
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
          ],
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildModList() {
 | 
						|
    // Get the original mod order from mod manager
 | 
						|
    final fullModList = modManager.activeMods.keys.toList();
 | 
						|
 | 
						|
    return Card(
 | 
						|
      margin: AppThemeExtension.of(context).paddingRegular,
 | 
						|
      child: Column(
 | 
						|
        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
						|
        children: [
 | 
						|
          Container(
 | 
						|
            color: Theme.of(context).primaryColor,
 | 
						|
            padding: AppThemeExtension.of(context).paddingRegular,
 | 
						|
            child: Column(
 | 
						|
              children: [
 | 
						|
                Text(
 | 
						|
                  'Active Mods (${fullModList.length})',
 | 
						|
                  style: const TextStyle(fontWeight: FontWeight.bold),
 | 
						|
                  textAlign: TextAlign.center,
 | 
						|
                ),
 | 
						|
                if (_nextForwardMove != null || _nextBackwardMove != null)
 | 
						|
                  Text(
 | 
						|
                    'Click ↓blue areas to move forward, ↑purple to move backward',
 | 
						|
                    style: TextStyle(
 | 
						|
                      fontSize: AppThemeExtension.of(context).textSizeSmall,
 | 
						|
                      fontStyle: FontStyle.italic,
 | 
						|
                      color: Colors.grey.shade300,
 | 
						|
                    ),
 | 
						|
                    textAlign: TextAlign.center,
 | 
						|
                  ),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
          Expanded(
 | 
						|
            child: ListView.builder(
 | 
						|
              itemCount: fullModList.length,
 | 
						|
              itemBuilder: (context, index) {
 | 
						|
                final modId = fullModList[index];
 | 
						|
                final mod = modManager.mods[modId];
 | 
						|
 | 
						|
                if (mod == null) return const SizedBox.shrink();
 | 
						|
 | 
						|
                // Determine if this mod is in the selection range for highlighted navigation
 | 
						|
                final bool isSelected = _loadOrder.order.any(
 | 
						|
                  (m) => m.id == modId,
 | 
						|
                );
 | 
						|
 | 
						|
                // Check if this mod would be included in the next Forward/Backward move
 | 
						|
                bool isInNextForward = false;
 | 
						|
                bool isInNextBackward = false;
 | 
						|
 | 
						|
                if (_nextForwardMove != null &&
 | 
						|
                    index >= _nextForwardMove!.startIndex &&
 | 
						|
                    index < _nextForwardMove!.endIndex) {
 | 
						|
                  isInNextForward = true;
 | 
						|
                }
 | 
						|
 | 
						|
                if (_nextBackwardMove != null &&
 | 
						|
                    index >= _nextBackwardMove!.startIndex &&
 | 
						|
                    index < _nextBackwardMove!.endIndex) {
 | 
						|
                  isInNextBackward = true;
 | 
						|
                }
 | 
						|
 | 
						|
                // Determine mod status for coloring
 | 
						|
                final bool isChecked = _checkedMods.contains(modId);
 | 
						|
                final bool isProblem = _problemMods.contains(modId);
 | 
						|
 | 
						|
                return GestureDetector(
 | 
						|
                  onTap: () {
 | 
						|
                    // Navigation takes precedence if this mod is in a navigation range
 | 
						|
                    if (isInNextForward) {
 | 
						|
                      _navigateForward();
 | 
						|
                    } else if (isInNextBackward) {
 | 
						|
                      _navigateBackward();
 | 
						|
                    }
 | 
						|
                    // Otherwise toggle the status of this mod
 | 
						|
                    else if (isChecked) {
 | 
						|
                      _markAsProblem(modId);
 | 
						|
                    } else if (isProblem) {
 | 
						|
                      _clearMarks(modId);
 | 
						|
                    } else {
 | 
						|
                      _markAsGood(modId);
 | 
						|
                    }
 | 
						|
                  },
 | 
						|
                  child: Card(
 | 
						|
                    margin: const EdgeInsets.symmetric(
 | 
						|
                      horizontal: 8.0,
 | 
						|
                      vertical: 4.0,
 | 
						|
                    ),
 | 
						|
                    color: _getModCardColor(
 | 
						|
                      isSelected: isSelected,
 | 
						|
                      isChecked: isChecked,
 | 
						|
                      isProblem: isProblem,
 | 
						|
                      isInNextForward: isInNextForward,
 | 
						|
                      isInNextBackward: isInNextBackward,
 | 
						|
                    ),
 | 
						|
                    child: ListTile(
 | 
						|
                      leading: Text(
 | 
						|
                        '${index + 1}',
 | 
						|
                        style: TextStyle(
 | 
						|
                          fontWeight: FontWeight.bold,
 | 
						|
                          color: isSelected ? Colors.white : Colors.grey,
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                      title: Row(
 | 
						|
                        children: [
 | 
						|
                          if (isSelected)
 | 
						|
                            Container(
 | 
						|
                              padding: const EdgeInsets.symmetric(
 | 
						|
                                horizontal: 4,
 | 
						|
                                vertical: 0,
 | 
						|
                              ),
 | 
						|
                              margin: const EdgeInsets.only(right: 4),
 | 
						|
                              decoration: BoxDecoration(
 | 
						|
                                color: const Color(
 | 
						|
                                  0x28303F9F,
 | 
						|
                                ), // Blue with alpha 40
 | 
						|
                                borderRadius: BorderRadius.circular(4),
 | 
						|
                              ),
 | 
						|
                              child: Text(
 | 
						|
                                'TESTING',
 | 
						|
                                style: TextStyle(
 | 
						|
                                  color: Colors.blue.shade200,
 | 
						|
                                  fontSize:
 | 
						|
                                      AppThemeExtension.of(
 | 
						|
                                        context,
 | 
						|
                                      ).textSizeSmall,
 | 
						|
                                  fontWeight: FontWeight.bold,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          if (isChecked)
 | 
						|
                            Container(
 | 
						|
                              padding: const EdgeInsets.symmetric(
 | 
						|
                                horizontal: 4,
 | 
						|
                                vertical: 0,
 | 
						|
                              ),
 | 
						|
                              margin: const EdgeInsets.only(right: 4),
 | 
						|
                              decoration: BoxDecoration(
 | 
						|
                                color: const Color(
 | 
						|
                                  0x1E2E7D32,
 | 
						|
                                ), // Green with alpha 30
 | 
						|
                                borderRadius: BorderRadius.circular(4),
 | 
						|
                              ),
 | 
						|
                              child: Text(
 | 
						|
                                'GOOD',
 | 
						|
                                style: TextStyle(
 | 
						|
                                  color: Colors.green.shade200,
 | 
						|
                                  fontSize:
 | 
						|
                                      AppThemeExtension.of(
 | 
						|
                                        context,
 | 
						|
                                      ).textSizeSmall,
 | 
						|
                                  fontWeight: FontWeight.bold,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          if (isProblem)
 | 
						|
                            Container(
 | 
						|
                              padding: const EdgeInsets.symmetric(
 | 
						|
                                horizontal: 4,
 | 
						|
                                vertical: 0,
 | 
						|
                              ),
 | 
						|
                              margin: const EdgeInsets.only(right: 4),
 | 
						|
                              decoration: BoxDecoration(
 | 
						|
                                color: const Color(
 | 
						|
                                  0x1EC62828,
 | 
						|
                                ), // Red with alpha 30
 | 
						|
                                borderRadius: BorderRadius.circular(4),
 | 
						|
                              ),
 | 
						|
                              child: Text(
 | 
						|
                                'PROBLEM',
 | 
						|
                                style: TextStyle(
 | 
						|
                                  color: Colors.red.shade200,
 | 
						|
                                  fontSize:
 | 
						|
                                      AppThemeExtension.of(
 | 
						|
                                        context,
 | 
						|
                                      ).textSizeSmall,
 | 
						|
                                  fontWeight: FontWeight.bold,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          Expanded(
 | 
						|
                            child: Text(
 | 
						|
                              mod.name,
 | 
						|
                              style: TextStyle(
 | 
						|
                                fontWeight:
 | 
						|
                                    isSelected
 | 
						|
                                        ? FontWeight.bold
 | 
						|
                                        : FontWeight.normal,
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          ),
 | 
						|
                        ],
 | 
						|
                      ),
 | 
						|
                      subtitle: Text(
 | 
						|
                        modId,
 | 
						|
                        style: TextStyle(
 | 
						|
                          fontSize: AppThemeExtension.of(context).textSizeSmall,
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                      trailing: Row(
 | 
						|
                        mainAxisSize: MainAxisSize.min,
 | 
						|
                        children: [
 | 
						|
                          // Display mod characteristics
 | 
						|
                          if (mod.isBaseGame)
 | 
						|
                            Tooltip(
 | 
						|
                              message: 'Base Game',
 | 
						|
                              child: Icon(
 | 
						|
                                Icons.home,
 | 
						|
                                color:
 | 
						|
                                    AppThemeExtension.of(context).baseGameColor,
 | 
						|
                                size:
 | 
						|
                                    AppThemeExtension.of(context).iconSizeSmall,
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          if (mod.isExpansion)
 | 
						|
                            Tooltip(
 | 
						|
                              message: 'Expansion',
 | 
						|
                              child: Icon(
 | 
						|
                                Icons.star,
 | 
						|
                                color:
 | 
						|
                                    AppThemeExtension.of(
 | 
						|
                                      context,
 | 
						|
                                    ).expansionColor,
 | 
						|
                                size:
 | 
						|
                                    AppThemeExtension.of(context).iconSizeSmall,
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          if (mod.dependencies.isNotEmpty)
 | 
						|
                            Tooltip(
 | 
						|
                              message:
 | 
						|
                                  'Dependencies:\n${mod.dependencies.join('\n')}',
 | 
						|
                              child: Icon(
 | 
						|
                                Icons.link,
 | 
						|
                                color: AppThemeExtension.of(context).linkColor,
 | 
						|
                                size:
 | 
						|
                                    AppThemeExtension.of(context).iconSizeSmall,
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
 | 
						|
                          // Display status icon
 | 
						|
                          if (isChecked)
 | 
						|
                            Tooltip(
 | 
						|
                              message: 'Marked as working correctly',
 | 
						|
                              child: Icon(
 | 
						|
                                Icons.check_circle,
 | 
						|
                                color: Colors.green.shade300,
 | 
						|
                              ),
 | 
						|
                            )
 | 
						|
                          else if (isProblem)
 | 
						|
                            Tooltip(
 | 
						|
                              message: 'Marked as problematic',
 | 
						|
                              child: Icon(
 | 
						|
                                Icons.error,
 | 
						|
                                color: Colors.red.shade300,
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
 | 
						|
                          const SizedBox(width: 4),
 | 
						|
 | 
						|
                          // Show navigation indicators
 | 
						|
                          if (isInNextForward)
 | 
						|
                            Container(
 | 
						|
                              padding: const EdgeInsets.symmetric(
 | 
						|
                                horizontal: 4,
 | 
						|
                                vertical: 2,
 | 
						|
                              ),
 | 
						|
                              decoration: BoxDecoration(
 | 
						|
                                color: const Color(
 | 
						|
                                  0x0A2196F3,
 | 
						|
                                ), // Blue with alpha 10
 | 
						|
                                borderRadius: BorderRadius.circular(4),
 | 
						|
                              ),
 | 
						|
                              child: Tooltip(
 | 
						|
                                message:
 | 
						|
                                    'Click to move Forward (test this mod)',
 | 
						|
                                child: Row(
 | 
						|
                                  mainAxisSize: MainAxisSize.min,
 | 
						|
                                  children: [
 | 
						|
                                    Icon(
 | 
						|
                                      Icons.arrow_forward,
 | 
						|
                                      color: Colors.blue.shade300,
 | 
						|
                                      size:
 | 
						|
                                          AppThemeExtension.of(
 | 
						|
                                            context,
 | 
						|
                                          ).iconSizeSmall,
 | 
						|
                                    ),
 | 
						|
                                    const SizedBox(width: 2),
 | 
						|
                                    Text(
 | 
						|
                                      'Forward',
 | 
						|
                                      style: TextStyle(
 | 
						|
                                        color: Colors.blue.shade300,
 | 
						|
                                        fontSize:
 | 
						|
                                            AppThemeExtension.of(
 | 
						|
                                              context,
 | 
						|
                                            ).textSizeSmall,
 | 
						|
                                      ),
 | 
						|
                                    ),
 | 
						|
                                  ],
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
 | 
						|
                          if (isInNextBackward)
 | 
						|
                            Container(
 | 
						|
                              padding: const EdgeInsets.symmetric(
 | 
						|
                                horizontal: 4,
 | 
						|
                                vertical: 2,
 | 
						|
                              ),
 | 
						|
                              decoration: BoxDecoration(
 | 
						|
                                color: const Color(
 | 
						|
                                  0x0A9C27B0,
 | 
						|
                                ), // Purple with alpha 10
 | 
						|
                                borderRadius: BorderRadius.circular(4),
 | 
						|
                              ),
 | 
						|
                              child: Tooltip(
 | 
						|
                                message:
 | 
						|
                                    'Click to move Backward (test this mod)',
 | 
						|
                                child: Row(
 | 
						|
                                  mainAxisSize: MainAxisSize.min,
 | 
						|
                                  children: [
 | 
						|
                                    Icon(
 | 
						|
                                      Icons.arrow_back,
 | 
						|
                                      color: Colors.purple.shade300,
 | 
						|
                                      size:
 | 
						|
                                          AppThemeExtension.of(
 | 
						|
                                            context,
 | 
						|
                                          ).iconSizeSmall,
 | 
						|
                                    ),
 | 
						|
                                    const SizedBox(width: 2),
 | 
						|
                                    Text(
 | 
						|
                                      'Back',
 | 
						|
                                      style: TextStyle(
 | 
						|
                                        color: Colors.purple.shade300,
 | 
						|
                                        fontSize:
 | 
						|
                                            AppThemeExtension.of(
 | 
						|
                                              context,
 | 
						|
                                            ).textSizeSmall,
 | 
						|
                                      ),
 | 
						|
                                    ),
 | 
						|
                                  ],
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                        ],
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                );
 | 
						|
              },
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Color _getModCardColor({
 | 
						|
    required bool isSelected,
 | 
						|
    required bool isChecked,
 | 
						|
    required bool isProblem,
 | 
						|
    required bool isInNextForward,
 | 
						|
    required bool isInNextBackward,
 | 
						|
  }) {
 | 
						|
    // Priority: 1. Selected, 2. Navigation areas, 3. Status
 | 
						|
    if (isSelected) {
 | 
						|
      return const Color(0x80303F9F);
 | 
						|
    } else if (isInNextForward && isInNextBackward) {
 | 
						|
      return const Color(0x50673AB7);
 | 
						|
    } else if (isInNextForward) {
 | 
						|
      return const Color(0x402196F3);
 | 
						|
    } else if (isInNextBackward) {
 | 
						|
      return const Color(0x409C27B0);
 | 
						|
    } else if (isChecked) {
 | 
						|
      return const Color(0x802E7D32);
 | 
						|
    } else if (isProblem) {
 | 
						|
      return const Color(0x80C62828);
 | 
						|
    }
 | 
						|
 | 
						|
    return Colors.transparent;
 | 
						|
  }
 | 
						|
}
 |