using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using SynapticAIPro;
using Newtonsoft.Json;
namespace SynapticPro
{
///
/// Project planning and Todo management window
/// Displays and manages AI-generated plans
///
public class NexusProjectPlannerWindow : EditorWindow
{
// [MenuItem("Window/Synaptic Pro/📋 Project Planner")]
public static void ShowWindow()
{
var window = GetWindow("📋 Project Planner");
window.minSize = new Vector2(600, 400);
window.Show();
}
private Vector2 scrollPosition;
private ProjectPlan currentPlan;
private List tasks = new List();
private string newTaskInput = "";
private int selectedTaskIndex = -1;
// Styles
private GUIStyle headerStyle;
private GUIStyle taskStyle;
private GUIStyle completedTaskStyle;
private GUIStyle phaseStyle;
[Serializable]
public class ProjectPlan
{
public string title = "New Project";
public string overview = "";
public List phases = new List();
public string currentPhase = "planning";
public float progress = 0f;
}
[Serializable]
public class ProjectPhase
{
public string name;
public List tasks;
public bool isCompleted;
}
[Serializable]
public class ProjectTask
{
public int id;
public string name;
public string description;
public string status = "pending"; // pending, in_progress, completed
public string priority = "medium"; // low, medium, high
public List subtasks;
public DateTime createdAt;
public DateTime? completedAt;
}
private void OnEnable()
{
// Setup WebSocket message reception
NexusWebSocketClient.Instance.OnMessageReceived += OnWebSocketMessage;
// Load saved data
LoadProjectData();
}
private void OnDisable()
{
NexusWebSocketClient.Instance.OnMessageReceived -= OnWebSocketMessage;
// Save data
SaveProjectData();
}
private void InitializeStyles()
{
headerStyle = new GUIStyle(EditorStyles.largeLabel)
{
fontSize = 20,
fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter
};
taskStyle = new GUIStyle(EditorStyles.label)
{
fontSize = 14,
padding = new RectOffset(20, 10, 5, 5),
wordWrap = true
};
completedTaskStyle = new GUIStyle(taskStyle)
{
normal = { textColor = new Color(0.5f, 0.5f, 0.5f) },
fontStyle = FontStyle.Italic
};
phaseStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = 16,
padding = new RectOffset(10, 10, 10, 5)
};
}
private void OnGUI()
{
if (headerStyle == null)
InitializeStyles();
DrawHeader();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
if (currentPlan != null)
{
DrawProjectOverview();
DrawPhases();
}
DrawTaskList();
DrawTaskInput();
EditorGUILayout.EndScrollView();
}
private void DrawHeader()
{
EditorGUILayout.Space(10);
GUILayout.Label("📋 Project Planner", headerStyle, GUILayout.Height(30));
if (currentPlan != null)
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
// Progress bar
var rect = GUILayoutUtility.GetRect(300, 20);
EditorGUI.ProgressBar(rect, currentPlan.progress, $"Progress: {currentPlan.progress * 100:F0}%");
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space(10);
}
private void DrawProjectOverview()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
// Project title
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Project:", GUILayout.Width(80));
currentPlan.title = EditorGUILayout.TextField(currentPlan.title);
EditorGUILayout.EndHorizontal();
// Overview
GUILayout.Label("Overview:", EditorStyles.boldLabel);
currentPlan.overview = EditorGUILayout.TextArea(currentPlan.overview, GUILayout.Height(60));
EditorGUILayout.EndVertical();
EditorGUILayout.Space(10);
}
private void DrawPhases()
{
if (currentPlan.phases.Count == 0) return;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("📅 Development Phases", EditorStyles.boldLabel);
foreach (var phase in currentPlan.phases)
{
EditorGUILayout.BeginHorizontal();
// Checkbox
bool wasCompleted = phase.isCompleted;
phase.isCompleted = EditorGUILayout.Toggle(phase.isCompleted, GUILayout.Width(20));
if (wasCompleted != phase.isCompleted)
{
UpdateProgress();
}
// Phase name
var style = phase.isCompleted ? completedTaskStyle : phaseStyle;
GUILayout.Label(phase.name, style);
EditorGUILayout.EndHorizontal();
// Phase tasks
if (phase.tasks != null && phase.tasks.Count > 0)
{
EditorGUI.indentLevel++;
foreach (var task in phase.tasks)
{
EditorGUILayout.LabelField($"• {task}", EditorStyles.miniLabel);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.Space(5);
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(10);
}
private void DrawTaskList()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("✅ Task List", EditorStyles.boldLabel);
if (tasks.Count == 0)
{
EditorGUILayout.HelpBox("No tasks available. Try telling AI \"I want to create something like...\"", MessageType.Info);
}
else
{
for (int i = 0; i < tasks.Count; i++)
{
DrawTask(tasks[i], i, 0);
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(10);
}
private void DrawTask(ProjectTask task, int index, int indent)
{
EditorGUILayout.BeginHorizontal();
// Indent
GUILayout.Space(indent * 20);
// Checkbox
bool wasCompleted = task.status == "completed";
bool isCompleted = EditorGUILayout.Toggle(wasCompleted, GUILayout.Width(20));
if (wasCompleted != isCompleted)
{
task.status = isCompleted ? "completed" : "pending";
task.completedAt = isCompleted ? DateTime.Now : (DateTime?)null;
UpdateProgress();
}
// Priority indicator
var priorityColor = task.priority == "high" ? Color.red :
task.priority == "medium" ? Color.yellow :
Color.green;
var oldColor = GUI.color;
GUI.color = priorityColor;
GUILayout.Label("●", GUILayout.Width(15));
GUI.color = oldColor;
// Task name
var style = task.status == "completed" ? completedTaskStyle : taskStyle;
if (GUILayout.Button(task.name, style))
{
selectedTaskIndex = index;
}
// Status
var statusIcon = task.status == "completed" ? "✅" :
task.status == "in_progress" ? "🔄" : "⏳";
GUILayout.Label(statusIcon, GUILayout.Width(25));
// Delete button
if (GUILayout.Button("×", GUILayout.Width(20)))
{
tasks.Remove(task);
}
EditorGUILayout.EndHorizontal();
// Selected task details
if (selectedTaskIndex == index && !string.IsNullOrEmpty(task.description))
{
EditorGUI.indentLevel++;
EditorGUILayout.HelpBox(task.description, MessageType.None);
EditorGUI.indentLevel--;
}
// Subtasks
if (task.subtasks != null && task.subtasks.Count > 0)
{
foreach (var subtask in task.subtasks)
{
DrawTask(subtask, -1, indent + 1);
}
}
}
private void DrawTaskInput()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("New Task:", GUILayout.Width(80));
newTaskInput = EditorGUILayout.TextField(newTaskInput);
if (GUILayout.Button("Add", GUILayout.Width(60)))
{
if (!string.IsNullOrEmpty(newTaskInput))
{
AddTask(newTaskInput);
newTaskInput = "";
GUI.FocusControl(null);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void AddTask(string taskName, string priority = "medium")
{
var task = new ProjectTask
{
id = tasks.Count + 1,
name = taskName,
status = "pending",
priority = priority,
createdAt = DateTime.Now,
subtasks = new List()
};
tasks.Add(task);
SaveProjectData();
}
private void OnWebSocketMessage(string message)
{
try
{
var data = JsonConvert.DeserializeObject>(message);
if (data != null && data.ContainsKey("type"))
{
var type = data["type"].ToString();
switch (type)
{
case "project_plan":
UpdateProjectPlan(data);
break;
case "task_list":
UpdateTaskList(data);
break;
case "task_update":
UpdateTask(data);
break;
}
}
}
catch (Exception e)
{
Debug.LogError($"[Project Planner] Error processing message: {e.Message}");
}
}
private void UpdateProjectPlan(Dictionary data)
{
if (data.ContainsKey("plan"))
{
var planData = data["plan"] as Newtonsoft.Json.Linq.JObject;
if (planData != null)
{
currentPlan = planData.ToObject();
Repaint();
}
}
}
private void UpdateTaskList(Dictionary data)
{
if (data.ContainsKey("tasks"))
{
var tasksData = data["tasks"] as Newtonsoft.Json.Linq.JArray;
if (tasksData != null)
{
tasks = tasksData.ToObject>();
Repaint();
}
}
}
private void UpdateTask(Dictionary data)
{
if (data.ContainsKey("taskId") && data.ContainsKey("status"))
{
int taskId = Convert.ToInt32(data["taskId"]);
string status = data["status"].ToString();
var task = tasks.Find(t => t.id == taskId);
if (task != null)
{
task.status = status;
if (status == "completed")
{
task.completedAt = DateTime.Now;
}
UpdateProgress();
Repaint();
}
}
}
private void UpdateProgress()
{
if (currentPlan != null)
{
int totalTasks = tasks.Count;
int completedTasks = tasks.FindAll(t => t.status == "completed").Count;
if (totalTasks > 0)
{
currentPlan.progress = (float)completedTasks / totalTasks;
}
// Also consider phase progress
if (currentPlan.phases.Count > 0)
{
int completedPhases = currentPlan.phases.FindAll(p => p.isCompleted).Count;
float phaseProgress = (float)completedPhases / currentPlan.phases.Count;
// Overall progress is average of task progress and phase progress
currentPlan.progress = (currentPlan.progress + phaseProgress) / 2f;
}
}
}
private void LoadProjectData()
{
// Load saved data from EditorPrefs
string planJson = EditorPrefs.GetString("NexusProjectPlan", "");
if (!string.IsNullOrEmpty(planJson))
{
currentPlan = JsonConvert.DeserializeObject(planJson);
}
else
{
currentPlan = new ProjectPlan();
}
string tasksJson = EditorPrefs.GetString("NexusProjectTasks", "");
if (!string.IsNullOrEmpty(tasksJson))
{
tasks = JsonConvert.DeserializeObject>(tasksJson);
}
}
private void SaveProjectData()
{
// Save to EditorPrefs
if (currentPlan != null)
{
EditorPrefs.SetString("NexusProjectPlan", JsonConvert.SerializeObject(currentPlan));
}
if (tasks != null && tasks.Count > 0)
{
EditorPrefs.SetString("NexusProjectTasks", JsonConvert.SerializeObject(tasks));
}
}
}
}