using UnityEditor; using UnityEngine; using LLM.Editor.Core; using LLM.Editor.Client; using LLM.Editor.Commands; using LLM.Editor.Analysis; using System.Threading.Tasks; using System.Collections.Generic; namespace LLM.Editor.GUI { public class MCPWindow : EditorWindow { private string _promptText = "The grenade launcher should hit the target."; private Vector2 _scrollPos; private bool _isRequestInProgress; private readonly List _stagedContextObjects = new(); private List _requestedRoles = new(); private bool _isAnalysisContextRequested; private ILlmApiClient _apiClient; [MenuItem("Tools/LLM/MCP Assistant")] public static void ShowWindow() { GetWindow("MCP Assistant"); } private void OnEnable() { CommandExecutor.OnQueueUpdated += Repaint; RequestAnalysisContextCommand.OnAnalysisContextRequested += HandleAnalysisContextRequested; CommandExecutor.OnContextReadyForNextTurn += HandleContextReadyForNextTurn; _apiClient = ApiClientFactory.GetClient(); } private void OnDisable() { CommandExecutor.OnQueueUpdated -= Repaint; RequestAnalysisContextCommand.OnAnalysisContextRequested -= HandleAnalysisContextRequested; CommandExecutor.OnContextReadyForNextTurn -= HandleContextReadyForNextTurn; } private void HandleAnalysisContextRequested(RequestAnalysisContextParams contextParams) { _isAnalysisContextRequested = true; _requestedRoles = new List(contextParams.subjectRoles); // Ensure our staging list has enough slots for what was requested while (_stagedContextObjects.Count < _requestedRoles.Count) { _stagedContextObjects.Add(null); } Repaint(); } private void HandleContextReadyForNextTurn(string detailedContext) { Debug.Log("[MCPWindow] Received detailed context. Sending follow-up to LLM."); _ = SendFollowUpAsync(detailedContext); } private void OnGUI() { EditorGUILayout.LabelField("LLM Co-Pilot", EditorStyles.boldLabel); DrawSessionManagement(); EditorGUILayout.Space(); UnityEngine.GUI.enabled = SessionManager.HasActiveSession(); DrawContextStagingArea(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Enter your request:"); _promptText = EditorGUILayout.TextArea(_promptText, GUILayout.Height(60)); DrawActionButton(); EditorGUILayout.Space(); DrawCommandQueue(); UnityEngine.GUI.enabled = true; } private void DrawSessionManagement() { EditorGUILayout.BeginHorizontal(); if (SessionManager.HasActiveSession()) { EditorGUILayout.LabelField($"Session: {SessionManager.GetCurrentSessionId()}"); if (GUILayout.Button("End Session")) { SessionManager.EndSession(); _stagedContextObjects.Clear(); _isAnalysisContextRequested = false; } } else { if (GUILayout.Button("Start New Session")) { SessionManager.StartNewSession(); } } EditorGUILayout.EndHorizontal(); } private void DrawContextStagingArea() { EditorGUILayout.LabelField("Context (Drag & Drop Assets Here)", EditorStyles.boldLabel); if (GUILayout.Button("Add Context Slot", GUILayout.Width(150))) { _stagedContextObjects.Add(null); if(_isAnalysisContextRequested) _requestedRoles.Add("Custom"); } var indexToRemove = -1; for (var i = 0; i < _stagedContextObjects.Count; i++) { EditorGUILayout.BeginHorizontal(); var label = (_isAnalysisContextRequested && i < _requestedRoles.Count) ? _requestedRoles[i] : $"Context {i + 1}"; _stagedContextObjects[i] = EditorGUILayout.ObjectField(label, _stagedContextObjects[i], typeof(Object), true); if (GUILayout.Button("X", GUILayout.Width(20))) { indexToRemove = i; } EditorGUILayout.EndHorizontal(); } if (indexToRemove == -1) return; _stagedContextObjects.RemoveAt(indexToRemove); if(_isAnalysisContextRequested && indexToRemove < _requestedRoles.Count) { _requestedRoles.RemoveAt(indexToRemove); } Repaint(); } private void DrawActionButton() { UnityEngine.GUI.enabled = SessionManager.HasActiveSession() && !_isRequestInProgress; if (_isAnalysisContextRequested) { if (!GUILayout.Button("Provide Staged Context to LLM")) return; var contextSummary = ContextBuilder.BuildTier1Summary(_stagedContextObjects); var followUpMessage = "Here is the context you requested:\n" + contextSummary; _ = SendFollowUpAsync(followUpMessage); } else { if (GUILayout.Button(_isRequestInProgress ? "Waiting for response..." : "Send Prompt")) { _ = SendInitialPromptAsync(); } } } private void DrawCommandQueue() { UnityEngine.GUI.enabled = !_isRequestInProgress; EditorGUILayout.LabelField("Pending Commands", EditorStyles.boldLabel); _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, EditorStyles.helpBox); if (CommandExecutor.HasPendingCommands()) { var nextCommand = CommandExecutor.GetNextCommand(); EditorGUILayout.LabelField("Next Up: " + nextCommand.commandName); if (GUILayout.Button("Execute Next Command")) { CommandExecutor.ExecuteNextCommand(); } } else { EditorGUILayout.LabelField("No pending commands."); } EditorGUILayout.EndScrollView(); } private async Task SendInitialPromptAsync() { _isRequestInProgress = true; Repaint(); // Pass the staged objects to the API client await _apiClient.SendPrompt(_promptText, _stagedContextObjects); _isRequestInProgress = false; ClearStagingArea(); Repaint(); } private async Task SendFollowUpAsync(string followUpMessage) { _isRequestInProgress = true; Repaint(); await _apiClient.SendFollowUp(followUpMessage); _isRequestInProgress = false; // Only clear the staging area if the follow-up was initiated by the user // providing context. This preserves the UI state otherwise. if (_isAnalysisContextRequested) { ClearStagingArea(); } Repaint(); } private void ClearStagingArea() { _isAnalysisContextRequested = false; _stagedContextObjects.Clear(); _requestedRoles.Clear(); } } }