unity-mcp/MCPForUnity/Editor/Setup/SetupWizardWindow.cs

345 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Linq;
using MCPForUnity.Editor.Data;
using MCPForUnity.Editor.Dependencies;
using MCPForUnity.Editor.Dependencies.Models;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Models;
using UnityEditor;
using UnityEngine;
namespace MCPForUnity.Editor.Setup
{
/// <summary>
/// Setup wizard window for guiding users through dependency installation
/// </summary>
public class SetupWizardWindow : EditorWindow
{
private DependencyCheckResult _dependencyResult;
private Vector2 _scrollPosition;
private int _currentStep = 0;
private readonly string[] _stepTitles = {
"Setup",
"Complete"
};
public static void ShowWindow(DependencyCheckResult dependencyResult = null)
{
var window = GetWindow<SetupWizardWindow>("MCP for Unity Setup");
window.minSize = new Vector2(500, 400);
window.maxSize = new Vector2(800, 600);
window._dependencyResult = dependencyResult ?? DependencyManager.CheckAllDependencies();
window.Show();
}
private void OnEnable()
{
if (_dependencyResult == null)
{
_dependencyResult = DependencyManager.CheckAllDependencies();
}
}
private void OnGUI()
{
DrawHeader();
DrawProgressBar();
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
switch (_currentStep)
{
case 0: DrawSetupStep(); break;
Remove old UI and do lots of cleanup (#340) * Remove legacy UI and correct priority ordering of menu items * Remove old UI screen Users now have the new UI alone, less confusing and more predictable * Remove unused config files * Remove test for window that doesn't exist * Remove unused code * Remove dangling .meta file * refactor: remove client configuration step from setup wizard * refactor: remove menu item attributes and manual window actions from Python tool sync * feat: update minimum Python version requirement from 3.10 to 3.11 The docs have 3.12. However, feature wise it seems that 3.11 is required * fix: replace emoji warning symbol with unicode character in setup wizard dialogs * docs: reorganize images into docs/images directory and update references * docs: add UI preview image to README * docs: add run_test function and resources section to available tools list The recent changes should close #311 * fix: add SystemRoot env var to Windows config to support Python path resolution Closes #315 * refactor: consolidate package installation and detection into unified lifecycle manager Duplicate code for pretty much no reason, as they both initialized there was a small chance of a race condition as well. Consolidating made sense here * Doc fixes from CodeRabbit * Excellent bug catch from CodeRabbit * fix: preserve existing environment variables when updating codex server config * Update docs so the paths match the original name * style: fix list indentation in README-DEV.md development docs * refactor: simplify env table handling in CodexConfigHelper by removing preservation logic * refactor: simplify configuration logic by removing redundant change detection Always overwrite configs * feat: ensure config directory exists before writing config files * feat: persist server installation errors and show retry UI instead of auto-marking as handled * refactor: consolidate configuration helpers by merging McpConfigFileHelper into McpConfigurationHelper * Small fixes from CodeRabbit * Remove test because we overwrite Codex configs * Remove unused function * feat: improve server cleanup and process handling on Windows - Added DeleteDirectoryWithRetry helper to handle Windows file locking with retries and readonly attribute clearing - Implemented KillWindowsUvProcesses to safely terminate Python processes in virtual environments using WMIC - Extended TryKillUvForPath to work on Windows, preventing file handle locks during server deletion - Improved error messages to be more descriptive about file locking issues - Replaced direct Directory.Delete calls with * fix: improve TCP socket cleanup to prevent CLOSE_WAIT states - Added proper socket shutdown sequence using Socket.Shutdown() before closing connections - Enhanced error handling with specific catches for SocketException vs general exceptions - Added debug logging for socket shutdown errors to help diagnose connection issues - Restructured HandleClientAsync to ensure socket cleanup happens in the correct order - Implemented proper socket teardown in both client handling and connection cleanup paths
2025-10-24 12:50:29 +08:00
case 1: DrawCompleteStep(); break;
}
EditorGUILayout.EndScrollView();
DrawFooter();
}
private void DrawHeader()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("MCP for Unity Setup Wizard", EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
GUILayout.Label($"Step {_currentStep + 1} of {_stepTitles.Length}");
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
// Step title
var titleStyle = new GUIStyle(EditorStyles.largeLabel)
{
fontSize = 16,
fontStyle = FontStyle.Bold
};
EditorGUILayout.LabelField(_stepTitles[_currentStep], titleStyle);
EditorGUILayout.Space();
}
private void DrawProgressBar()
{
var rect = EditorGUILayout.GetControlRect(false, 4);
var progress = (_currentStep + 1) / (float)_stepTitles.Length;
EditorGUI.ProgressBar(rect, progress, "");
EditorGUILayout.Space();
}
private void DrawSetupStep()
{
// Welcome section
DrawSectionTitle("MCP for Unity Setup");
EditorGUILayout.LabelField(
"This wizard will help you set up MCP for Unity to connect AI assistants with your Unity Editor.",
EditorStyles.wordWrappedLabel
);
EditorGUILayout.Space();
// Dependency check section
EditorGUILayout.BeginHorizontal();
DrawSectionTitle("System Check", 14);
GUILayout.FlexibleSpace();
if (GUILayout.Button("Refresh", GUILayout.Width(60), GUILayout.Height(20)))
{
_dependencyResult = DependencyManager.CheckAllDependencies();
}
EditorGUILayout.EndHorizontal();
// Show simplified dependency status
foreach (var dep in _dependencyResult.Dependencies)
{
DrawSimpleDependencyStatus(dep);
}
// Overall status and installation guidance
EditorGUILayout.Space();
if (!_dependencyResult.IsSystemReady)
{
// Only show critical warnings when dependencies are actually missing
EditorGUILayout.HelpBox(
feat: lower minimum Python requirement to 3.10+ (#362) * feat: lower minimum Python requirement to 3.10+ - Updated Python version requirement from 3.11+ to 3.10+ across all platform detectors - Added Python 3.10 to MacOS framework search paths for broader compatibility - Modified version validation logic to accept Python 3.10 or higher - Updated documentation and error messages to reflect new minimum Python version - Changed pyproject.toml requires-python field to ">=3.10" - Updated badges and requirements in README files to show Python 3.10 support * feat: add Python 3.10 and 3.11 to Windows path detection - Added Python 3.10 installation path to LocalApplicationData search locations - Added Python 3.10 and 3.11 paths to ProgramFiles search locations - Expanded Python version compatibility to support older installations while maintaining support for newer versions * feat: add Python 3.14 support and update path detection - Added Python 3.14 installation paths to Windows and macOS platform detectors - Removed legacy Python 3.9 paths from Windows path detection - Updated Windows installation recommendations to suggest Python 3.10 or higher - Added additional Python framework paths (3.10, 3.11) for macOS UV package manager detection - Extended UV executable path detection to include Python 3.14 locations on both platforms * Reduce size of README img * Revert "Reduce size of README img" This reverts commit 6fb99c7047bdef3610fb94dd3741c71c9e3ffcc1. * Adjust size in README to maintain quality but be smaller
2025-11-01 03:44:10 +08:00
"\u26A0 Missing Dependencies: MCP for Unity requires Python 3.10+ and UV package manager to function properly.",
MessageType.Warning
);
EditorGUILayout.Space();
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
DrawErrorStatus("Installation Required");
var recommendations = DependencyManager.GetInstallationRecommendations();
EditorGUILayout.LabelField(recommendations, EditorStyles.wordWrappedLabel);
EditorGUILayout.Space();
if (GUILayout.Button("Open Installation Links", GUILayout.Height(25)))
{
OpenInstallationUrls();
}
EditorGUILayout.EndVertical();
}
else
{
DrawSuccessStatus("System Ready");
EditorGUILayout.LabelField("All requirements are met. You can proceed to configure your AI clients.", EditorStyles.wordWrappedLabel);
}
}
private void DrawCompleteStep()
{
DrawSectionTitle("Setup Complete");
// Refresh dependency check with caching to avoid heavy operations on every repaint
if (_dependencyResult == null || (DateTime.UtcNow - _dependencyResult.CheckedAt).TotalSeconds > 2)
{
_dependencyResult = DependencyManager.CheckAllDependencies();
}
if (_dependencyResult.IsSystemReady)
{
DrawSuccessStatus("MCP for Unity Ready!");
EditorGUILayout.HelpBox(
"🎉 MCP for Unity is now set up and ready to use!\n\n" +
"• Dependencies verified\n" +
"• MCP server ready\n" +
"• Client configuration accessible",
MessageType.Info
);
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Documentation", GUILayout.Height(30)))
{
Application.OpenURL("https://github.com/CoplayDev/unity-mcp");
}
if (GUILayout.Button("Client Settings", GUILayout.Height(30)))
{
Windows.MCPForUnityEditorWindow.ShowWindow();
}
EditorGUILayout.EndHorizontal();
}
else
{
DrawErrorStatus("Setup Incomplete - Package Non-Functional");
EditorGUILayout.HelpBox(
"🚨 MCP for Unity CANNOT work - dependencies still missing!\n\n" +
"Install ALL required dependencies before the package will function.",
MessageType.Error
);
var missingDeps = _dependencyResult.GetMissingRequired();
if (missingDeps.Count > 0)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Still Missing:", EditorStyles.boldLabel);
foreach (var dep in missingDeps)
{
EditorGUILayout.LabelField($"✗ {dep.Name}", EditorStyles.label);
}
}
EditorGUILayout.Space();
if (GUILayout.Button("Go Back to Setup", GUILayout.Height(30)))
{
_currentStep = 0;
}
}
}
// Helper methods for consistent UI components
private void DrawSectionTitle(string title, int fontSize = 16)
{
var titleStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = fontSize,
fontStyle = FontStyle.Bold
};
EditorGUILayout.LabelField(title, titleStyle);
EditorGUILayout.Space();
}
private void DrawSuccessStatus(string message)
{
var originalColor = GUI.color;
GUI.color = Color.green;
EditorGUILayout.LabelField($"✓ {message}", EditorStyles.boldLabel);
GUI.color = originalColor;
EditorGUILayout.Space();
}
private void DrawErrorStatus(string message)
{
var originalColor = GUI.color;
GUI.color = Color.red;
EditorGUILayout.LabelField($"✗ {message}", EditorStyles.boldLabel);
GUI.color = originalColor;
EditorGUILayout.Space();
}
private void DrawSimpleDependencyStatus(DependencyStatus dep)
{
EditorGUILayout.BeginHorizontal();
var statusIcon = dep.IsAvailable ? "✓" : "✗";
var statusColor = dep.IsAvailable ? Color.green : Color.red;
var originalColor = GUI.color;
GUI.color = statusColor;
GUILayout.Label(statusIcon, GUILayout.Width(20));
EditorGUILayout.LabelField(dep.Name, EditorStyles.boldLabel);
GUI.color = originalColor;
if (!dep.IsAvailable && !string.IsNullOrEmpty(dep.ErrorMessage))
{
EditorGUILayout.LabelField($"({dep.ErrorMessage})", EditorStyles.miniLabel);
}
EditorGUILayout.EndHorizontal();
}
private void DrawFooter()
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
// Back button
GUI.enabled = _currentStep > 0;
if (GUILayout.Button("Back", GUILayout.Width(60)))
{
_currentStep--;
}
GUILayout.FlexibleSpace();
// Skip button
if (GUILayout.Button("Skip", GUILayout.Width(60)))
{
bool dismiss = EditorUtility.DisplayDialog(
"Skip Setup",
Remove old UI and do lots of cleanup (#340) * Remove legacy UI and correct priority ordering of menu items * Remove old UI screen Users now have the new UI alone, less confusing and more predictable * Remove unused config files * Remove test for window that doesn't exist * Remove unused code * Remove dangling .meta file * refactor: remove client configuration step from setup wizard * refactor: remove menu item attributes and manual window actions from Python tool sync * feat: update minimum Python version requirement from 3.10 to 3.11 The docs have 3.12. However, feature wise it seems that 3.11 is required * fix: replace emoji warning symbol with unicode character in setup wizard dialogs * docs: reorganize images into docs/images directory and update references * docs: add UI preview image to README * docs: add run_test function and resources section to available tools list The recent changes should close #311 * fix: add SystemRoot env var to Windows config to support Python path resolution Closes #315 * refactor: consolidate package installation and detection into unified lifecycle manager Duplicate code for pretty much no reason, as they both initialized there was a small chance of a race condition as well. Consolidating made sense here * Doc fixes from CodeRabbit * Excellent bug catch from CodeRabbit * fix: preserve existing environment variables when updating codex server config * Update docs so the paths match the original name * style: fix list indentation in README-DEV.md development docs * refactor: simplify env table handling in CodexConfigHelper by removing preservation logic * refactor: simplify configuration logic by removing redundant change detection Always overwrite configs * feat: ensure config directory exists before writing config files * feat: persist server installation errors and show retry UI instead of auto-marking as handled * refactor: consolidate configuration helpers by merging McpConfigFileHelper into McpConfigurationHelper * Small fixes from CodeRabbit * Remove test because we overwrite Codex configs * Remove unused function * feat: improve server cleanup and process handling on Windows - Added DeleteDirectoryWithRetry helper to handle Windows file locking with retries and readonly attribute clearing - Implemented KillWindowsUvProcesses to safely terminate Python processes in virtual environments using WMIC - Extended TryKillUvForPath to work on Windows, preventing file handle locks during server deletion - Improved error messages to be more descriptive about file locking issues - Replaced direct Directory.Delete calls with * fix: improve TCP socket cleanup to prevent CLOSE_WAIT states - Added proper socket shutdown sequence using Socket.Shutdown() before closing connections - Enhanced error handling with specific catches for SocketException vs general exceptions - Added debug logging for socket shutdown errors to help diagnose connection issues - Restructured HandleClientAsync to ensure socket cleanup happens in the correct order - Implemented proper socket teardown in both client handling and connection cleanup paths
2025-10-24 12:50:29 +08:00
"\u26A0 Skipping setup will leave MCP for Unity non-functional!\n\n" +
"You can restart setup from: Window > MCP for Unity > Setup Wizard (Required)",
"Skip Anyway",
"Cancel"
);
if (dismiss)
{
SetupWizard.MarkSetupDismissed();
Close();
}
}
// Next/Done button
GUI.enabled = true;
string buttonText = _currentStep == _stepTitles.Length - 1 ? "Done" : "Next";
if (GUILayout.Button(buttonText, GUILayout.Width(80)))
{
if (_currentStep == _stepTitles.Length - 1)
{
SetupWizard.MarkSetupCompleted();
Close();
}
else
{
_currentStep++;
}
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
}
private void OpenInstallationUrls()
{
var (pythonUrl, uvUrl) = DependencyManager.GetInstallationUrls();
bool openPython = EditorUtility.DisplayDialog(
"Open Installation URLs",
"Open Python installation page?",
"Yes",
"No"
);
if (openPython)
{
Application.OpenURL(pythonUrl);
}
bool openUV = EditorUtility.DisplayDialog(
"Open Installation URLs",
"Open UV installation page?",
"Yes",
"No"
);
if (openUV)
{
Application.OpenURL(uvUrl);
}
}
}
}