chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Setup
|
||||
{
|
||||
public class McpForUnitySkillInstaller : EditorWindow
|
||||
{
|
||||
private const string RepoUrlKey = "UnityMcpSkillSync.RepoUrl";
|
||||
private const string BranchKey = "UnityMcpSkillSync.Branch";
|
||||
private const string CliKey = "UnityMcpSkillSync.Cli";
|
||||
private const string InstallDirKey = "UnityMcpSkillSync.InstallDir";
|
||||
private const string CodexCli = "codex";
|
||||
private const string ClaudeCli = "claude";
|
||||
private static readonly string[] BranchOptions = { "beta", "main" };
|
||||
private static readonly string[] CliOptions = { CodexCli, ClaudeCli };
|
||||
|
||||
private string _repoUrl;
|
||||
private string _targetBranch;
|
||||
private string _cliType;
|
||||
private string _installDir;
|
||||
private Vector2 _scroll;
|
||||
private volatile bool _isRunning;
|
||||
private readonly ConcurrentQueue<string> _pendingLogs = new();
|
||||
private readonly StringBuilder _logBuilder = new(4096);
|
||||
|
||||
public static void OpenWindow()
|
||||
{
|
||||
GetWindow<McpForUnitySkillInstaller>("Unity MCP Skill Install(Sync)");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var userHome = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
_repoUrl = EditorPrefs.GetString(RepoUrlKey, "https://github.com/CoplayDev/unity-mcp");
|
||||
_targetBranch = EditorPrefs.GetString(BranchKey, "beta");
|
||||
if (!BranchOptions.Contains(_targetBranch))
|
||||
{
|
||||
_targetBranch = "beta";
|
||||
}
|
||||
_cliType = EditorPrefs.GetString(CliKey, CodexCli);
|
||||
if (!CliOptions.Contains(_cliType))
|
||||
{
|
||||
_cliType = CodexCli;
|
||||
}
|
||||
_installDir = EditorPrefs.GetString(InstallDirKey, GetDefaultInstallDir(userHome, _cliType));
|
||||
EditorApplication.update += OnEditorUpdate;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorApplication.update -= OnEditorUpdate;
|
||||
EditorPrefs.SetString(RepoUrlKey, _repoUrl);
|
||||
EditorPrefs.SetString(BranchKey, _targetBranch);
|
||||
EditorPrefs.SetString(CliKey, _cliType);
|
||||
EditorPrefs.SetString(InstallDirKey, _installDir);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
FlushPendingLogs();
|
||||
EditorGUILayout.HelpBox("Sync Unity MCP Skill to the latest on the selected branch and output the changed file list.", MessageType.Info);
|
||||
EditorGUILayout.Space(4f);
|
||||
|
||||
EditorGUILayout.LabelField("Config", EditorStyles.boldLabel);
|
||||
using (new EditorGUI.DisabledScope(_isRunning))
|
||||
{
|
||||
_repoUrl = EditorGUILayout.TextField("Repo URL", _repoUrl);
|
||||
var branchIndex = Array.IndexOf(BranchOptions, _targetBranch);
|
||||
if (branchIndex < 0)
|
||||
{
|
||||
branchIndex = 0;
|
||||
}
|
||||
|
||||
var selectedBranchIndex = EditorGUILayout.Popup("Branch", branchIndex, BranchOptions);
|
||||
_targetBranch = BranchOptions[selectedBranchIndex];
|
||||
|
||||
var cliIndex = Array.IndexOf(CliOptions, _cliType);
|
||||
if (cliIndex < 0)
|
||||
{
|
||||
cliIndex = 0;
|
||||
}
|
||||
|
||||
var selectedCliIndex = EditorGUILayout.Popup("CLI", cliIndex, CliOptions);
|
||||
if (selectedCliIndex != cliIndex)
|
||||
{
|
||||
var previousCli = _cliType;
|
||||
_cliType = CliOptions[selectedCliIndex];
|
||||
TryApplyCliDefaultInstallPath(previousCli, _cliType);
|
||||
}
|
||||
|
||||
_installDir = EditorGUILayout.TextField("Install Dir", _installDir);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(8f);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
using (new EditorGUI.DisabledScope(_isRunning))
|
||||
{
|
||||
if (GUILayout.Button($"Sync Latest ({_targetBranch})", GUILayout.Height(32f)))
|
||||
{
|
||||
AppendLineImmediate("Sync task queued...");
|
||||
AppendLineImmediate("Will use GitHub API to read the remote directory tree and perform incremental sync (no repository clone).");
|
||||
RunSyncLatest();
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Clear Log", GUILayout.Width(100f), GUILayout.Height(32f)))
|
||||
{
|
||||
_logBuilder.Clear();
|
||||
while (_pendingLogs.TryDequeue(out _))
|
||||
{
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(8f);
|
||||
EditorGUILayout.LabelField("Output", EditorStyles.boldLabel);
|
||||
_scroll = EditorGUILayout.BeginScrollView(_scroll);
|
||||
EditorGUILayout.TextArea(_logBuilder.ToString(), GUILayout.ExpandHeight(true));
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void OnEditorUpdate()
|
||||
{
|
||||
var changed = FlushPendingLogs();
|
||||
if (_isRunning || changed)
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void RunSyncLatest()
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
SkillSyncService.SyncAsync(_repoUrl, _installDir, _targetBranch,
|
||||
line => _pendingLogs.Enqueue($"[{DateTime.Now:HH:mm:ss}] {SanitizeLogLine(line)}"),
|
||||
result =>
|
||||
{
|
||||
_isRunning = false;
|
||||
if (result.Success)
|
||||
{
|
||||
_pendingLogs.Enqueue($"[{DateTime.Now:HH:mm:ss}] Sync complete: +{result.Added} ~{result.Updated} -{result.Deleted}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_pendingLogs.Enqueue($"[{DateTime.Now:HH:mm:ss}] [ERROR] {result.Error}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void TryApplyCliDefaultInstallPath(string previousCli, string currentCli)
|
||||
{
|
||||
var userHome = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
var previousDefaultInstall = GetDefaultInstallDir(userHome, previousCli);
|
||||
var currentDefaultInstall = GetDefaultInstallDir(userHome, currentCli);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_installDir) || PathsEqual(_installDir, previousDefaultInstall))
|
||||
{
|
||||
_installDir = currentDefaultInstall;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDefaultInstallDir(string userHome, string cliType)
|
||||
{
|
||||
var baseDir = IsClaudeCli(cliType) ? ".claude" : ".codex";
|
||||
return Path.Combine(userHome, baseDir, "skills/unity-mcp-skill");
|
||||
}
|
||||
|
||||
private static bool IsClaudeCli(string cliType)
|
||||
{
|
||||
return string.Equals(cliType, ClaudeCli, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static bool PathsEqual(string left, string right)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(left) || string.IsNullOrWhiteSpace(right))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return string.Equals(
|
||||
SkillSyncService.ExpandPath(left),
|
||||
SkillSyncService.ExpandPath(right),
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendLineImmediate(string line)
|
||||
{
|
||||
var sanitized = SanitizeLogLine(line);
|
||||
if (string.IsNullOrWhiteSpace(sanitized))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logBuilder.AppendLine($"[{DateTime.Now:HH:mm:ss}] {sanitized}");
|
||||
_scroll.y = float.MaxValue;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private bool FlushPendingLogs()
|
||||
{
|
||||
var hasNewLine = false;
|
||||
while (_pendingLogs.TryDequeue(out var line))
|
||||
{
|
||||
_logBuilder.AppendLine(line);
|
||||
hasNewLine = true;
|
||||
}
|
||||
|
||||
if (hasNewLine)
|
||||
{
|
||||
_scroll.y = float.MaxValue;
|
||||
}
|
||||
|
||||
return hasNewLine;
|
||||
}
|
||||
|
||||
private static string SanitizeLogLine(string line)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder(line.Length);
|
||||
var inEscape = false;
|
||||
foreach (var ch in line)
|
||||
{
|
||||
if (inEscape)
|
||||
{
|
||||
if (ch >= '@' && ch <= '~')
|
||||
{
|
||||
inEscape = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\u001b')
|
||||
{
|
||||
inEscape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\t' || (ch >= ' ' && ch != 127))
|
||||
{
|
||||
sb.Append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString().Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user