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

View File

@ -18,7 +18,11 @@ namespace MCPForUnity.Editor.Setup
{
private const string SETUP_COMPLETED_KEY = EditorPrefKeys.SetupCompleted;
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()
{
@ -35,10 +39,12 @@ namespace MCPForUnity.Editor.Setup
/// </summary>
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;
_hasCheckedThisSession = true;
SessionState.SetBool(SessionCheckedKey, true);
try
{
@ -48,11 +54,16 @@ namespace MCPForUnity.Editor.Setup
bool userOverrodeHttpUrl = EditorPrefs.HasKey(EditorPrefKeys.HttpBaseUrl);
// 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.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;
}
@ -66,7 +77,10 @@ namespace MCPForUnity.Editor.Setup
}
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)
@ -108,6 +122,5 @@ namespace MCPForUnity.Editor.Setup
EditorPrefs.SetBool(SETUP_DISMISSED_KEY, true);
McpLog.Info("Setup marked as dismissed");
}
}
}

View File

@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
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 UnityEngine;
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
{
@ -20,14 +20,47 @@ namespace MCPForUnity.Editor.Windows
private McpClientConfigSection clientConfigSection;
private static readonly HashSet<MCPForUnityEditorWindow> OpenWindows = new();
private bool guiCreated = false;
public static void ShowWindow()
{
var window = GetWindow<MCPForUnityEditorWindow>("MCP For Unity");
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()
{
// Guard against repeated CreateGUI calls (e.g., domain reloads)
if (guiCreated)
return;
string basePath = AssetPathUtility.GetMcpPackageRootPath();
// Load main window UXML
@ -37,7 +70,9 @@ namespace MCPForUnity.Editor.Windows
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;
}
@ -78,8 +113,10 @@ namespace MCPForUnity.Editor.Windows
var settingsRoot = settingsTree.Instantiate();
sectionsContainer.Add(settingsRoot);
settingsSection = new McpSettingsSection(settingsRoot);
settingsSection.OnGitUrlChanged += () => clientConfigSection?.UpdateManualConfiguration();
settingsSection.OnHttpServerCommandUpdateRequested += () => connectionSection?.UpdateHttpServerCommandDisplay();
settingsSection.OnGitUrlChanged += () =>
clientConfigSection?.UpdateManualConfiguration();
settingsSection.OnHttpServerCommandUpdateRequested += () =>
connectionSection?.UpdateHttpServerCommandDisplay();
}
// Load and initialize Connection section
@ -91,7 +128,8 @@ namespace MCPForUnity.Editor.Windows
var connectionRoot = connectionTree.Instantiate();
sectionsContainer.Add(connectionRoot);
connectionSection = new McpConnectionSection(connectionRoot);
connectionSection.OnManualConfigUpdateRequested += () => clientConfigSection?.UpdateManualConfiguration();
connectionSection.OnManualConfigUpdateRequested += () =>
clientConfigSection?.UpdateManualConfiguration();
}
// Load and initialize Client Configuration section
@ -105,6 +143,8 @@ namespace MCPForUnity.Editor.Windows
clientConfigSection = new McpClientConfigSection(clientConfigRoot);
}
guiCreated = true;
// Initial updates
RefreshAllData();
}
@ -119,6 +159,7 @@ namespace MCPForUnity.Editor.Windows
{
EditorApplication.update -= OnEditorUpdate;
OpenWindows.Remove(this);
guiCreated = false;
}
private void OnFocus()
@ -163,11 +204,11 @@ namespace MCPForUnity.Editor.Windows
{
EditorApplication.delayCall += async () =>
{
if (this == null)
if (this == null || connectionSection == null)
{
return;
}
await connectionSection?.VerifyBridgeConnectionAsync();
await connectionSection.VerifyBridgeConnectionAsync();
};
}
}