fix: Changed flag management to EditorPrefs (#408)

* fix: Changed flag management to EditorPrefs

* refactor: Improve code readability and error handling in MCP window toggle

* fix: Refactor MCP window toggle logic to use new helper methods for better readability and maintainability

* fix: Reorder using directives and improve error logging format in MCP window

* Address CodeRabbit feedback: use McpLog.Warn and guard against repeated CreateGUI calls

---------

Co-authored-by: David Sarno <david@lighthaus.us>
main
カラス 2025-12-02 12:49:28 +09:00 committed by GitHub
parent 0d6c274e3c
commit b57a2ece9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 38 deletions

View File

@ -5,42 +5,25 @@ using UnityEngine;
namespace MCPForUnity.Editor.MenuItems namespace MCPForUnity.Editor.MenuItems
{ {
/// <summary>
/// Centralized menu items for MCP For Unity
/// </summary>
public static class MCPForUnityMenu public static class MCPForUnityMenu
{ {
// ========================================
// Main Menu Items
// ========================================
/// <summary>
/// Show the Setup Window
/// </summary>
[MenuItem("Window/MCP For Unity/Setup Window", priority = 1)] [MenuItem("Window/MCP For Unity/Setup Window", priority = 1)]
public static void ShowSetupWindow() public static void ShowSetupWindow()
{ {
SetupWindowService.ShowSetupWindow(); SetupWindowService.ShowSetupWindow();
} }
/// <summary>
/// Toggle the main MCP For Unity window
/// </summary>
[MenuItem("Window/MCP For Unity/Toggle MCP Window %#m", priority = 2)] [MenuItem("Window/MCP For Unity/Toggle MCP Window %#m", priority = 2)]
public static void ToggleMCPWindow() public static void ToggleMCPWindow()
{ {
if (EditorWindow.HasOpenInstances<MCPForUnityEditorWindow>()) if (MCPForUnityEditorWindow.HasAnyOpenWindow())
{ {
foreach (var window in UnityEngine.Resources.FindObjectsOfTypeAll<MCPForUnityEditorWindow>()) MCPForUnityEditorWindow.CloseAllOpenWindows();
{
window.Close();
}
} }
else else
{ {
MCPForUnityEditorWindow.ShowWindow(); MCPForUnityEditorWindow.ShowWindow();
} }
} }
} }
} }

View File

@ -18,7 +18,11 @@ namespace MCPForUnity.Editor.Setup
{ {
private const string SETUP_COMPLETED_KEY = EditorPrefKeys.SetupCompleted; private const string SETUP_COMPLETED_KEY = EditorPrefKeys.SetupCompleted;
private const string SETUP_DISMISSED_KEY = EditorPrefKeys.SetupDismissed; private const string SETUP_DISMISSED_KEY = EditorPrefKeys.SetupDismissed;
private static bool _hasCheckedThisSession = false;
// Use SessionState to persist "checked this editor session" across domain reloads.
// SessionState survives assembly reloads within the same Editor session, which prevents
// the setup window from reappearing after code reloads / playmode transitions.
private const string SessionCheckedKey = "MCPForUnity.SetupWindowCheckedThisEditorSession";
static SetupWindowService() static SetupWindowService()
{ {
@ -35,10 +39,12 @@ namespace MCPForUnity.Editor.Setup
/// </summary> /// </summary>
private static void CheckSetupNeeded() private static void CheckSetupNeeded()
{ {
if (_hasCheckedThisSession) // Ensure we only run once per Editor session (survives domain reloads).
// This avoids showing the setup dialog repeatedly when scripts recompile or Play mode toggles.
if (SessionState.GetBool(SessionCheckedKey, false))
return; return;
_hasCheckedThisSession = true; SessionState.SetBool(SessionCheckedKey, true);
try try
{ {
@ -48,11 +54,16 @@ namespace MCPForUnity.Editor.Setup
bool userOverrodeHttpUrl = EditorPrefs.HasKey(EditorPrefKeys.HttpBaseUrl); bool userOverrodeHttpUrl = EditorPrefs.HasKey(EditorPrefKeys.HttpBaseUrl);
// In Asset Store builds with a remote default URL (and no user override), skip the local setup wizard. // In Asset Store builds with a remote default URL (and no user override), skip the local setup wizard.
if (!userOverrodeHttpUrl if (
!userOverrodeHttpUrl
&& McpDistribution.Settings.skipSetupWindowWhenRemoteDefault && McpDistribution.Settings.skipSetupWindowWhenRemoteDefault
&& McpDistribution.Settings.IsRemoteDefault) && McpDistribution.Settings.IsRemoteDefault
)
{ {
McpLog.Info("Skipping Setup Window because this distribution ships with a hosted MCP URL. Open Window/MCP For Unity/Setup Window if you want to configure a local runtime.", always: false); McpLog.Info(
"Skipping Setup Window because this distribution ships with a hosted MCP URL. Open Window/MCP For Unity/Setup Window if you want to configure a local runtime.",
always: false
);
return; return;
} }
@ -66,7 +77,10 @@ namespace MCPForUnity.Editor.Setup
} }
else else
{ {
McpLog.Info("Setup Window skipped - previously completed or dismissed", always: false); McpLog.Info(
"Setup Window skipped - previously completed or dismissed",
always: false
);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -108,6 +122,5 @@ namespace MCPForUnity.Editor.Setup
EditorPrefs.SetBool(SETUP_DISMISSED_KEY, true); EditorPrefs.SetBool(SETUP_DISMISSED_KEY, true);
McpLog.Info("Setup marked as dismissed"); McpLog.Info("Setup marked as dismissed");
} }
} }
} }

View File

@ -1,14 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services;
using MCPForUnity.Editor.Windows.Components.ClientConfig;
using MCPForUnity.Editor.Windows.Components.Connection;
using MCPForUnity.Editor.Windows.Components.Settings;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services;
using MCPForUnity.Editor.Windows.Components.Settings;
using MCPForUnity.Editor.Windows.Components.Connection;
using MCPForUnity.Editor.Windows.Components.ClientConfig;
namespace MCPForUnity.Editor.Windows namespace MCPForUnity.Editor.Windows
{ {
@ -20,14 +20,47 @@ namespace MCPForUnity.Editor.Windows
private McpClientConfigSection clientConfigSection; private McpClientConfigSection clientConfigSection;
private static readonly HashSet<MCPForUnityEditorWindow> OpenWindows = new(); private static readonly HashSet<MCPForUnityEditorWindow> OpenWindows = new();
private bool guiCreated = false;
public static void ShowWindow() public static void ShowWindow()
{ {
var window = GetWindow<MCPForUnityEditorWindow>("MCP For Unity"); var window = GetWindow<MCPForUnityEditorWindow>("MCP For Unity");
window.minSize = new Vector2(500, 600); window.minSize = new Vector2(500, 600);
} }
// Helper to check and manage open windows from other classes
public static bool HasAnyOpenWindow()
{
return OpenWindows.Count > 0;
}
public static void CloseAllOpenWindows()
{
if (OpenWindows.Count == 0)
return;
// Copy to array to avoid modifying the collection while iterating
var arr = new MCPForUnityEditorWindow[OpenWindows.Count];
OpenWindows.CopyTo(arr);
foreach (var window in arr)
{
try
{
window?.Close();
}
catch (Exception ex)
{
McpLog.Warn($"Error closing MCP window: {ex.Message}");
}
}
}
public void CreateGUI() public void CreateGUI()
{ {
// Guard against repeated CreateGUI calls (e.g., domain reloads)
if (guiCreated)
return;
string basePath = AssetPathUtility.GetMcpPackageRootPath(); string basePath = AssetPathUtility.GetMcpPackageRootPath();
// Load main window UXML // Load main window UXML
@ -37,7 +70,9 @@ namespace MCPForUnity.Editor.Windows
if (visualTree == null) if (visualTree == null)
{ {
McpLog.Error($"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindow.uxml"); McpLog.Error(
$"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindow.uxml"
);
return; return;
} }
@ -78,8 +113,10 @@ namespace MCPForUnity.Editor.Windows
var settingsRoot = settingsTree.Instantiate(); var settingsRoot = settingsTree.Instantiate();
sectionsContainer.Add(settingsRoot); sectionsContainer.Add(settingsRoot);
settingsSection = new McpSettingsSection(settingsRoot); settingsSection = new McpSettingsSection(settingsRoot);
settingsSection.OnGitUrlChanged += () => clientConfigSection?.UpdateManualConfiguration(); settingsSection.OnGitUrlChanged += () =>
settingsSection.OnHttpServerCommandUpdateRequested += () => connectionSection?.UpdateHttpServerCommandDisplay(); clientConfigSection?.UpdateManualConfiguration();
settingsSection.OnHttpServerCommandUpdateRequested += () =>
connectionSection?.UpdateHttpServerCommandDisplay();
} }
// Load and initialize Connection section // Load and initialize Connection section
@ -91,7 +128,8 @@ namespace MCPForUnity.Editor.Windows
var connectionRoot = connectionTree.Instantiate(); var connectionRoot = connectionTree.Instantiate();
sectionsContainer.Add(connectionRoot); sectionsContainer.Add(connectionRoot);
connectionSection = new McpConnectionSection(connectionRoot); connectionSection = new McpConnectionSection(connectionRoot);
connectionSection.OnManualConfigUpdateRequested += () => clientConfigSection?.UpdateManualConfiguration(); connectionSection.OnManualConfigUpdateRequested += () =>
clientConfigSection?.UpdateManualConfiguration();
} }
// Load and initialize Client Configuration section // Load and initialize Client Configuration section
@ -105,6 +143,8 @@ namespace MCPForUnity.Editor.Windows
clientConfigSection = new McpClientConfigSection(clientConfigRoot); clientConfigSection = new McpClientConfigSection(clientConfigRoot);
} }
guiCreated = true;
// Initial updates // Initial updates
RefreshAllData(); RefreshAllData();
} }
@ -119,6 +159,7 @@ namespace MCPForUnity.Editor.Windows
{ {
EditorApplication.update -= OnEditorUpdate; EditorApplication.update -= OnEditorUpdate;
OpenWindows.Remove(this); OpenWindows.Remove(this);
guiCreated = false;
} }
private void OnFocus() private void OnFocus()
@ -163,11 +204,11 @@ namespace MCPForUnity.Editor.Windows
{ {
EditorApplication.delayCall += async () => EditorApplication.delayCall += async () =>
{ {
if (this == null) if (this == null || connectionSection == null)
{ {
return; return;
} }
await connectionSection?.VerifyBridgeConnectionAsync(); await connectionSection.VerifyBridgeConnectionAsync();
}; };
} }
} }