feat(installer): rebuild package manager with ui toolkit
Publish UPM package / publish (push) Successful in 2s
Publish UPM package / publish (push) Successful in 2s
This commit is contained in:
@@ -2,145 +2,469 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace ShrinkSDK.Installer
|
||||
{
|
||||
internal sealed class ShrinkSdkInstallerWindow : EditorWindow
|
||||
{
|
||||
private readonly Dictionary<string, UnityEditor.PackageManager.PackageInfo> _installed =
|
||||
new(StringComparer.Ordinal);
|
||||
private ListRequest? _listRequest;
|
||||
private int _selectedPackageIndex;
|
||||
private string _status = "Ready";
|
||||
private MessageType _statusType = MessageType.Info;
|
||||
private bool _resolvePending;
|
||||
private double _resolveRequestedAt;
|
||||
private HelpBox? _statusBox;
|
||||
private Label? _registryState;
|
||||
private VisualElement? _page;
|
||||
private ToolbarToggle? _bundleTab;
|
||||
private ToolbarToggle? _packageTab;
|
||||
private string _searchText = string.Empty;
|
||||
private bool _showBundles = true;
|
||||
|
||||
[MenuItem("ShrinkSDK/Packages")]
|
||||
[MenuItem("ShrinkSDK/包管理")]
|
||||
private static void Open()
|
||||
{
|
||||
GetWindow<ShrinkSdkInstallerWindow>("ShrinkSDK Packages");
|
||||
var window = GetWindow<ShrinkSdkInstallerWindow>("ShrinkSDK 包管理");
|
||||
window.minSize = new Vector2(820f, 560f);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
public void CreateGUI()
|
||||
{
|
||||
RefreshDiagnostics();
|
||||
var root = rootVisualElement;
|
||||
root.Clear();
|
||||
root.style.flexDirection = FlexDirection.Column;
|
||||
root.Add(BuildHeader());
|
||||
root.Add(BuildToolbar());
|
||||
|
||||
_page = new ScrollView(ScrollViewMode.Vertical);
|
||||
_page.style.flexGrow = 1f;
|
||||
_page.style.paddingLeft = 16f;
|
||||
_page.style.paddingRight = 16f;
|
||||
_page.style.paddingTop = 12f;
|
||||
_page.style.paddingBottom = 16f;
|
||||
root.Add(_page);
|
||||
|
||||
_statusBox = new HelpBox("正在读取已安装包……", HelpBoxMessageType.Info);
|
||||
_statusBox.style.marginLeft = 16f;
|
||||
_statusBox.style.marginRight = 16f;
|
||||
_statusBox.style.marginBottom = 12f;
|
||||
root.Add(_statusBox);
|
||||
|
||||
RenderPage();
|
||||
RefreshPackages();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
private VisualElement BuildHeader()
|
||||
{
|
||||
EditorGUILayout.LabelField("ShrinkSDK", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(4f);
|
||||
EditorGUILayout.LabelField("Registry", ShrinkSdkManifestStore.HasShrinkSdkRegistry()
|
||||
? ShrinkSdkPackageCatalog.RegistryUrl
|
||||
: "Not configured");
|
||||
var header = new VisualElement();
|
||||
header.style.paddingLeft = 16f;
|
||||
header.style.paddingRight = 16f;
|
||||
header.style.paddingTop = 14f;
|
||||
header.style.paddingBottom = 10f;
|
||||
header.style.borderBottomWidth = 1f;
|
||||
header.style.borderBottomColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
|
||||
using (new EditorGUI.DisabledScope(IsBusy))
|
||||
var title = new Label("ShrinkSDK 包管理");
|
||||
title.style.fontSize = 20f;
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
header.Add(title);
|
||||
|
||||
var description = new Label("以 Context 为组合基础,按用途安装固定版本的模块与集成包。");
|
||||
description.style.marginTop = 4f;
|
||||
description.style.whiteSpace = WhiteSpace.Normal;
|
||||
header.Add(description);
|
||||
|
||||
var registryRow = new VisualElement();
|
||||
registryRow.style.flexDirection = FlexDirection.Row;
|
||||
registryRow.style.alignItems = Align.Center;
|
||||
registryRow.style.marginTop = 10f;
|
||||
registryRow.Add(new Label("软件源:"));
|
||||
|
||||
_registryState = new Label();
|
||||
_registryState.style.flexGrow = 1f;
|
||||
_registryState.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
registryRow.Add(_registryState);
|
||||
|
||||
var configure = new Button(ConfigureRegistry) { text = "配置软件源" };
|
||||
configure.style.width = 110f;
|
||||
registryRow.Add(configure);
|
||||
header.Add(registryRow);
|
||||
|
||||
RefreshRegistryState();
|
||||
return header;
|
||||
}
|
||||
|
||||
private VisualElement BuildToolbar()
|
||||
{
|
||||
var toolbar = new Toolbar();
|
||||
_bundleTab = new ToolbarToggle { text = "推荐组合", value = true };
|
||||
_packageTab = new ToolbarToggle { text = "全部包" };
|
||||
_bundleTab.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (GUILayout.Button("Configure Registry"))
|
||||
{
|
||||
ConfigureRegistry();
|
||||
}
|
||||
if (!evt.newValue)
|
||||
return;
|
||||
_showBundles = true;
|
||||
_packageTab?.SetValueWithoutNotify(false);
|
||||
RenderPage();
|
||||
});
|
||||
_packageTab.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (!evt.newValue)
|
||||
return;
|
||||
_showBundles = false;
|
||||
_bundleTab?.SetValueWithoutNotify(false);
|
||||
RenderPage();
|
||||
});
|
||||
toolbar.Add(_bundleTab);
|
||||
toolbar.Add(_packageTab);
|
||||
toolbar.Add(new ToolbarSpacer { flex = true });
|
||||
|
||||
EditorGUILayout.Space(8f);
|
||||
if (GUILayout.Button("Install Starter Basic"))
|
||||
{
|
||||
BeginInstall(ShrinkSdkPackageCatalog.StarterBasic);
|
||||
}
|
||||
|
||||
var labels = ShrinkSdkPackageCatalog.Packages.Select(entry =>
|
||||
entry.DisplayName + " " + entry.Version).ToArray();
|
||||
_selectedPackageIndex = EditorGUILayout.Popup("Package", _selectedPackageIndex, labels);
|
||||
if (GUILayout.Button("Install Selected Package"))
|
||||
{
|
||||
BeginInstall(ShrinkSdkPackageCatalog.Packages[_selectedPackageIndex]);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Refresh Diagnostics"))
|
||||
{
|
||||
RefreshDiagnostics();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(8f);
|
||||
EditorGUILayout.HelpBox(_status, _statusType);
|
||||
var search = new ToolbarSearchField();
|
||||
search.style.width = 230f;
|
||||
search.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
_searchText = evt.newValue?.Trim() ?? string.Empty;
|
||||
RenderPage();
|
||||
});
|
||||
toolbar.Add(search);
|
||||
toolbar.Add(new ToolbarButton(RefreshPackages) { text = "刷新状态" });
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
private bool IsBusy => _listRequest != null;
|
||||
|
||||
private void Update()
|
||||
private void RenderPage()
|
||||
{
|
||||
if (_listRequest != null && _listRequest.IsCompleted)
|
||||
if (_page == null)
|
||||
return;
|
||||
_page.Clear();
|
||||
if (_showBundles)
|
||||
RenderBundles(_page);
|
||||
else
|
||||
RenderPackages(_page);
|
||||
}
|
||||
|
||||
private void RenderBundles(VisualElement parent)
|
||||
{
|
||||
foreach (var bundle in ShrinkSdkPackageCatalog.Bundles.Where(MatchesSearch))
|
||||
{
|
||||
var request = _listRequest;
|
||||
_listRequest = null;
|
||||
if (request.Status == StatusCode.Success)
|
||||
var card = CreateCard();
|
||||
var titleRow = new VisualElement();
|
||||
titleRow.style.flexDirection = FlexDirection.Row;
|
||||
titleRow.style.alignItems = Align.Center;
|
||||
var title = new Label(bundle.DisplayName + (bundle.Recommended ? " · 推荐" : string.Empty));
|
||||
title.style.fontSize = 15f;
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
title.style.flexGrow = 1f;
|
||||
titleRow.Add(title);
|
||||
|
||||
var exact = bundle.Packages.Count(IsExactInstalled);
|
||||
var button = new Button(() => BeginInstall(bundle.InstallRoots));
|
||||
if (exact == bundle.Packages.Count)
|
||||
{
|
||||
var installed = request.Result.Count(package => package.name.StartsWith("com.cneicy.", StringComparison.Ordinal));
|
||||
SetStatus("Registry is configured. Installed ShrinkSDK packages: " + installed, MessageType.Info);
|
||||
button.text = "已完成";
|
||||
button.SetEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorMessage = request.Error?.message;
|
||||
SetStatus(
|
||||
string.IsNullOrWhiteSpace(errorMessage)
|
||||
? "Package diagnostics did not complete while Unity was resolving packages. Refresh diagnostics after resolution completes."
|
||||
: "Package diagnostics failed: " + errorMessage,
|
||||
MessageType.Warning);
|
||||
button.text = exact > 0 ? "补齐组合" : "安装组合";
|
||||
button.SetEnabled(!IsBusy);
|
||||
}
|
||||
button.style.width = 100f;
|
||||
titleRow.Add(button);
|
||||
card.Add(titleRow);
|
||||
|
||||
var summary = new Label(bundle.Summary);
|
||||
summary.style.whiteSpace = WhiteSpace.Normal;
|
||||
summary.style.marginTop = 5f;
|
||||
card.Add(summary);
|
||||
|
||||
var progress = new Label($"已就绪 {exact}/{bundle.Packages.Count}");
|
||||
progress.style.marginTop = 8f;
|
||||
progress.style.color = exact == bundle.Packages.Count
|
||||
? new Color(0.35f, 0.75f, 0.45f)
|
||||
: new Color(0.65f, 0.65f, 0.65f);
|
||||
card.Add(progress);
|
||||
|
||||
var contents = new Foldout { text = "包含的包", value = false };
|
||||
contents.style.marginTop = 4f;
|
||||
foreach (var package in bundle.Packages)
|
||||
contents.Add(CreateCompactPackageRow(package));
|
||||
card.Add(contents);
|
||||
parent.Add(card);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderPackages(VisualElement parent)
|
||||
{
|
||||
var packages = ShrinkSdkPackageCatalog.Packages.Where(MatchesSearch).ToArray();
|
||||
foreach (var group in packages.GroupBy(entry => entry.Layer))
|
||||
{
|
||||
var heading = new Label(GetLayerName(group.Key));
|
||||
heading.style.fontSize = 15f;
|
||||
heading.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
heading.style.marginTop = 8f;
|
||||
heading.style.marginBottom = 4f;
|
||||
parent.Add(heading);
|
||||
foreach (var package in group)
|
||||
parent.Add(CreatePackageCard(package));
|
||||
}
|
||||
}
|
||||
|
||||
private VisualElement CreatePackageCard(ShrinkSdkPackageEntry package)
|
||||
{
|
||||
var card = CreateCard();
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.alignItems = Align.Center;
|
||||
var text = new VisualElement();
|
||||
text.style.flexGrow = 1f;
|
||||
var title = new Label(package.DisplayName);
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
text.Add(title);
|
||||
var id = new Label(package.PackageName + " · 固定版本 " + package.Version);
|
||||
id.style.fontSize = 10f;
|
||||
id.style.color = new Color(0.58f, 0.58f, 0.58f);
|
||||
id.style.marginTop = 2f;
|
||||
text.Add(id);
|
||||
var summary = new Label(package.Summary);
|
||||
summary.style.whiteSpace = WhiteSpace.Normal;
|
||||
summary.style.marginTop = 4f;
|
||||
text.Add(summary);
|
||||
row.Add(text);
|
||||
|
||||
var action = CreatePackageAction(package);
|
||||
action.style.width = 118f;
|
||||
action.style.marginLeft = 12f;
|
||||
row.Add(action);
|
||||
card.Add(row);
|
||||
return card;
|
||||
}
|
||||
|
||||
private VisualElement CreateCompactPackageRow(ShrinkSdkPackageEntry package)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.alignItems = Align.Center;
|
||||
row.style.marginTop = 3f;
|
||||
var name = new Label(package.DisplayName);
|
||||
name.style.flexGrow = 1f;
|
||||
row.Add(name);
|
||||
var status = new Label(GetInstalledStatus(package));
|
||||
status.style.fontSize = 10f;
|
||||
status.style.color = IsExactInstalled(package)
|
||||
? new Color(0.35f, 0.75f, 0.45f)
|
||||
: new Color(0.65f, 0.65f, 0.65f);
|
||||
row.Add(status);
|
||||
return row;
|
||||
}
|
||||
|
||||
private Button CreatePackageAction(ShrinkSdkPackageEntry package)
|
||||
{
|
||||
var button = new Button(() => BeginInstall(new[] { package }));
|
||||
if (!_installed.TryGetValue(package.PackageName, out var installed))
|
||||
button.text = "安装 " + package.Version;
|
||||
else if (string.Equals(installed.version, package.Version, StringComparison.Ordinal))
|
||||
{
|
||||
button.text = "已安装 " + installed.version;
|
||||
button.SetEnabled(false);
|
||||
}
|
||||
else
|
||||
button.text = installed.version + " → " + package.Version;
|
||||
|
||||
if (IsBusy)
|
||||
button.SetEnabled(false);
|
||||
return button;
|
||||
}
|
||||
|
||||
private static VisualElement CreateCard()
|
||||
{
|
||||
var card = new VisualElement();
|
||||
card.style.marginBottom = 8f;
|
||||
card.style.paddingLeft = 12f;
|
||||
card.style.paddingRight = 12f;
|
||||
card.style.paddingTop = 10f;
|
||||
card.style.paddingBottom = 10f;
|
||||
card.style.borderLeftWidth = 1f;
|
||||
card.style.borderRightWidth = 1f;
|
||||
card.style.borderTopWidth = 1f;
|
||||
card.style.borderBottomWidth = 1f;
|
||||
card.style.borderLeftColor = new Color(0.28f, 0.28f, 0.28f);
|
||||
card.style.borderRightColor = new Color(0.28f, 0.28f, 0.28f);
|
||||
card.style.borderTopColor = new Color(0.28f, 0.28f, 0.28f);
|
||||
card.style.borderBottomColor = new Color(0.28f, 0.28f, 0.28f);
|
||||
card.style.borderTopLeftRadius = 4f;
|
||||
card.style.borderTopRightRadius = 4f;
|
||||
card.style.borderBottomLeftRadius = 4f;
|
||||
card.style.borderBottomRightRadius = 4f;
|
||||
return card;
|
||||
}
|
||||
|
||||
private bool MatchesSearch(ShrinkSdkPackageBundle bundle) =>
|
||||
string.IsNullOrEmpty(_searchText) ||
|
||||
bundle.DisplayName.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
bundle.Summary.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
bundle.Packages.Any(MatchesSearch);
|
||||
|
||||
private bool MatchesSearch(ShrinkSdkPackageEntry package) =>
|
||||
string.IsNullOrEmpty(_searchText) ||
|
||||
package.DisplayName.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
package.Summary.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
package.PackageName.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
|
||||
private bool IsExactInstalled(ShrinkSdkPackageEntry package) =>
|
||||
_installed.TryGetValue(package.PackageName, out var installed) &&
|
||||
string.Equals(installed.version, package.Version, StringComparison.Ordinal);
|
||||
|
||||
private string GetInstalledStatus(ShrinkSdkPackageEntry package)
|
||||
{
|
||||
if (!_installed.TryGetValue(package.PackageName, out var installed))
|
||||
return "未安装 · " + package.Version;
|
||||
return string.Equals(installed.version, package.Version, StringComparison.Ordinal)
|
||||
? "已安装 " + installed.version
|
||||
: "已安装 " + installed.version + " · 固定 " + package.Version;
|
||||
}
|
||||
|
||||
private bool IsBusy => _listRequest != null || _resolvePending;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_resolvePending && EditorApplication.timeSinceStartup - _resolveRequestedAt >= 0.5d)
|
||||
{
|
||||
_resolvePending = false;
|
||||
SetStatus("正在等待依赖解析并刷新安装状态。", HelpBoxMessageType.Info);
|
||||
StartListRequest();
|
||||
}
|
||||
|
||||
if (_listRequest == null || !_listRequest.IsCompleted)
|
||||
return;
|
||||
|
||||
var list = _listRequest;
|
||||
_listRequest = null;
|
||||
if (list.Status == StatusCode.Success)
|
||||
{
|
||||
_installed.Clear();
|
||||
foreach (var package in list.Result)
|
||||
_installed[package.name] = package;
|
||||
var count = _installed.Keys.Count(name => name.StartsWith("com.cneicy.", StringComparison.Ordinal));
|
||||
SetStatus("状态已刷新:当前项目共解析到 " + count + " 个 ShrinkSDK 包。", HelpBoxMessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStatus("读取安装状态失败:" + GetRequestError(list.Error), HelpBoxMessageType.Warning);
|
||||
}
|
||||
RenderPage();
|
||||
}
|
||||
|
||||
private void ConfigureRegistry()
|
||||
{
|
||||
if (IsBusy)
|
||||
return;
|
||||
try
|
||||
{
|
||||
ShrinkSdkManifestStore.EnsureShrinkSdkRegistry();
|
||||
_listRequest = null;
|
||||
Client.Resolve();
|
||||
SetStatus("Registry configuration written. Unity is resolving packages.", MessageType.Info);
|
||||
RefreshRegistryState();
|
||||
BeginResolve("软件源已写入,Unity 正在解析依赖。");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
SetStatus(exception.Message, MessageType.Error);
|
||||
SetStatus(exception.Message, HelpBoxMessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginInstall(ShrinkSdkPackageEntry entry)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShrinkSdkManifestStore.EnsurePackageInstallation(entry);
|
||||
_listRequest = null;
|
||||
Client.Resolve();
|
||||
SetStatus(
|
||||
"Pinned " + entry.PackageName + " " + entry.Version + " in Packages/manifest.json. Unity is resolving packages.",
|
||||
MessageType.Info);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
SetStatus(exception.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshDiagnostics()
|
||||
private void BeginInstall(IEnumerable<ShrinkSdkPackageEntry> packages)
|
||||
{
|
||||
if (IsBusy)
|
||||
{
|
||||
return;
|
||||
try
|
||||
{
|
||||
var roots = ShrinkSdkPackageCatalog.DistinctInstallRoots(packages);
|
||||
ShrinkSdkManifestStore.EnsurePackageInstallation(roots);
|
||||
RefreshRegistryState();
|
||||
BeginResolve("已写入 " + roots.Count + " 个固定版本入口,Unity 正在安装并解析依赖。");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
SetStatus(exception.Message, HelpBoxMessageType.Error);
|
||||
}
|
||||
|
||||
_listRequest = Client.List(true);
|
||||
SetStatus("Refreshing installed package diagnostics.", MessageType.Info);
|
||||
}
|
||||
|
||||
private void SetStatus(string message, MessageType messageType)
|
||||
private void BeginResolve(string status)
|
||||
{
|
||||
_status = message;
|
||||
_statusType = messageType;
|
||||
Repaint();
|
||||
_listRequest = null;
|
||||
Client.Resolve();
|
||||
_resolvePending = true;
|
||||
_resolveRequestedAt = EditorApplication.timeSinceStartup;
|
||||
SetStatus(status, HelpBoxMessageType.Info);
|
||||
RenderPage();
|
||||
}
|
||||
|
||||
private void RefreshPackages()
|
||||
{
|
||||
if (IsBusy)
|
||||
return;
|
||||
StartListRequest();
|
||||
SetStatus("正在读取当前项目的直接与间接依赖。", HelpBoxMessageType.Info);
|
||||
RenderPage();
|
||||
}
|
||||
|
||||
private void StartListRequest()
|
||||
{
|
||||
if (_listRequest == null)
|
||||
_listRequest = Client.List(true, true);
|
||||
}
|
||||
|
||||
private void RefreshRegistryState()
|
||||
{
|
||||
if (_registryState == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
var configured = ShrinkSdkManifestStore.HasShrinkSdkRegistry();
|
||||
_registryState.text = configured ? "已配置 · " + ShrinkSdkPackageCatalog.RegistryUrl : "未配置";
|
||||
_registryState.style.color = configured
|
||||
? new Color(0.35f, 0.75f, 0.45f)
|
||||
: new Color(0.85f, 0.55f, 0.25f);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_registryState.text = "无法读取 Packages/manifest.json";
|
||||
_registryState.style.color = new Color(0.85f, 0.35f, 0.3f);
|
||||
SetStatus(exception.Message, HelpBoxMessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetStatus(string message, HelpBoxMessageType type)
|
||||
{
|
||||
if (_statusBox == null)
|
||||
return;
|
||||
_statusBox.text = message;
|
||||
_statusBox.messageType = type;
|
||||
}
|
||||
|
||||
private static string GetRequestError(Error? error)
|
||||
{
|
||||
var message = error?.message;
|
||||
return string.IsNullOrWhiteSpace(message)
|
||||
? "Unity Package Manager 未返回详细原因,请在依赖解析结束后刷新状态。"
|
||||
: message!;
|
||||
}
|
||||
|
||||
private static string GetLayerName(ShrinkSdkPackageLayer layer) => layer switch
|
||||
{
|
||||
ShrinkSdkPackageLayer.Context => "Context 组合基础",
|
||||
ShrinkSdkPackageLayer.Host => "应用与组合根",
|
||||
ShrinkSdkPackageLayer.Feature => "独立功能模块",
|
||||
ShrinkSdkPackageLayer.Integration => "模块集成",
|
||||
ShrinkSdkPackageLayer.Tooling => "编译与工具基础",
|
||||
_ => layer.ToString()
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user