feat: improve editor window UI + add transport mismatch warning (#613)
parent
ecb091eb9e
commit
cb1a7dd2a1
|
|
@ -17,6 +17,12 @@ namespace MCPForUnity.Editor.Clients
|
|||
/// <summary>Current status cached by the configurator.</summary>
|
||||
McpStatus Status { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The transport type the client is currently configured for.
|
||||
/// Returns Unknown if the client is not configured or the transport cannot be determined.
|
||||
/// </summary>
|
||||
ConfiguredTransport ConfiguredTransport { get; }
|
||||
|
||||
/// <summary>True if this client supports auto-configure.</summary>
|
||||
bool SupportsAutoConfigure { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
public string Id => client.name.Replace(" ", "").ToLowerInvariant();
|
||||
public virtual string DisplayName => client.name;
|
||||
public McpStatus Status => client.status;
|
||||
public ConfiguredTransport ConfiguredTransport => client.configuredTransport;
|
||||
public virtual bool SupportsAutoConfigure => true;
|
||||
public virtual string GetConfigureActionLabel() => "Configure";
|
||||
|
||||
|
|
@ -94,6 +95,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (!File.Exists(path))
|
||||
{
|
||||
client.SetStatus(McpStatus.NotConfigured);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
return client.status;
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +137,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (standardConfig?.mcpServers?.unityMCP != null)
|
||||
{
|
||||
args = standardConfig.mcpServers.unityMCP.args;
|
||||
configuredUrl = standardConfig.mcpServers.unityMCP.url;
|
||||
configExists = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,9 +145,24 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (!configExists)
|
||||
{
|
||||
client.SetStatus(McpStatus.MissingConfig);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
return client.status;
|
||||
}
|
||||
|
||||
// Determine and set the configured transport type
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(configuredUrl))
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Http;
|
||||
}
|
||||
else
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
bool matches = false;
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
|
|
@ -171,6 +189,9 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (result == "Configured successfully")
|
||||
{
|
||||
client.SetStatus(McpStatus.Configured);
|
||||
// Update transport after rewrite based on current server setting
|
||||
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
client.configuredTransport = useHttp ? Models.ConfiguredTransport.Http : Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -185,6 +206,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
catch (Exception ex)
|
||||
{
|
||||
client.SetStatus(McpStatus.Error, ex.Message);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
return client.status;
|
||||
|
|
@ -198,6 +220,9 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (result == "Configured successfully")
|
||||
{
|
||||
client.SetStatus(McpStatus.Configured);
|
||||
// Set transport based on current server setting
|
||||
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
client.configuredTransport = useHttp ? Models.ConfiguredTransport.Http : Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -237,12 +262,27 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (!File.Exists(path))
|
||||
{
|
||||
client.SetStatus(McpStatus.NotConfigured);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
return client.status;
|
||||
}
|
||||
|
||||
string toml = File.ReadAllText(path);
|
||||
if (CodexConfigHelper.TryParseCodexServer(toml, out _, out var args, out var url))
|
||||
{
|
||||
// Determine and set the configured transport type
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Http;
|
||||
}
|
||||
else if (args != null && args.Length > 0)
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
bool matches = false;
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
|
|
@ -262,6 +302,10 @@ namespace MCPForUnity.Editor.Clients
|
|||
return client.status;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
if (attemptAutoRewrite)
|
||||
{
|
||||
|
|
@ -269,6 +313,9 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (result == "Configured successfully")
|
||||
{
|
||||
client.SetStatus(McpStatus.Configured);
|
||||
// Update transport after rewrite based on current server setting
|
||||
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
client.configuredTransport = useHttp ? Models.ConfiguredTransport.Http : Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -283,6 +330,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
catch (Exception ex)
|
||||
{
|
||||
client.SetStatus(McpStatus.Error, ex.Message);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
return client.status;
|
||||
|
|
@ -296,6 +344,9 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (result == "Configured successfully")
|
||||
{
|
||||
client.SetStatus(McpStatus.Configured);
|
||||
// Set transport based on current server setting
|
||||
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
client.configuredTransport = useHttp ? Models.ConfiguredTransport.Http : Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -363,6 +414,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
if (string.IsNullOrEmpty(claudePath))
|
||||
{
|
||||
client.SetStatus(McpStatus.NotConfigured, "Claude CLI not found");
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
return client.status;
|
||||
}
|
||||
|
||||
|
|
@ -415,6 +467,20 @@ namespace MCPForUnity.Editor.Clients
|
|||
bool registeredWithHttp = getStdout.Contains("Type: http", StringComparison.OrdinalIgnoreCase);
|
||||
bool registeredWithStdio = getStdout.Contains("Type: stdio", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Set the configured transport based on what we detected
|
||||
if (registeredWithHttp)
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Http;
|
||||
}
|
||||
else if (registeredWithStdio)
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
else
|
||||
{
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
// Check for transport mismatch
|
||||
bool hasTransportMismatch = (currentUseHttp && registeredWithStdio) || (!currentUseHttp && registeredWithHttp);
|
||||
|
||||
|
|
@ -479,10 +545,12 @@ namespace MCPForUnity.Editor.Clients
|
|||
}
|
||||
|
||||
client.SetStatus(McpStatus.NotConfigured);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
client.SetStatus(McpStatus.Error, ex.Message);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
return client.status;
|
||||
|
|
@ -558,6 +626,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
|
||||
McpLog.Info($"Successfully registered with Claude Code using {(useHttpTransport ? "HTTP" : "stdio")} transport.");
|
||||
client.SetStatus(McpStatus.Configured);
|
||||
client.configuredTransport = useHttpTransport ? Models.ConfiguredTransport.Http : Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -577,6 +646,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
|
||||
McpLog.Info("MCP server successfully unregistered from Claude Code.");
|
||||
client.SetStatus(McpStatus.NotConfigured);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
private void Register()
|
||||
|
|
@ -645,6 +715,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
// Set status to Configured immediately after successful registration
|
||||
// The UI will trigger an async verification check separately to avoid blocking
|
||||
client.SetStatus(McpStatus.Configured);
|
||||
client.configuredTransport = useHttpTransport ? Models.ConfiguredTransport.Http : Models.ConfiguredTransport.Stdio;
|
||||
}
|
||||
|
||||
private void Unregister()
|
||||
|
|
@ -675,6 +746,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
|
||||
McpLog.Info("MCP server successfully unregistered from Claude Code.");
|
||||
client.SetStatus(McpStatus.NotConfigured);
|
||||
client.configuredTransport = Models.ConfiguredTransport.Unknown;
|
||||
}
|
||||
|
||||
public override string GetManualSnippet()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
namespace MCPForUnity.Editor.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for health check status values.
|
||||
/// Used for coordinating health state between Connection and Advanced sections.
|
||||
/// </summary>
|
||||
public static class HealthStatus
|
||||
{
|
||||
public const string Unknown = "Unknown";
|
||||
public const string Healthy = "Healthy";
|
||||
public const string PingFailed = "Ping Failed";
|
||||
public const string Unhealthy = "Unhealthy";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b945a3de2cb2683448d686c0f3174faa
|
||||
guid: c15ed2426f43860479f1b8a99a343d16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -69,7 +69,9 @@ namespace MCPForUnity.Editor.Helpers
|
|||
/// </summary>
|
||||
public static GameObject FindById(int instanceId)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
return EditorUtility.InstanceIDToObject(instanceId) as GameObject;
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -103,7 +105,9 @@ namespace MCPForUnity.Editor.Helpers
|
|||
case SearchMethod.ById:
|
||||
if (int.TryParse(searchTerm, out int instanceId))
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var obj = EditorUtility.InstanceIDToObject(instanceId) as GameObject;
|
||||
#pragma warning restore CS0618
|
||||
if (obj != null && (includeInactive || obj.activeInHierarchy))
|
||||
{
|
||||
results.Add(instanceId);
|
||||
|
|
|
|||
|
|
@ -15,5 +15,9 @@ namespace MCPForUnity.Editor.Models
|
|||
// VSCode expects a transport type; include only when explicitly set
|
||||
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string type;
|
||||
|
||||
// URL for HTTP transport mode
|
||||
[JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string url;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace MCPForUnity.Editor.Models
|
|||
public string linuxConfigPath;
|
||||
public string configStatus;
|
||||
public McpStatus status = McpStatus.NotConfigured;
|
||||
public ConfiguredTransport configuredTransport = ConfiguredTransport.Unknown;
|
||||
|
||||
// Capability flags/config for JSON-based configurators
|
||||
public bool IsVsCodeLayout; // Whether the config file follows VS Code layout (env object at root)
|
||||
|
|
|
|||
|
|
@ -14,5 +14,16 @@ namespace MCPForUnity.Editor.Models
|
|||
UnsupportedOS, // OS is not supported
|
||||
Error, // General error state
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the transport type a client is configured to use.
|
||||
/// Used to detect mismatches between server and client transport settings.
|
||||
/// </summary>
|
||||
public enum ConfiguredTransport
|
||||
{
|
||||
Unknown, // Could not determine transport type
|
||||
Stdio, // Client configured for stdio transport
|
||||
Http // Client configured for HTTP transport
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e753cf64be77814d8074be7a9451338
|
||||
guid: 7723ed5eaaccb104e93acb9fd2d8cd32
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
|
|
@ -5,24 +5,18 @@ using MCPForUnity.Editor.Constants;
|
|||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Services;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace MCPForUnity.Editor.Windows.Components.Settings
|
||||
namespace MCPForUnity.Editor.Windows.Components.Advanced
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller for the Settings section of the MCP For Unity editor window.
|
||||
/// Handles version display, debug logs, validation level, and advanced path overrides.
|
||||
/// Controller for the Advanced Settings section.
|
||||
/// Handles path overrides, server source configuration, dev mode, and package deployment.
|
||||
/// </summary>
|
||||
public class McpSettingsSection
|
||||
public class McpAdvancedSection
|
||||
{
|
||||
// UI Elements
|
||||
private Label versionLabel;
|
||||
private Toggle debugLogsToggle;
|
||||
private EnumField validationLevelField;
|
||||
private Label validationDescription;
|
||||
private Foldout advancedSettingsFoldout;
|
||||
private TextField uvxPathOverride;
|
||||
private Button browseUvxButton;
|
||||
private Button clearUvxButton;
|
||||
|
|
@ -30,6 +24,7 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
private TextField gitUrlOverride;
|
||||
private Button browseGitUrlButton;
|
||||
private Button clearGitUrlButton;
|
||||
private Toggle debugLogsToggle;
|
||||
private Toggle devModeForceRefreshToggle;
|
||||
private TextField deploySourcePath;
|
||||
private Button browseDeploySourceButton;
|
||||
|
|
@ -39,26 +34,18 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
private Label deployTargetLabel;
|
||||
private Label deployBackupLabel;
|
||||
private Label deployStatusLabel;
|
||||
|
||||
// Data
|
||||
private ValidationLevel currentValidationLevel = ValidationLevel.Standard;
|
||||
private VisualElement healthIndicator;
|
||||
private Label healthStatus;
|
||||
private Button testConnectionButton;
|
||||
|
||||
// Events
|
||||
public event Action OnGitUrlChanged;
|
||||
public event Action OnHttpServerCommandUpdateRequested;
|
||||
|
||||
// Validation levels
|
||||
private enum ValidationLevel
|
||||
{
|
||||
Basic,
|
||||
Standard,
|
||||
Comprehensive,
|
||||
Strict
|
||||
}
|
||||
public event Action OnTestConnectionRequested;
|
||||
|
||||
public VisualElement Root { get; private set; }
|
||||
|
||||
public McpSettingsSection(VisualElement root)
|
||||
public McpAdvancedSection(VisualElement root)
|
||||
{
|
||||
Root = root;
|
||||
CacheUIElements();
|
||||
|
|
@ -68,11 +55,6 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
|
||||
private void CacheUIElements()
|
||||
{
|
||||
versionLabel = Root.Q<Label>("version-label");
|
||||
debugLogsToggle = Root.Q<Toggle>("debug-logs-toggle");
|
||||
validationLevelField = Root.Q<EnumField>("validation-level");
|
||||
validationDescription = Root.Q<Label>("validation-description");
|
||||
advancedSettingsFoldout = Root.Q<Foldout>("advanced-settings-foldout");
|
||||
uvxPathOverride = Root.Q<TextField>("uv-path-override");
|
||||
browseUvxButton = Root.Q<Button>("browse-uv-button");
|
||||
clearUvxButton = Root.Q<Button>("clear-uv-button");
|
||||
|
|
@ -80,6 +62,7 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
gitUrlOverride = Root.Q<TextField>("git-url-override");
|
||||
browseGitUrlButton = Root.Q<Button>("browse-git-url-button");
|
||||
clearGitUrlButton = Root.Q<Button>("clear-git-url-button");
|
||||
debugLogsToggle = Root.Q<Toggle>("debug-logs-toggle");
|
||||
devModeForceRefreshToggle = Root.Q<Toggle>("dev-mode-force-refresh-toggle");
|
||||
deploySourcePath = Root.Q<TextField>("deploy-source-path");
|
||||
browseDeploySourceButton = Root.Q<Button>("browse-deploy-source-button");
|
||||
|
|
@ -89,46 +72,70 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
deployTargetLabel = Root.Q<Label>("deploy-target-label");
|
||||
deployBackupLabel = Root.Q<Label>("deploy-backup-label");
|
||||
deployStatusLabel = Root.Q<Label>("deploy-status-label");
|
||||
healthIndicator = Root.Q<VisualElement>("health-indicator");
|
||||
healthStatus = Root.Q<Label>("health-status");
|
||||
testConnectionButton = Root.Q<Button>("test-connection-button");
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
UpdateVersionLabel();
|
||||
// Set tooltips for fields
|
||||
if (uvxPathOverride != null)
|
||||
uvxPathOverride.tooltip = "Override path to uvx executable. Leave empty for auto-detection.";
|
||||
if (gitUrlOverride != null)
|
||||
gitUrlOverride.tooltip = "Override server source for uvx --from. Leave empty to use default PyPI package. Example local dev: /path/to/unity-mcp/Server";
|
||||
if (debugLogsToggle != null)
|
||||
{
|
||||
debugLogsToggle.tooltip = "Enable verbose debug logging to the Unity Console.";
|
||||
var debugLabel = debugLogsToggle?.parent?.Q<Label>();
|
||||
if (debugLabel != null)
|
||||
debugLabel.tooltip = debugLogsToggle.tooltip;
|
||||
}
|
||||
if (devModeForceRefreshToggle != null)
|
||||
{
|
||||
devModeForceRefreshToggle.tooltip = "When enabled, generated uvx commands add '--no-cache --refresh' before launching (slower startup, but avoids stale cached builds while iterating on the Server).";
|
||||
var forceRefreshLabel = devModeForceRefreshToggle?.parent?.Q<Label>();
|
||||
if (forceRefreshLabel != null)
|
||||
forceRefreshLabel.tooltip = devModeForceRefreshToggle.tooltip;
|
||||
}
|
||||
if (testConnectionButton != null)
|
||||
testConnectionButton.tooltip = "Test the connection between Unity and the MCP server.";
|
||||
if (deploySourcePath != null)
|
||||
deploySourcePath.tooltip = "Copy a MCPForUnity folder into this project's package location.";
|
||||
|
||||
// Set tooltips for buttons
|
||||
if (browseUvxButton != null)
|
||||
browseUvxButton.tooltip = "Browse for uvx executable";
|
||||
if (clearUvxButton != null)
|
||||
clearUvxButton.tooltip = "Clear override and use auto-detection";
|
||||
if (browseGitUrlButton != null)
|
||||
browseGitUrlButton.tooltip = "Select local server source folder";
|
||||
if (clearGitUrlButton != null)
|
||||
clearGitUrlButton.tooltip = "Clear override and use default PyPI package";
|
||||
if (browseDeploySourceButton != null)
|
||||
browseDeploySourceButton.tooltip = "Select MCPForUnity source folder";
|
||||
if (clearDeploySourceButton != null)
|
||||
clearDeploySourceButton.tooltip = "Clear deployment source path";
|
||||
if (deployButton != null)
|
||||
deployButton.tooltip = "Copy MCPForUnity to this project's package location";
|
||||
if (deployRestoreButton != null)
|
||||
deployRestoreButton.tooltip = "Restore the last backup before deployment";
|
||||
|
||||
gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
|
||||
|
||||
bool debugEnabled = EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false);
|
||||
debugLogsToggle.value = debugEnabled;
|
||||
McpLog.SetDebugLoggingEnabled(debugEnabled);
|
||||
|
||||
validationLevelField.Init(ValidationLevel.Standard);
|
||||
int savedLevel = EditorPrefs.GetInt(EditorPrefKeys.ValidationLevel, 1);
|
||||
currentValidationLevel = (ValidationLevel)Mathf.Clamp(savedLevel, 0, 3);
|
||||
validationLevelField.value = currentValidationLevel;
|
||||
UpdateValidationDescription();
|
||||
|
||||
advancedSettingsFoldout.value = false;
|
||||
gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
|
||||
devModeForceRefreshToggle.value = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false);
|
||||
|
||||
UpdatePathOverrides();
|
||||
UpdateDeploymentSection();
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
debugLogsToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
McpLog.SetDebugLoggingEnabled(evt.newValue);
|
||||
});
|
||||
|
||||
validationLevelField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
currentValidationLevel = (ValidationLevel)evt.newValue;
|
||||
EditorPrefs.SetInt(EditorPrefKeys.ValidationLevel, (int)currentValidationLevel);
|
||||
UpdateValidationDescription();
|
||||
});
|
||||
|
||||
browseUvxButton.clicked += OnBrowseUvxClicked;
|
||||
clearUvxButton.clicked += OnClearUvxClicked;
|
||||
|
||||
browseGitUrlButton.clicked += OnBrowseGitUrlClicked;
|
||||
|
||||
gitUrlOverride.RegisterValueChangedCallback(evt =>
|
||||
|
|
@ -154,6 +161,11 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
OnHttpServerCommandUpdateRequested?.Invoke();
|
||||
};
|
||||
|
||||
debugLogsToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
McpLog.SetDebugLoggingEnabled(evt.newValue);
|
||||
});
|
||||
|
||||
devModeForceRefreshToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
EditorPrefs.SetBool(EditorPrefKeys.DevModeForceServerRefresh, evt.newValue);
|
||||
|
|
@ -167,7 +179,7 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
MCPServiceLocator.Deployment.SetStoredSourcePath(path);
|
||||
|
|
@ -183,6 +195,7 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
clearDeploySourceButton.clicked += OnClearDeploySourceClicked;
|
||||
deployButton.clicked += OnDeployClicked;
|
||||
deployRestoreButton.clicked += OnRestoreBackupClicked;
|
||||
testConnectionButton.clicked += () => OnTestConnectionRequested?.Invoke();
|
||||
}
|
||||
|
||||
public void UpdatePathOverrides()
|
||||
|
|
@ -259,47 +272,11 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
}
|
||||
|
||||
gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
|
||||
debugLogsToggle.value = EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false);
|
||||
devModeForceRefreshToggle.value = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false);
|
||||
UpdateDeploymentSection();
|
||||
}
|
||||
|
||||
private void UpdateVersionLabel()
|
||||
{
|
||||
string currentVersion = AssetPathUtility.GetPackageVersion();
|
||||
versionLabel.text = $"v{currentVersion}";
|
||||
|
||||
var updateCheck = MCPServiceLocator.Updates.CheckForUpdate(currentVersion);
|
||||
|
||||
if (updateCheck.UpdateAvailable && !string.IsNullOrEmpty(updateCheck.LatestVersion))
|
||||
{
|
||||
versionLabel.text = $"\u2191 v{currentVersion} (Update available: v{updateCheck.LatestVersion})";
|
||||
versionLabel.style.color = new Color(1f, 0.7f, 0f);
|
||||
versionLabel.tooltip = $"Version {updateCheck.LatestVersion} is available. Update via Package Manager.\n\nGit URL: https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity";
|
||||
}
|
||||
else
|
||||
{
|
||||
versionLabel.style.color = StyleKeyword.Null;
|
||||
versionLabel.tooltip = $"Current version: {currentVersion}";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValidationDescription()
|
||||
{
|
||||
validationDescription.text = GetValidationLevelDescription((int)currentValidationLevel);
|
||||
}
|
||||
|
||||
private string GetValidationLevelDescription(int index)
|
||||
{
|
||||
return index switch
|
||||
{
|
||||
0 => "Only basic syntax checks (braces, quotes, comments)",
|
||||
1 => "Syntax checks + Unity best practices and warnings",
|
||||
2 => "All checks + semantic analysis and performance warnings",
|
||||
3 => "Full semantic validation with namespace/type resolution (requires Roslyn)",
|
||||
_ => "Standard validation"
|
||||
};
|
||||
}
|
||||
|
||||
private void OnBrowseUvxClicked()
|
||||
{
|
||||
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
|
||||
|
|
@ -439,5 +416,33 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
|
|||
? new StyleColor(new Color(0.85f, 0.2f, 0.2f))
|
||||
: StyleKeyword.Null;
|
||||
}
|
||||
|
||||
public void UpdateHealthStatus(bool isHealthy, string statusText)
|
||||
{
|
||||
if (healthStatus != null)
|
||||
{
|
||||
healthStatus.text = statusText;
|
||||
}
|
||||
|
||||
if (healthIndicator != null)
|
||||
{
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("disconnected");
|
||||
healthIndicator.RemoveFromClassList("unknown");
|
||||
|
||||
if (isHealthy)
|
||||
{
|
||||
healthIndicator.AddToClassList("healthy");
|
||||
}
|
||||
else if (statusText == HealthStatus.Unknown)
|
||||
{
|
||||
healthIndicator.AddToClassList("unknown");
|
||||
}
|
||||
else
|
||||
{
|
||||
healthIndicator.AddToClassList("disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bf87d9c1c3b287e4180379f65af95dca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
|
||||
<Style src="../Common.uss" />
|
||||
<ui:VisualElement name="advanced-section" class="section">
|
||||
<ui:Label text="Advanced Settings" class="section-title" />
|
||||
<ui:VisualElement class="section-content">
|
||||
<ui:VisualElement class="override-row">
|
||||
<ui:Label text="UVX Path:" class="override-label" />
|
||||
<ui:VisualElement class="status-indicator-small" name="uv-path-status" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="path-override-controls">
|
||||
<ui:TextField name="uv-path-override" readonly="true" class="override-field" />
|
||||
<ui:Button name="browse-uv-button" text="Browse" class="icon-button" />
|
||||
<ui:Button name="clear-uv-button" text="Clear" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement class="override-row" style="margin-top: 8px;">
|
||||
<ui:Label text="Server Source:" class="override-label" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="path-override-controls">
|
||||
<ui:TextField name="git-url-override" placeholder-text="/path/to/Server or git+https://..." class="override-field" />
|
||||
<ui:Button name="browse-git-url-button" text="Select" class="icon-button" />
|
||||
<ui:Button name="clear-git-url-button" text="Clear" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement class="setting-row" style="margin-top: 8px;">
|
||||
<ui:Label text="Debug Logging:" class="setting-label" />
|
||||
<ui:Toggle name="debug-logs-toggle" class="setting-toggle" />
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement class="setting-row">
|
||||
<ui:Label text="Server Health:" class="setting-label" />
|
||||
<ui:VisualElement class="status-container">
|
||||
<ui:VisualElement name="health-indicator" class="status-dot" />
|
||||
<ui:Label name="health-status" text="Unknown" class="status-text" />
|
||||
</ui:VisualElement>
|
||||
<ui:Button name="test-connection-button" text="Test" class="action-button" />
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement class="setting-row">
|
||||
<ui:Label text="Force Fresh Install:" class="setting-label" />
|
||||
<ui:Toggle name="dev-mode-force-refresh-toggle" class="setting-toggle" />
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement class="override-row" style="margin-top: 8px;">
|
||||
<ui:Label text="Package Source:" class="override-label" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="path-override-controls">
|
||||
<ui:TextField name="deploy-source-path" class="override-field" />
|
||||
<ui:Button name="browse-deploy-source-button" text="Select" class="icon-button" />
|
||||
<ui:Button name="clear-deploy-source-button" text="Clear" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="deploy-target-label" class="help-text" />
|
||||
<ui:Label name="deploy-backup-label" class="help-text" />
|
||||
<ui:VisualElement class="path-override-controls" style="margin-top: 4px;">
|
||||
<ui:Button name="deploy-button" text="Deploy" class="icon-button" />
|
||||
<ui:Button name="deploy-restore-button" text="Restore" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="deploy-status-label" class="help-text" />
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 405ae4db7c048f542ac9b8f83099bc45
|
||||
guid: d7e63a0b220a4c9458289415ad91e7df
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
|
|
@ -31,6 +31,7 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
private VisualElement claudeCliPathRow;
|
||||
private TextField claudeCliPath;
|
||||
private Button browseClaudeButton;
|
||||
private Foldout manualConfigFoldout;
|
||||
private TextField configPathField;
|
||||
private Button copyPathButton;
|
||||
private Button openFileButton;
|
||||
|
|
@ -45,6 +46,13 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
private static readonly TimeSpan StatusRefreshInterval = TimeSpan.FromSeconds(45);
|
||||
private int selectedClientIndex = 0;
|
||||
|
||||
// Events
|
||||
/// <summary>
|
||||
/// Fired when the selected client's configured transport is detected/updated.
|
||||
/// The parameter contains the client name and its configured transport.
|
||||
/// </summary>
|
||||
public event Action<string, ConfiguredTransport> OnClientTransportDetected;
|
||||
|
||||
public VisualElement Root { get; private set; }
|
||||
|
||||
public McpClientConfigSection(VisualElement root)
|
||||
|
|
@ -66,6 +74,7 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
claudeCliPathRow = Root.Q<VisualElement>("claude-cli-path-row");
|
||||
claudeCliPath = Root.Q<TextField>("claude-cli-path");
|
||||
browseClaudeButton = Root.Q<Button>("browse-claude-button");
|
||||
manualConfigFoldout = Root.Q<Foldout>("manual-config-foldout");
|
||||
configPathField = Root.Q<TextField>("config-path");
|
||||
copyPathButton = Root.Q<Button>("copy-path-button");
|
||||
openFileButton = Root.Q<Button>("open-file-button");
|
||||
|
|
@ -76,6 +85,12 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
|
||||
private void InitializeUI()
|
||||
{
|
||||
// Ensure manual config foldout starts collapsed
|
||||
if (manualConfigFoldout != null)
|
||||
{
|
||||
manualConfigFoldout.value = false;
|
||||
}
|
||||
|
||||
var clientNames = configurators.Select(c => c.DisplayName).ToList();
|
||||
clientDropdown.choices = clientNames;
|
||||
if (clientNames.Count > 0)
|
||||
|
|
@ -84,7 +99,7 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
}
|
||||
|
||||
claudeCliPathRow.style.display = DisplayStyle.None;
|
||||
|
||||
|
||||
// Initialize the configuration display for the first selected client
|
||||
UpdateClientStatus();
|
||||
UpdateManualConfiguration();
|
||||
|
|
@ -510,27 +525,48 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
return;
|
||||
}
|
||||
|
||||
clientStatusLabel.text = GetStatusDisplayString(client.Status);
|
||||
clientStatusLabel.style.color = StyleKeyword.Null;
|
||||
|
||||
switch (client.Status)
|
||||
// Check for transport mismatch
|
||||
bool hasTransportMismatch = false;
|
||||
if (client.ConfiguredTransport != ConfiguredTransport.Unknown)
|
||||
{
|
||||
case McpStatus.Configured:
|
||||
case McpStatus.Running:
|
||||
case McpStatus.Connected:
|
||||
clientStatusIndicator.AddToClassList("configured");
|
||||
break;
|
||||
case McpStatus.IncorrectPath:
|
||||
case McpStatus.CommunicationError:
|
||||
case McpStatus.NoResponse:
|
||||
clientStatusIndicator.AddToClassList("warning");
|
||||
break;
|
||||
default:
|
||||
clientStatusIndicator.AddToClassList("not-configured");
|
||||
break;
|
||||
bool serverUsesHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
ConfiguredTransport serverTransport = serverUsesHttp ? ConfiguredTransport.Http : ConfiguredTransport.Stdio;
|
||||
hasTransportMismatch = client.ConfiguredTransport != serverTransport;
|
||||
}
|
||||
|
||||
// If configured but with transport mismatch, show warning state
|
||||
if (hasTransportMismatch && (client.Status == McpStatus.Configured || client.Status == McpStatus.Running || client.Status == McpStatus.Connected))
|
||||
{
|
||||
clientStatusLabel.text = "Transport Mismatch";
|
||||
clientStatusIndicator.AddToClassList("warning");
|
||||
}
|
||||
else
|
||||
{
|
||||
clientStatusLabel.text = GetStatusDisplayString(client.Status);
|
||||
|
||||
switch (client.Status)
|
||||
{
|
||||
case McpStatus.Configured:
|
||||
case McpStatus.Running:
|
||||
case McpStatus.Connected:
|
||||
clientStatusIndicator.AddToClassList("configured");
|
||||
break;
|
||||
case McpStatus.IncorrectPath:
|
||||
case McpStatus.CommunicationError:
|
||||
case McpStatus.NoResponse:
|
||||
clientStatusIndicator.AddToClassList("warning");
|
||||
break;
|
||||
default:
|
||||
clientStatusIndicator.AddToClassList("not-configured");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clientStatusLabel.style.color = StyleKeyword.Null;
|
||||
configureButton.text = client.GetConfigureActionLabel();
|
||||
|
||||
// Notify listeners about the client's configured transport
|
||||
OnClientTransportDetected?.Invoke(client.DisplayName, client.ConfiguredTransport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<ui:TextField name="claude-cli-path" readonly="true" class="path-display-field" />
|
||||
<ui:Button name="browse-claude-button" text="Browse" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Foldout name="manual-config-foldout" text="Manual Configuration" class="manual-config-foldout">
|
||||
<ui:Foldout name="manual-config-foldout" text="Manual Configuration" value="false" class="manual-config-foldout">
|
||||
<ui:VisualElement class="manual-config-content">
|
||||
<ui:Label text="Config Path:" class="config-label" />
|
||||
<ui:VisualElement class="path-row">
|
||||
|
|
|
|||
|
|
@ -10,29 +10,38 @@
|
|||
font-size: 20px;
|
||||
-unity-font-style: bold;
|
||||
margin-bottom: 20px;
|
||||
padding: 12px;
|
||||
padding: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Section Styling */
|
||||
.section {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 14px;
|
||||
padding: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-color: rgba(0, 0, 0, 0.2);
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Remove bottom margin from last section in a stack */
|
||||
.section-stack > .section:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
-unity-font-style: bold;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 6px;
|
||||
letter-spacing: 0.3px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
padding: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Setting Rows */
|
||||
|
|
@ -41,7 +50,7 @@
|
|||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 4px;
|
||||
min-height: 24px;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
|
|
@ -49,7 +58,7 @@
|
|||
|
||||
.setting-column {
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 4px;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
|
@ -394,6 +403,23 @@
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Manual Command Foldout */
|
||||
.manual-command-foldout {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.manual-command-foldout > .unity-foldout__toggle {
|
||||
font-size: 11px;
|
||||
padding: 4px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.manual-command-foldout > .unity-foldout__content {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.advanced-settings-content {
|
||||
padding: 8px;
|
||||
margin-top: 8px;
|
||||
|
|
@ -496,6 +522,10 @@
|
|||
border-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.unity-theme-light .section-title {
|
||||
border-bottom-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.unity-theme-dark .tool-tag {
|
||||
color: rgba(220, 220, 220, 1);
|
||||
background-color: rgba(80, 80, 80, 0.6);
|
||||
|
|
@ -547,3 +577,37 @@
|
|||
.unity-theme-light .path-display-field > .unity-text-field__input {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Warning Banner (for transport mismatch, etc.) */
|
||||
.warning-banner {
|
||||
display: none;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 6px;
|
||||
background-color: rgba(255, 180, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-color: rgba(255, 180, 0, 0.5);
|
||||
}
|
||||
|
||||
.warning-banner.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.warning-banner-text {
|
||||
font-size: 11px;
|
||||
white-space: normal;
|
||||
color: rgba(180, 120, 0, 1);
|
||||
}
|
||||
|
||||
.unity-theme-dark .warning-banner {
|
||||
background-color: rgba(255, 180, 0, 0.15);
|
||||
border-color: rgba(255, 180, 0, 0.4);
|
||||
}
|
||||
|
||||
.unity-theme-dark .warning-banner-text {
|
||||
color: rgba(255, 200, 100, 1);
|
||||
}
|
||||
|
||||
.unity-theme-light .manual-command-foldout > .unity-foldout__toggle {
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using MCPForUnity.Editor.Constants;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Models;
|
||||
using MCPForUnity.Editor.Services;
|
||||
using MCPForUnity.Editor.Services.Transport;
|
||||
using UnityEditor;
|
||||
|
|
@ -27,7 +28,11 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
|
||||
// UI Elements
|
||||
private EnumField transportDropdown;
|
||||
private VisualElement transportMismatchWarning;
|
||||
private Label transportMismatchText;
|
||||
private VisualElement httpUrlRow;
|
||||
private VisualElement httpServerControlRow;
|
||||
private Foldout manualCommandFoldout;
|
||||
private VisualElement httpServerCommandSection;
|
||||
private TextField httpServerCommandField;
|
||||
private Button copyHttpServerCommandButton;
|
||||
|
|
@ -35,17 +40,11 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
private TextField httpUrlField;
|
||||
private Button startHttpServerButton;
|
||||
private Button stopHttpServerButton;
|
||||
private VisualElement projectScopedToolsRow;
|
||||
private Toggle projectScopedToolsToggle;
|
||||
private VisualElement unitySocketPortRow;
|
||||
private TextField unityPortField;
|
||||
private VisualElement statusIndicator;
|
||||
private Label connectionStatusLabel;
|
||||
private Button connectionToggleButton;
|
||||
private VisualElement healthIndicator;
|
||||
private Label healthStatusLabel;
|
||||
private VisualElement healthRow;
|
||||
private Button testConnectionButton;
|
||||
|
||||
private bool connectionToggleInProgress;
|
||||
private bool httpServerToggleInProgress;
|
||||
|
|
@ -54,11 +53,8 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
private double lastLocalServerRunningPollTime;
|
||||
private bool lastLocalServerRunning;
|
||||
|
||||
// Health status constants
|
||||
private const string HealthStatusUnknown = "Unknown";
|
||||
private const string HealthStatusHealthy = "Healthy";
|
||||
private const string HealthStatusPingFailed = "Ping Failed";
|
||||
private const string HealthStatusUnhealthy = "Unhealthy";
|
||||
// Reference to Advanced section for health status updates
|
||||
private Action<bool, string> onHealthStatusUpdate;
|
||||
|
||||
// Events
|
||||
public event Action OnManualConfigUpdateRequested;
|
||||
|
|
@ -66,6 +62,11 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
|
||||
public VisualElement Root { get; private set; }
|
||||
|
||||
public void SetHealthStatusUpdateCallback(Action<bool, string> callback)
|
||||
{
|
||||
onHealthStatusUpdate = callback;
|
||||
}
|
||||
|
||||
public McpConnectionSection(VisualElement root)
|
||||
{
|
||||
Root = root;
|
||||
|
|
@ -77,7 +78,11 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
private void CacheUIElements()
|
||||
{
|
||||
transportDropdown = Root.Q<EnumField>("transport-dropdown");
|
||||
transportMismatchWarning = Root.Q<VisualElement>("transport-mismatch-warning");
|
||||
transportMismatchText = Root.Q<Label>("transport-mismatch-text");
|
||||
httpUrlRow = Root.Q<VisualElement>("http-url-row");
|
||||
httpServerControlRow = Root.Q<VisualElement>("http-server-control-row");
|
||||
manualCommandFoldout = Root.Q<Foldout>("manual-command-foldout");
|
||||
httpServerCommandSection = Root.Q<VisualElement>("http-server-command-section");
|
||||
httpServerCommandField = Root.Q<TextField>("http-server-command");
|
||||
copyHttpServerCommandButton = Root.Q<Button>("copy-http-server-command-button");
|
||||
|
|
@ -85,21 +90,21 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
httpUrlField = Root.Q<TextField>("http-url");
|
||||
startHttpServerButton = Root.Q<Button>("start-http-server-button");
|
||||
stopHttpServerButton = Root.Q<Button>("stop-http-server-button");
|
||||
projectScopedToolsRow = Root.Q<VisualElement>("project-scoped-tools-row");
|
||||
projectScopedToolsToggle = Root.Q<Toggle>("project-scoped-tools-toggle");
|
||||
unitySocketPortRow = Root.Q<VisualElement>("unity-socket-port-row");
|
||||
unityPortField = Root.Q<TextField>("unity-port");
|
||||
statusIndicator = Root.Q<VisualElement>("status-indicator");
|
||||
connectionStatusLabel = Root.Q<Label>("connection-status");
|
||||
connectionToggleButton = Root.Q<Button>("connection-toggle");
|
||||
healthIndicator = Root.Q<VisualElement>("health-indicator");
|
||||
healthStatusLabel = Root.Q<Label>("health-status");
|
||||
healthRow = Root.Q<VisualElement>("health-row");
|
||||
testConnectionButton = Root.Q<Button>("test-connection-button");
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
// Ensure manual command foldout starts collapsed
|
||||
if (manualCommandFoldout != null)
|
||||
{
|
||||
manualCommandFoldout.value = false;
|
||||
}
|
||||
|
||||
transportDropdown.Init(TransportProtocol.HTTPLocal);
|
||||
bool useHttpTransport = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
if (!useHttpTransport)
|
||||
|
|
@ -126,15 +131,15 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
transportDropdown.value = scope == "remote" ? TransportProtocol.HTTPRemote : TransportProtocol.HTTPLocal;
|
||||
}
|
||||
|
||||
httpUrlField.value = HttpEndpointUtility.GetBaseUrl();
|
||||
// Set tooltips
|
||||
if (httpUrlField != null)
|
||||
httpUrlField.tooltip = "HTTP endpoint URL for the MCP server. Use localhost for local servers.";
|
||||
if (unityPortField != null)
|
||||
unityPortField.tooltip = "Port for Unity's internal MCP bridge socket. Used for stdio transport.";
|
||||
if (connectionToggleButton != null)
|
||||
connectionToggleButton.tooltip = "Start or end the MCP session between Unity and the server.";
|
||||
|
||||
if (projectScopedToolsToggle != null)
|
||||
{
|
||||
projectScopedToolsToggle.value = EditorPrefs.GetBool(
|
||||
EditorPrefKeys.ProjectScopedToolsLocalHttp,
|
||||
false
|
||||
);
|
||||
}
|
||||
httpUrlField.value = HttpEndpointUtility.GetBaseUrl();
|
||||
|
||||
int unityPort = EditorPrefs.GetInt(EditorPrefKeys.UnitySocketPort, 0);
|
||||
if (unityPort == 0)
|
||||
|
|
@ -146,16 +151,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
UpdateHttpFieldVisibility();
|
||||
RefreshHttpUi();
|
||||
UpdateConnectionStatus();
|
||||
|
||||
// Explain what "Health" means (it is a separate verify/ping check and can differ from session state).
|
||||
if (healthStatusLabel != null)
|
||||
{
|
||||
healthStatusLabel.tooltip = "Health is a lightweight verify/ping of the active transport. A session can be active while health is degraded.";
|
||||
}
|
||||
if (healthIndicator != null)
|
||||
{
|
||||
healthIndicator.tooltip = healthStatusLabel?.tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
|
|
@ -243,16 +238,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
};
|
||||
}
|
||||
|
||||
if (projectScopedToolsToggle != null)
|
||||
{
|
||||
projectScopedToolsToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
EditorPrefs.SetBool(EditorPrefKeys.ProjectScopedToolsLocalHttp, evt.newValue);
|
||||
UpdateHttpServerCommandDisplay();
|
||||
OnManualConfigUpdateRequested?.Invoke();
|
||||
});
|
||||
}
|
||||
|
||||
if (copyHttpServerCommandButton != null)
|
||||
{
|
||||
copyHttpServerCommandButton.clicked += () =>
|
||||
|
|
@ -276,7 +261,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
});
|
||||
|
||||
connectionToggleButton.clicked += OnConnectionToggleClicked;
|
||||
testConnectionButton.clicked += OnTestConnectionClicked;
|
||||
}
|
||||
|
||||
private void PersistHttpUrlFromField()
|
||||
|
|
@ -327,20 +311,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
connectionToggleButton.style.display = showSessionToggle ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// Hide "Test" buttons unless Debug Mode is enabled.
|
||||
if (testConnectionButton != null)
|
||||
{
|
||||
testConnectionButton.style.display = debugMode ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// Health is useful mainly for diagnostics: hide it once we're "Healthy" unless Debug Mode is enabled.
|
||||
// If health is degraded, keep it visible even outside Debug Mode so it can act as a signal.
|
||||
if (healthRow != null)
|
||||
{
|
||||
bool showHealth = debugMode || (isRunning && lastHealthStatus != HealthStatusHealthy);
|
||||
healthRow.style.display = showHealth ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
// Show instance name (project folder name) for better identification in multi-instance scenarios.
|
||||
|
|
@ -387,11 +357,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
|
||||
unityPortField.SetEnabled(!isStdioResuming);
|
||||
|
||||
healthStatusLabel.text = HealthStatusUnknown;
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("warning");
|
||||
healthIndicator.AddToClassList("unknown");
|
||||
|
||||
int savedPort = EditorPrefs.GetInt(EditorPrefKeys.UnitySocketPort, 0);
|
||||
unityPortField.value = (savedPort == 0
|
||||
? bridgeService.CurrentPort
|
||||
|
|
@ -487,26 +452,13 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
private void UpdateHttpFieldVisibility()
|
||||
{
|
||||
bool useHttp = (TransportProtocol)transportDropdown.value != TransportProtocol.Stdio;
|
||||
bool httpLocalSelected = IsHttpLocalSelected();
|
||||
|
||||
httpUrlRow.style.display = useHttp ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
UpdateProjectScopedToolsVisibility();
|
||||
httpServerControlRow.style.display = useHttp && httpLocalSelected ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
unitySocketPortRow.style.display = useHttp ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
private void UpdateProjectScopedToolsVisibility()
|
||||
{
|
||||
if (projectScopedToolsRow == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool useHttp = transportDropdown != null && (TransportProtocol)transportDropdown.value != TransportProtocol.Stdio;
|
||||
bool httpLocalSelected = IsHttpLocalSelected();
|
||||
projectScopedToolsRow.style.display = useHttp && httpLocalSelected
|
||||
? DisplayStyle.Flex
|
||||
: DisplayStyle.None;
|
||||
}
|
||||
|
||||
private bool IsHttpLocalSelected()
|
||||
{
|
||||
return transportDropdown != null && (TransportProtocol)transportDropdown.value == TransportProtocol.HTTPLocal;
|
||||
|
|
@ -562,7 +514,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
{
|
||||
UpdateStartHttpButtonState();
|
||||
UpdateHttpServerCommandDisplay();
|
||||
UpdateProjectScopedToolsVisibility();
|
||||
}
|
||||
|
||||
private async void OnHttpServerToggleClicked()
|
||||
|
|
@ -759,11 +710,6 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
}
|
||||
}
|
||||
|
||||
private async void OnTestConnectionClicked()
|
||||
{
|
||||
await VerifyBridgeConnectionAsync();
|
||||
}
|
||||
|
||||
private async Task EndOrphanedSessionAsync()
|
||||
{
|
||||
// Fire-and-forget cleanup of orphaned session when server is no longer running.
|
||||
|
|
@ -808,33 +754,26 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
var bridgeService = MCPServiceLocator.Bridge;
|
||||
if (!bridgeService.IsRunning)
|
||||
{
|
||||
healthStatusLabel.text = HealthStatusUnknown;
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("warning");
|
||||
healthIndicator.AddToClassList("unknown");
|
||||
|
||||
onHealthStatusUpdate?.Invoke(false, HealthStatus.Unknown);
|
||||
|
||||
// Only log if state changed
|
||||
if (lastHealthStatus != HealthStatusUnknown)
|
||||
if (lastHealthStatus != HealthStatus.Unknown)
|
||||
{
|
||||
McpLog.Warn("Cannot verify connection: Bridge is not running");
|
||||
lastHealthStatus = HealthStatusUnknown;
|
||||
lastHealthStatus = HealthStatus.Unknown;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await bridgeService.VerifyAsync();
|
||||
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("warning");
|
||||
healthIndicator.RemoveFromClassList("unknown");
|
||||
|
||||
string newStatus;
|
||||
bool isHealthy;
|
||||
if (result.Success && result.PingSucceeded)
|
||||
{
|
||||
newStatus = HealthStatusHealthy;
|
||||
healthStatusLabel.text = newStatus;
|
||||
healthIndicator.AddToClassList("healthy");
|
||||
|
||||
newStatus = HealthStatus.Healthy;
|
||||
isHealthy = true;
|
||||
|
||||
// Only log if state changed
|
||||
if (lastHealthStatus != newStatus)
|
||||
{
|
||||
|
|
@ -844,10 +783,9 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
}
|
||||
else if (result.HandshakeValid)
|
||||
{
|
||||
newStatus = HealthStatusPingFailed;
|
||||
healthStatusLabel.text = newStatus;
|
||||
healthIndicator.AddToClassList("warning");
|
||||
|
||||
newStatus = HealthStatus.PingFailed;
|
||||
isHealthy = false;
|
||||
|
||||
// Log once per distinct warning state
|
||||
if (lastHealthStatus != newStatus)
|
||||
{
|
||||
|
|
@ -857,10 +795,9 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
}
|
||||
else
|
||||
{
|
||||
newStatus = HealthStatusUnhealthy;
|
||||
healthStatusLabel.text = newStatus;
|
||||
healthIndicator.AddToClassList("warning");
|
||||
|
||||
newStatus = HealthStatus.Unhealthy;
|
||||
isHealthy = false;
|
||||
|
||||
// Log once per distinct error state
|
||||
if (lastHealthStatus != newStatus)
|
||||
{
|
||||
|
|
@ -868,6 +805,56 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
|
|||
lastHealthStatus = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
onHealthStatusUpdate?.Invoke(isHealthy, newStatus);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the transport mismatch warning banner based on the client's configured transport.
|
||||
/// Shows a warning if the client's transport doesn't match the server's current transport setting.
|
||||
/// </summary>
|
||||
/// <param name="clientName">The display name of the client being checked.</param>
|
||||
/// <param name="clientTransport">The transport the client is configured to use.</param>
|
||||
public void UpdateTransportMismatchWarning(string clientName, ConfiguredTransport clientTransport)
|
||||
{
|
||||
if (transportMismatchWarning == null || transportMismatchText == null)
|
||||
return;
|
||||
|
||||
// If client transport is unknown, hide the warning (we can't determine mismatch)
|
||||
if (clientTransport == ConfiguredTransport.Unknown)
|
||||
{
|
||||
transportMismatchWarning.RemoveFromClassList("visible");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the server's current transport setting
|
||||
bool serverUsesHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
|
||||
ConfiguredTransport serverTransport = serverUsesHttp ? ConfiguredTransport.Http : ConfiguredTransport.Stdio;
|
||||
|
||||
// Check for mismatch
|
||||
bool hasMismatch = clientTransport != serverTransport;
|
||||
|
||||
if (hasMismatch)
|
||||
{
|
||||
string clientTransportName = clientTransport == ConfiguredTransport.Http ? "HTTP" : "stdio";
|
||||
string serverTransportName = serverTransport == ConfiguredTransport.Http ? "HTTP" : "stdio";
|
||||
|
||||
transportMismatchText.text = $"⚠ {clientName} is configured for \"{clientTransportName}\" but server is set to \"{serverTransportName}\". " +
|
||||
"Click \"Configure\" in Client Configuration to update.";
|
||||
transportMismatchWarning.AddToClassList("visible");
|
||||
}
|
||||
else
|
||||
{
|
||||
transportMismatchWarning.RemoveFromClassList("visible");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the transport mismatch warning banner.
|
||||
/// </summary>
|
||||
public void ClearTransportMismatchWarning()
|
||||
{
|
||||
transportMismatchWarning?.RemoveFromClassList("visible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,22 @@
|
|||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
|
||||
<Style src="../Common.uss" />
|
||||
<ui:VisualElement name="connection-section" class="section">
|
||||
<ui:Label text="Connection" class="section-title" />
|
||||
<ui:Label text="Server" class="section-title" />
|
||||
<ui:VisualElement class="section-content">
|
||||
<ui:VisualElement class="setting-row">
|
||||
<ui:Label text="Transport:" class="setting-label" />
|
||||
<uie:EnumField name="transport-dropdown" class="setting-dropdown-inline" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name="transport-mismatch-warning" class="warning-banner">
|
||||
<ui:Label name="transport-mismatch-text" class="warning-banner-text" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="setting-row" name="http-url-row">
|
||||
<ui:Label text="HTTP URL:" class="setting-label" />
|
||||
<ui:TextField name="http-url" class="url-field" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name="http-server-command-section" class="manual-config-content">
|
||||
<ui:Label text="Use this command to launch the server manually:" class="config-label" />
|
||||
<ui:VisualElement class="config-json-row">
|
||||
<ui:TextField name="http-server-command" readonly="true" multiline="true" class="config-json-field" />
|
||||
<ui:Button name="copy-http-server-command-button" text="Copy" class="icon-button-vertical" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="http-server-command-hint" class="help-text" />
|
||||
<ui:VisualElement style="flex-direction: row; justify-content: flex-start; margin-bottom: 6px;">
|
||||
<ui:Button name="start-http-server-button" text="Start Server" class="action-button start-server-button" style="width: auto; flex-grow: 1;" />
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="setting-row" name="project-scoped-tools-row">
|
||||
<ui:Label text="Project-Scoped Tools:" class="setting-label" />
|
||||
<ui:Toggle name="project-scoped-tools-toggle" />
|
||||
<ui:VisualElement class="setting-row" name="http-server-control-row">
|
||||
<ui:Label text="Local Server:" class="setting-label" />
|
||||
<ui:Button name="start-http-server-button" text="Start Server" class="action-button start-server-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="setting-row" name="unity-socket-port-row">
|
||||
<ui:Label text="Unity Socket Port:" class="setting-label" />
|
||||
|
|
@ -37,14 +29,16 @@
|
|||
</ui:VisualElement>
|
||||
<ui:Button name="connection-toggle" text="Start" class="action-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="setting-row" name="health-row">
|
||||
<ui:Label text="Health:" class="setting-label" />
|
||||
<ui:VisualElement class="status-container">
|
||||
<ui:VisualElement name="health-indicator" class="status-dot" />
|
||||
<ui:Label name="health-status" text="Unknown" class="status-text" />
|
||||
<ui:Foldout name="manual-command-foldout" text="Manual Server Launch" value="false" class="manual-config-foldout">
|
||||
<ui:VisualElement name="http-server-command-section" class="manual-config-content">
|
||||
<ui:Label text="Use this command to launch the server manually:" class="config-label" />
|
||||
<ui:VisualElement class="config-json-row">
|
||||
<ui:TextField name="http-server-command" readonly="true" multiline="true" class="config-json-field" />
|
||||
<ui:Button name="copy-http-server-command-button" text="Copy" class="icon-button-vertical" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="http-server-command-hint" class="help-text" />
|
||||
</ui:VisualElement>
|
||||
<ui:Button name="test-connection-button" text="Test" class="action-button" />
|
||||
</ui:VisualElement>
|
||||
</ui:Foldout>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
|
||||
<Style src="../Common.uss" />
|
||||
<ui:VisualElement name="settings-section" class="section">
|
||||
<ui:Label text="Settings" class="section-title" />
|
||||
<ui:VisualElement class="section-content">
|
||||
<ui:VisualElement class="setting-row">
|
||||
<ui:Label text="Version:" class="setting-label" />
|
||||
<ui:Label text="..." name="version-label" class="setting-value" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="setting-row">
|
||||
<ui:Label text="Debug Mode:" class="setting-label" />
|
||||
<ui:Toggle name="debug-logs-toggle" class="setting-toggle" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="setting-column">
|
||||
<ui:Label text="Script Validation Level:" class="setting-label" />
|
||||
<uie:EnumField name="validation-level" class="setting-dropdown" />
|
||||
<ui:Label name="validation-description" class="validation-description" />
|
||||
</ui:VisualElement>
|
||||
<ui:Foldout name="advanced-settings-foldout" text="Advanced Settings" class="advanced-settings-foldout">
|
||||
<ui:VisualElement class="advanced-settings-content">
|
||||
<ui:Label text="Path Overrides (leave empty for auto-detection):" class="advanced-label" />
|
||||
<ui:VisualElement class="override-row">
|
||||
<ui:Label text="UVX Path:" class="override-label" />
|
||||
<ui:VisualElement class="status-indicator-small" name="uv-path-status" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="path-override-controls">
|
||||
<ui:TextField name="uv-path-override" readonly="true" class="override-field" />
|
||||
<ui:Button name="browse-uv-button" text="Browse" class="icon-button" />
|
||||
<ui:Button name="clear-uv-button" text="Clear" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label text="Server Source Override:" class="advanced-label" style="margin-top: 10px;" />
|
||||
<ui:Label text="Override the source used for uvx --from. Leave empty to use default package." class="help-text" />
|
||||
<ui:VisualElement class="override-row">
|
||||
<ui:Label text="Server Path/URL:" class="override-label" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="path-override-controls">
|
||||
<ui:TextField name="git-url-override" placeholder-text="/path/to/Server or git+https://..." class="override-field" />
|
||||
<ui:Button name="browse-git-url-button" text="Select" class="icon-button" />
|
||||
<ui:Button name="clear-git-url-button" text="Clear" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label text="Override example (default uses PyPI):" class="help-text" style="margin-top: 5px;" />
|
||||
<ui:Label text="• Local dev: /path/to/unity-mcp/Server" class="help-text" />
|
||||
|
||||
<ui:Label text="Dev Mode:" class="advanced-label" style="margin-top: 10px;" />
|
||||
<ui:Label text="When enabled, generated uvx commands add '--no-cache --refresh' before launching (slower startup, but avoids stale cached builds while iterating on the Server)." class="help-text" />
|
||||
<ui:VisualElement class="setting-row">
|
||||
<ui:Label text="Force fresh server install:" class="setting-label" />
|
||||
<ui:Toggle name="dev-mode-force-refresh-toggle" class="setting-toggle" />
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:Label text="Local Package Deployment:" class="advanced-label" style="margin-top: 12px;" />
|
||||
<ui:Label text="Copy a MCPForUnity folder into this project's package location." class="help-text" />
|
||||
<ui:VisualElement class="override-row">
|
||||
<ui:Label text="MCP For Unity Source Folder:" class="override-label" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement class="path-override-controls">
|
||||
<ui:TextField name="deploy-source-path" class="override-field" />
|
||||
<ui:Button name="browse-deploy-source-button" text="Select" class="icon-button" />
|
||||
<ui:Button name="clear-deploy-source-button" text="Clear" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="deploy-target-label" class="help-text" />
|
||||
<ui:Label name="deploy-backup-label" class="help-text" />
|
||||
<ui:VisualElement class="path-override-controls" style="margin-top: 4px;">
|
||||
<ui:Button name="deploy-button" text="Deploy to Project" class="icon-button" />
|
||||
<ui:Button name="deploy-restore-button" text="Restore Last Backup" class="icon-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="deploy-status-label" class="help-text" />
|
||||
</ui:VisualElement>
|
||||
</ui:Foldout>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
|
@ -17,6 +17,7 @@ namespace MCPForUnity.Editor.Windows.Components.Tools
|
|||
public class McpToolsSection
|
||||
{
|
||||
private readonly Dictionary<string, Toggle> toolToggleMap = new();
|
||||
private Toggle projectScopedToolsToggle;
|
||||
private Label summaryLabel;
|
||||
private Label noteLabel;
|
||||
private Button enableAllButton;
|
||||
|
|
@ -36,6 +37,7 @@ namespace MCPForUnity.Editor.Windows.Components.Tools
|
|||
|
||||
private void CacheUIElements()
|
||||
{
|
||||
projectScopedToolsToggle = Root.Q<Toggle>("project-scoped-tools-toggle");
|
||||
summaryLabel = Root.Q<Label>("tools-summary");
|
||||
noteLabel = Root.Q<Label>("tools-note");
|
||||
enableAllButton = Root.Q<Button>("enable-all-button");
|
||||
|
|
@ -46,6 +48,19 @@ namespace MCPForUnity.Editor.Windows.Components.Tools
|
|||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
if (projectScopedToolsToggle != null)
|
||||
{
|
||||
projectScopedToolsToggle.value = EditorPrefs.GetBool(
|
||||
EditorPrefKeys.ProjectScopedToolsLocalHttp,
|
||||
false
|
||||
);
|
||||
projectScopedToolsToggle.tooltip = "When enabled, register project-scoped tools with HTTP Local transport. Allows per-project tool customization.";
|
||||
projectScopedToolsToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
EditorPrefs.SetBool(EditorPrefKeys.ProjectScopedToolsLocalHttp, evt.newValue);
|
||||
});
|
||||
}
|
||||
|
||||
if (enableAllButton != null)
|
||||
{
|
||||
enableAllButton.AddToClassList("tool-action-button");
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
<ui:VisualElement name="tools-section" class="section">
|
||||
<ui:Label text="Tools" class="section-title" />
|
||||
<ui:VisualElement class="section-content">
|
||||
<ui:VisualElement class="setting-row" name="project-scoped-tools-row">
|
||||
<ui:Label text="Project-Scoped Tools:" class="setting-label" />
|
||||
<ui:Toggle name="project-scoped-tools-toggle" />
|
||||
</ui:VisualElement>
|
||||
<ui:Label name="tools-summary" class="help-text" text="Discovering tools..." />
|
||||
<ui:VisualElement name="tools-actions" class="tool-actions">
|
||||
<ui:Button name="enable-all-button" text="Enable All" class="tool-action-button" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f68f3b0ff9e214244ad7e57b106d5c60
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using MCPForUnity.Editor.Constants;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace MCPForUnity.Editor.Windows.Components.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller for the Script Validation section.
|
||||
/// Handles script validation level settings.
|
||||
/// </summary>
|
||||
public class McpValidationSection
|
||||
{
|
||||
// UI Elements
|
||||
private EnumField validationLevelField;
|
||||
private Label validationDescription;
|
||||
|
||||
// Data
|
||||
private ValidationLevel currentValidationLevel = ValidationLevel.Standard;
|
||||
|
||||
// Validation levels
|
||||
public enum ValidationLevel
|
||||
{
|
||||
Basic,
|
||||
Standard,
|
||||
Comprehensive,
|
||||
Strict
|
||||
}
|
||||
|
||||
public VisualElement Root { get; private set; }
|
||||
|
||||
public McpValidationSection(VisualElement root)
|
||||
{
|
||||
Root = root;
|
||||
CacheUIElements();
|
||||
InitializeUI();
|
||||
RegisterCallbacks();
|
||||
}
|
||||
|
||||
private void CacheUIElements()
|
||||
{
|
||||
validationLevelField = Root.Q<EnumField>("validation-level");
|
||||
validationDescription = Root.Q<Label>("validation-description");
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
validationLevelField.Init(ValidationLevel.Standard);
|
||||
int savedLevel = EditorPrefs.GetInt(EditorPrefKeys.ValidationLevel, 1);
|
||||
currentValidationLevel = (ValidationLevel)Mathf.Clamp(savedLevel, 0, 3);
|
||||
validationLevelField.value = currentValidationLevel;
|
||||
UpdateValidationDescription();
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
validationLevelField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
currentValidationLevel = (ValidationLevel)evt.newValue;
|
||||
EditorPrefs.SetInt(EditorPrefKeys.ValidationLevel, (int)currentValidationLevel);
|
||||
UpdateValidationDescription();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateValidationDescription()
|
||||
{
|
||||
validationDescription.text = currentValidationLevel switch
|
||||
{
|
||||
ValidationLevel.Basic => "Basic: Validates syntax only. Fast compilation checks.",
|
||||
ValidationLevel.Standard => "Standard (Recommended): Checks syntax + common errors. Balanced speed and coverage.",
|
||||
ValidationLevel.Comprehensive => "Comprehensive: Detailed validation including code quality. Slower but thorough.",
|
||||
ValidationLevel.Strict => "Strict: Maximum validation + warnings as errors. Slowest but catches all issues.",
|
||||
_ => "Unknown validation level"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c65b5fd2ed3efbf469bbc0a089f845e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
|
||||
<Style src="../Common.uss" />
|
||||
<ui:VisualElement name="validation-section" class="section">
|
||||
<ui:Label text="Script Validation" class="section-title" />
|
||||
<ui:VisualElement class="section-content">
|
||||
<ui:VisualElement class="setting-column">
|
||||
<ui:Label text="Validation Level:" class="setting-label" />
|
||||
<uie:EnumField name="validation-level" class="setting-dropdown" />
|
||||
<ui:Label name="validation-description" class="validation-description" />
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f682815a83bb6841ac61f7f399d903c
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
|
|
@ -5,10 +5,11 @@ using System.Threading.Tasks;
|
|||
using MCPForUnity.Editor.Constants;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Services;
|
||||
using MCPForUnity.Editor.Windows.Components.Advanced;
|
||||
using MCPForUnity.Editor.Windows.Components.ClientConfig;
|
||||
using MCPForUnity.Editor.Windows.Components.Connection;
|
||||
using MCPForUnity.Editor.Windows.Components.Settings;
|
||||
using MCPForUnity.Editor.Windows.Components.Tools;
|
||||
using MCPForUnity.Editor.Windows.Components.Validation;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
|
|
@ -19,14 +20,24 @@ namespace MCPForUnity.Editor.Windows
|
|||
public class MCPForUnityEditorWindow : EditorWindow
|
||||
{
|
||||
// Section controllers
|
||||
private McpSettingsSection settingsSection;
|
||||
private McpConnectionSection connectionSection;
|
||||
private McpClientConfigSection clientConfigSection;
|
||||
private McpValidationSection validationSection;
|
||||
private McpAdvancedSection advancedSection;
|
||||
private McpToolsSection toolsSection;
|
||||
|
||||
private ToolbarToggle settingsTabToggle;
|
||||
// UI Elements
|
||||
private Label versionLabel;
|
||||
private VisualElement updateNotification;
|
||||
private Label updateNotificationText;
|
||||
|
||||
private ToolbarToggle clientsTabToggle;
|
||||
private ToolbarToggle validationTabToggle;
|
||||
private ToolbarToggle advancedTabToggle;
|
||||
private ToolbarToggle toolsTabToggle;
|
||||
private VisualElement settingsPanel;
|
||||
private VisualElement clientsPanel;
|
||||
private VisualElement validationPanel;
|
||||
private VisualElement advancedPanel;
|
||||
private VisualElement toolsPanel;
|
||||
|
||||
private static readonly HashSet<MCPForUnityEditorWindow> OpenWindows = new();
|
||||
|
|
@ -37,7 +48,9 @@ namespace MCPForUnity.Editor.Windows
|
|||
|
||||
private enum ActivePanel
|
||||
{
|
||||
Settings,
|
||||
Clients,
|
||||
Validation,
|
||||
Advanced,
|
||||
Tools
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +66,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<MCPForUnityEditorWindow>("MCP For Unity");
|
||||
window.minSize = new Vector2(500, 600);
|
||||
window.minSize = new Vector2(500, 340);
|
||||
}
|
||||
|
||||
// Helper to check and manage open windows from other classes
|
||||
|
|
@ -124,20 +137,41 @@ namespace MCPForUnity.Editor.Windows
|
|||
rootVisualElement.styleSheets.Add(commonStyleSheet);
|
||||
}
|
||||
|
||||
settingsPanel = rootVisualElement.Q<VisualElement>("settings-panel");
|
||||
// Cache UI elements
|
||||
versionLabel = rootVisualElement.Q<Label>("version-label");
|
||||
updateNotification = rootVisualElement.Q<VisualElement>("update-notification");
|
||||
updateNotificationText = rootVisualElement.Q<Label>("update-notification-text");
|
||||
|
||||
clientsPanel = rootVisualElement.Q<VisualElement>("clients-panel");
|
||||
validationPanel = rootVisualElement.Q<VisualElement>("validation-panel");
|
||||
advancedPanel = rootVisualElement.Q<VisualElement>("advanced-panel");
|
||||
toolsPanel = rootVisualElement.Q<VisualElement>("tools-panel");
|
||||
var settingsContainer = rootVisualElement.Q<VisualElement>("settings-container");
|
||||
var clientsContainer = rootVisualElement.Q<VisualElement>("clients-container");
|
||||
var validationContainer = rootVisualElement.Q<VisualElement>("validation-container");
|
||||
var advancedContainer = rootVisualElement.Q<VisualElement>("advanced-container");
|
||||
var toolsContainer = rootVisualElement.Q<VisualElement>("tools-container");
|
||||
|
||||
if (settingsPanel == null || toolsPanel == null)
|
||||
if (clientsPanel == null || validationPanel == null || advancedPanel == null || toolsPanel == null)
|
||||
{
|
||||
McpLog.Error("Failed to find tab panels in UXML");
|
||||
return;
|
||||
}
|
||||
|
||||
if (settingsContainer == null)
|
||||
if (clientsContainer == null)
|
||||
{
|
||||
McpLog.Error("Failed to find settings-container in UXML");
|
||||
McpLog.Error("Failed to find clients-container in UXML");
|
||||
return;
|
||||
}
|
||||
|
||||
if (validationContainer == null)
|
||||
{
|
||||
McpLog.Error("Failed to find validation-container in UXML");
|
||||
return;
|
||||
}
|
||||
|
||||
if (advancedContainer == null)
|
||||
{
|
||||
McpLog.Error("Failed to find advanced-container in UXML");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -147,23 +181,15 @@ namespace MCPForUnity.Editor.Windows
|
|||
return;
|
||||
}
|
||||
|
||||
SetupTabs();
|
||||
|
||||
// Load and initialize Settings section
|
||||
var settingsTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
||||
$"{basePath}/Editor/Windows/Components/Settings/McpSettingsSection.uxml"
|
||||
);
|
||||
if (settingsTree != null)
|
||||
// Initialize version label
|
||||
if (versionLabel != null)
|
||||
{
|
||||
var settingsRoot = settingsTree.Instantiate();
|
||||
settingsContainer.Add(settingsRoot);
|
||||
settingsSection = new McpSettingsSection(settingsRoot);
|
||||
settingsSection.OnGitUrlChanged += () =>
|
||||
clientConfigSection?.UpdateManualConfiguration();
|
||||
settingsSection.OnHttpServerCommandUpdateRequested += () =>
|
||||
connectionSection?.UpdateHttpServerCommandDisplay();
|
||||
string version = AssetPathUtility.GetPackageVersion();
|
||||
versionLabel.text = $"v{version}";
|
||||
}
|
||||
|
||||
SetupTabs();
|
||||
|
||||
// Load and initialize Connection section
|
||||
var connectionTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
||||
$"{basePath}/Editor/Windows/Components/Connection/McpConnectionSection.uxml"
|
||||
|
|
@ -171,7 +197,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
if (connectionTree != null)
|
||||
{
|
||||
var connectionRoot = connectionTree.Instantiate();
|
||||
settingsContainer.Add(connectionRoot);
|
||||
clientsContainer.Add(connectionRoot);
|
||||
connectionSection = new McpConnectionSection(connectionRoot);
|
||||
connectionSection.OnManualConfigUpdateRequested += () =>
|
||||
clientConfigSection?.UpdateManualConfiguration();
|
||||
|
|
@ -186,8 +212,50 @@ namespace MCPForUnity.Editor.Windows
|
|||
if (clientConfigTree != null)
|
||||
{
|
||||
var clientConfigRoot = clientConfigTree.Instantiate();
|
||||
settingsContainer.Add(clientConfigRoot);
|
||||
clientsContainer.Add(clientConfigRoot);
|
||||
clientConfigSection = new McpClientConfigSection(clientConfigRoot);
|
||||
|
||||
// Wire up transport mismatch detection: when client status is checked,
|
||||
// update the connection section's warning banner if there's a mismatch
|
||||
clientConfigSection.OnClientTransportDetected += (clientName, transport) =>
|
||||
connectionSection?.UpdateTransportMismatchWarning(clientName, transport);
|
||||
}
|
||||
|
||||
// Load and initialize Validation section
|
||||
var validationTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
||||
$"{basePath}/Editor/Windows/Components/Validation/McpValidationSection.uxml"
|
||||
);
|
||||
if (validationTree != null)
|
||||
{
|
||||
var validationRoot = validationTree.Instantiate();
|
||||
validationContainer.Add(validationRoot);
|
||||
validationSection = new McpValidationSection(validationRoot);
|
||||
}
|
||||
|
||||
// Load and initialize Advanced section
|
||||
var advancedTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
||||
$"{basePath}/Editor/Windows/Components/Advanced/McpAdvancedSection.uxml"
|
||||
);
|
||||
if (advancedTree != null)
|
||||
{
|
||||
var advancedRoot = advancedTree.Instantiate();
|
||||
advancedContainer.Add(advancedRoot);
|
||||
advancedSection = new McpAdvancedSection(advancedRoot);
|
||||
|
||||
// Wire up events from Advanced section
|
||||
advancedSection.OnGitUrlChanged += () =>
|
||||
clientConfigSection?.UpdateManualConfiguration();
|
||||
advancedSection.OnHttpServerCommandUpdateRequested += () =>
|
||||
connectionSection?.UpdateHttpServerCommandDisplay();
|
||||
advancedSection.OnTestConnectionRequested += async () =>
|
||||
{
|
||||
if (connectionSection != null)
|
||||
await connectionSection.VerifyBridgeConnectionAsync();
|
||||
};
|
||||
|
||||
// Wire up health status updates from Connection to Advanced
|
||||
connectionSection?.SetHealthStatusUpdateCallback((isHealthy, statusText) =>
|
||||
advancedSection?.UpdateHealthStatus(isHealthy, statusText));
|
||||
}
|
||||
|
||||
// Load and initialize Tools section
|
||||
|
|
@ -295,32 +363,43 @@ namespace MCPForUnity.Editor.Windows
|
|||
_ = connectionSection?.VerifyBridgeConnectionAsync();
|
||||
}
|
||||
|
||||
settingsSection?.UpdatePathOverrides();
|
||||
advancedSection?.UpdatePathOverrides();
|
||||
clientConfigSection?.RefreshSelectedClient();
|
||||
}
|
||||
|
||||
private void SetupTabs()
|
||||
{
|
||||
settingsTabToggle = rootVisualElement.Q<ToolbarToggle>("settings-tab");
|
||||
clientsTabToggle = rootVisualElement.Q<ToolbarToggle>("clients-tab");
|
||||
validationTabToggle = rootVisualElement.Q<ToolbarToggle>("validation-tab");
|
||||
advancedTabToggle = rootVisualElement.Q<ToolbarToggle>("advanced-tab");
|
||||
toolsTabToggle = rootVisualElement.Q<ToolbarToggle>("tools-tab");
|
||||
|
||||
settingsPanel?.RemoveFromClassList("hidden");
|
||||
clientsPanel?.RemoveFromClassList("hidden");
|
||||
validationPanel?.RemoveFromClassList("hidden");
|
||||
advancedPanel?.RemoveFromClassList("hidden");
|
||||
toolsPanel?.RemoveFromClassList("hidden");
|
||||
|
||||
if (settingsTabToggle != null)
|
||||
if (clientsTabToggle != null)
|
||||
{
|
||||
settingsTabToggle.RegisterValueChangedCallback(evt =>
|
||||
clientsTabToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (!evt.newValue)
|
||||
{
|
||||
if (toolsTabToggle != null && !toolsTabToggle.value)
|
||||
{
|
||||
settingsTabToggle.SetValueWithoutNotify(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (evt.newValue) SwitchPanel(ActivePanel.Clients);
|
||||
});
|
||||
}
|
||||
|
||||
SwitchPanel(ActivePanel.Settings);
|
||||
if (validationTabToggle != null)
|
||||
{
|
||||
validationTabToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue) SwitchPanel(ActivePanel.Validation);
|
||||
});
|
||||
}
|
||||
|
||||
if (advancedTabToggle != null)
|
||||
{
|
||||
advancedTabToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue) SwitchPanel(ActivePanel.Advanced);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -328,23 +407,14 @@ namespace MCPForUnity.Editor.Windows
|
|||
{
|
||||
toolsTabToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (!evt.newValue)
|
||||
{
|
||||
if (settingsTabToggle != null && !settingsTabToggle.value)
|
||||
{
|
||||
toolsTabToggle.SetValueWithoutNotify(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SwitchPanel(ActivePanel.Tools);
|
||||
if (evt.newValue) SwitchPanel(ActivePanel.Tools);
|
||||
});
|
||||
}
|
||||
|
||||
var savedPanel = EditorPrefs.GetString(EditorPrefKeys.EditorWindowActivePanel, ActivePanel.Settings.ToString());
|
||||
var savedPanel = EditorPrefs.GetString(EditorPrefKeys.EditorWindowActivePanel, ActivePanel.Clients.ToString());
|
||||
if (!Enum.TryParse(savedPanel, out ActivePanel initialPanel))
|
||||
{
|
||||
initialPanel = ActivePanel.Settings;
|
||||
initialPanel = ActivePanel.Clients;
|
||||
}
|
||||
|
||||
SwitchPanel(initialPanel);
|
||||
|
|
@ -352,26 +422,51 @@ namespace MCPForUnity.Editor.Windows
|
|||
|
||||
private void SwitchPanel(ActivePanel panel)
|
||||
{
|
||||
bool showSettings = panel == ActivePanel.Settings;
|
||||
|
||||
if (settingsPanel != null)
|
||||
// Hide all panels
|
||||
if (clientsPanel != null)
|
||||
{
|
||||
settingsPanel.style.display = showSettings ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
clientsPanel.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
if (validationPanel != null)
|
||||
{
|
||||
validationPanel.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
if (advancedPanel != null)
|
||||
{
|
||||
advancedPanel.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
if (toolsPanel != null)
|
||||
{
|
||||
toolsPanel.style.display = showSettings ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
toolsPanel.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
settingsTabToggle?.SetValueWithoutNotify(showSettings);
|
||||
toolsTabToggle?.SetValueWithoutNotify(!showSettings);
|
||||
|
||||
if (!showSettings)
|
||||
// Show selected panel
|
||||
switch (panel)
|
||||
{
|
||||
EnsureToolsLoaded();
|
||||
case ActivePanel.Clients:
|
||||
if (clientsPanel != null) clientsPanel.style.display = DisplayStyle.Flex;
|
||||
break;
|
||||
case ActivePanel.Validation:
|
||||
if (validationPanel != null) validationPanel.style.display = DisplayStyle.Flex;
|
||||
break;
|
||||
case ActivePanel.Advanced:
|
||||
if (advancedPanel != null) advancedPanel.style.display = DisplayStyle.Flex;
|
||||
break;
|
||||
case ActivePanel.Tools:
|
||||
if (toolsPanel != null) toolsPanel.style.display = DisplayStyle.Flex;
|
||||
EnsureToolsLoaded();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update toggle states
|
||||
clientsTabToggle?.SetValueWithoutNotify(panel == ActivePanel.Clients);
|
||||
validationTabToggle?.SetValueWithoutNotify(panel == ActivePanel.Validation);
|
||||
advancedTabToggle?.SetValueWithoutNotify(panel == ActivePanel.Advanced);
|
||||
toolsTabToggle?.SetValueWithoutNotify(panel == ActivePanel.Tools);
|
||||
|
||||
EditorPrefs.SetString(EditorPrefKeys.EditorWindowActivePanel, panel.ToString());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,102 @@
|
|||
/* Root Layout */
|
||||
#root-container {
|
||||
padding: 16px;
|
||||
padding: 0px;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Title */
|
||||
.title {
|
||||
font-size: 20px;
|
||||
-unity-font-style: bold;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
/* Header Bar */
|
||||
.header-bar {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 18px;
|
||||
margin: 12px 12px 5px 12px;
|
||||
min-height: 44px;
|
||||
flex-shrink: 0;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 16px;
|
||||
-unity-font-style: bold;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.header-version {
|
||||
font-size: 11px;
|
||||
color: rgba(180, 210, 255, 1);
|
||||
padding: 3px 10px;
|
||||
background-color: rgba(50, 120, 200, 0.25);
|
||||
border-radius: 10px;
|
||||
border-width: 1px;
|
||||
border-color: rgba(80, 150, 220, 0.4);
|
||||
}
|
||||
|
||||
/* Update Notification */
|
||||
.update-notification {
|
||||
display: none;
|
||||
padding: 8px 16px;
|
||||
margin: 0px 12px;
|
||||
background-color: rgba(100, 200, 100, 0.15);
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-color: rgba(100, 200, 100, 0.3);
|
||||
}
|
||||
|
||||
.update-notification.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.update-notification-text {
|
||||
font-size: 11px;
|
||||
color: rgba(100, 200, 100, 1);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.tab-toolbar {
|
||||
margin-bottom: 8px;
|
||||
margin: 8px 12px 0px 12px;
|
||||
padding: 0px;
|
||||
background-color: transparent;
|
||||
border-width: 0px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.tab-toolbar .unity-toolbar-button {
|
||||
flex-grow: 1;
|
||||
min-height: 32px;
|
||||
font-size: 12px;
|
||||
border-width: 0px;
|
||||
background-color: transparent;
|
||||
margin: 0px 2px 0px 0px;
|
||||
padding: 0px 12px;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.tab-toolbar .unity-toolbar-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.tab-toolbar .unity-toolbar-button:checked {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-width: 1px;
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgba(56, 56, 56, 1);
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
.panel-scroll {
|
||||
flex-grow: 1;
|
||||
margin-top: 8px;
|
||||
margin: 0px;
|
||||
padding: 0px 8px 0px 8px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
|
|
@ -38,3 +106,28 @@
|
|||
.section-stack {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Light Theme */
|
||||
.unity-theme-light .header-bar {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.unity-theme-light .header-version {
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.unity-theme-light .tab-toolbar .unity-toolbar-button:checked {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
border-bottom-color: rgba(194, 194, 194, 1);
|
||||
}
|
||||
|
||||
.unity-theme-light .update-notification {
|
||||
background-color: rgba(100, 200, 100, 0.1);
|
||||
border-color: rgba(100, 200, 100, 0.25);
|
||||
}
|
||||
|
||||
.unity-theme-dark .update-notification-text {
|
||||
color: rgba(150, 255, 150, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,26 @@
|
|||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
|
||||
<ui:VisualElement name="root-container" class="root-layout">
|
||||
<ui:Label text="MCP For Unity" name="title" class="title" />
|
||||
<ui:VisualElement name="header-bar" class="header-bar">
|
||||
<ui:Label text="MCP For Unity" name="title" class="header-title" />
|
||||
<ui:Label text="v9.1.0" name="version-label" class="header-version" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name="update-notification" class="update-notification">
|
||||
<ui:Label name="update-notification-text" class="update-notification-text" />
|
||||
</ui:VisualElement>
|
||||
<uie:Toolbar name="tab-toolbar" class="tab-toolbar">
|
||||
<uie:ToolbarToggle name="settings-tab" text="Settings" value="true" />
|
||||
<uie:ToolbarToggle name="clients-tab" text="Connect" value="true" />
|
||||
<uie:ToolbarToggle name="tools-tab" text="Tools" />
|
||||
<uie:ToolbarToggle name="validation-tab" text="Scripts" />
|
||||
<uie:ToolbarToggle name="advanced-tab" text="Advanced" />
|
||||
</uie:Toolbar>
|
||||
<ui:ScrollView name="settings-panel" class="panel-scroll" style="flex-grow: 1;">
|
||||
<ui:VisualElement name="settings-container" class="section-stack" />
|
||||
<ui:ScrollView name="clients-panel" class="panel-scroll" style="flex-grow: 1;">
|
||||
<ui:VisualElement name="clients-container" class="section-stack" />
|
||||
</ui:ScrollView>
|
||||
<ui:ScrollView name="validation-panel" class="panel-scroll hidden" style="flex-grow: 1;">
|
||||
<ui:VisualElement name="validation-container" class="section-stack" />
|
||||
</ui:ScrollView>
|
||||
<ui:ScrollView name="advanced-panel" class="panel-scroll hidden" style="flex-grow: 1;">
|
||||
<ui:VisualElement name="advanced-container" class="section-stack" />
|
||||
</ui:ScrollView>
|
||||
<ui:ScrollView name="tools-panel" class="panel-scroll hidden" style="flex-grow: 1;">
|
||||
<ui:VisualElement name="tools-container" class="section-stack" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue