feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
CI / Go Backend (push) Canceled after 0s

初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
xyou
2026-08-08 14:07:56 +08:00
parent 9500c4c80a
commit f70b061d1a
1972 changed files with 159760 additions and 6 deletions
@@ -0,0 +1,104 @@
using System.Windows;
using PCL.Core.UI;
namespace PCL;
public static class HintService
{
private struct HintMessage
{
public string Text;
public HintType Type;
public bool Log;
}
private static ModBase.SafeList<HintMessage> HintWaiting
{
get => field ??= new ModBase.SafeList<HintMessage>();
set;
}
public static void Hint(string? text, HintType type = HintType.Info, bool log = true)
{
var normalized = (text ?? "").Replace("\r\n", " ").Replace("\r", " ").Replace("\n", " ");
if (HintWaiting.Any(h => h.Text == normalized && h.Type == type)) return;
HintWaiting.Add(new HintMessage { Text = normalized, Type = type, Log = log });
}
public static void HintWrapper_OnShow(string message, HintTheme messageTheme)
{
var hintType = messageTheme switch
{
HintTheme.Success => HintType.Success,
HintTheme.Error => HintType.Error,
HintTheme.Warning => HintType.Warning,
_ => HintType.Info
};
Hint(message, hintType);
}
internal static void Tick()
{
try
{
ModMain.frmMain!.PanHint.HorizontalAlignment = HorizontalAlignment.Right;
ModMain.frmMain.PanHint.VerticalAlignment = VerticalAlignment.Bottom;
var extraHeight = ModMain.frmMain.PanExtraButtons.ActualHeight;
ModMain.frmMain.PanHint.Margin = new Thickness(0, 0, 0, extraHeight > 0 ? extraHeight + 20 : 20);
if (!HintWaiting.Any())
return;
var currentHint = HintWaiting[0];
var duplicate = ModMain.frmMain.PanHint.Children.OfType<MyToast>()
.FirstOrDefault(t => !t.IsDismissing && t.Context == currentHint.Text && t.ToastType == currentHint.Type);
if (duplicate != null)
{
duplicate.Emphasize();
HintWaiting.RemoveAt(0);
return;
}
var activeCount = ModMain.frmMain.PanHint.Children.OfType<MyToast>().Count(t => !t.IsDismissing);
if (activeCount >= 5)
{
var oldest = ModMain.frmMain.PanHint.Children.OfType<MyToast>().FirstOrDefault(t => !t.IsDismissing);
oldest?.Dismiss();
return;
}
var toast = new MyToast
{
Context = currentHint.Text,
ToastType = currentHint.Type,
Icon = currentHint.Type switch
{
HintType.Success => "lucide/circle-check",
HintType.Error => "lucide/circle-minus",
HintType.Warning => "lucide/triangle-alert",
_ => "lucide/info"
},
DisplayDuration = (800d + ModBase.MathClamp(currentHint.Text.Length, 5d, 23d) * 180d) * ModAnimation.aniSpeed
};
ModMain.frmMain.PanHint.Children.Add(toast);
toast.Show();
if (currentHint.Log)
ModBase.Log("[UI] 弹出提示:" + currentHint.Text);
HintWaiting.RemoveAt(0);
}
catch (Exception ex)
{
ModBase.Log(ex, "显示弹出提示失败", ModBase.LogLevel.Normal);
}
}
public static void HideAll()
{
foreach (MyToast toast in ModMain.frmMain!.PanHint.Children.OfType<MyToast>().ToList())
toast.Dismiss();
}
}
@@ -0,0 +1,9 @@
namespace PCL;
public enum HintType
{
Info,
Success,
Error,
Warning
}
@@ -0,0 +1,132 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using PCL.Core.App;
using PCL.Core.UI.Theme;
namespace PCL;
public static class ThemeManager
{
private static bool _contextMenuHandlerRegistered;
public static bool IsDarkMode => ThemeService.IsDarkMode;
public static ResourceDictionary AppResources => System.Windows.Application.Current.Resources;
public static ModBase.MyColor colorGray1 = new(AppResources["ColorObjectGray1"]);
public static ModBase.MyColor colorGray4 = new(AppResources["ColorObjectGray4"]);
public static ModBase.MyColor colorGray5 = new(AppResources["ColorObjectGray5"]);
public static ModBase.MyColor colorSemiTransparent = new(AppResources["ColorBrushSemiTransparent"]);
public static void ThemeRefresh(int newTheme = -1)
{
colorGray1 = new ModBase.MyColor(AppResources["ColorObjectGray1"]);
colorGray4 = new ModBase.MyColor(AppResources["ColorObjectGray4"]);
colorGray5 = new ModBase.MyColor(AppResources["ColorObjectGray5"]);
colorSemiTransparent = new ModBase.MyColor(AppResources["ColorBrushSemiTransparent"]);
ThemeRefreshMain();
}
public static void ThemeRefreshMain()
{
ModBase.RunInUi(() =>
{
if (!ModMain.frmMain.IsLoaded) return;
RefreshBackground();
RefreshAllContextMenuThemes();
});
}
// 主页面背景
private static void RefreshBackground()
{
if (Config.Preference.Background.BackgroundColorful)
{
var brush = new LinearGradientBrush
{
EndPoint = new Point(0.1, 1),
StartPoint = new Point(0.9, 0)
};
var hue = ThemeService.GetCurrentThemeArgs().Hue;
var hue1 = hue - 15;
var hue2 = hue + 15;
var tone = ThemeService.CurrentTone;
var darkLight = IsDarkMode ? 0.2d : 1d;
brush.GradientStops.Add(new GradientStop
{ Offset = -0.1d, Color = LabColor.FromLch(0.84d * darkLight, tone.C5, hue1) });
brush.GradientStops.Add(new GradientStop
{ Offset = 0.4d, Color = LabColor.FromLch(0.96d * darkLight, tone.C7, hue) });
brush.GradientStops.Add(new GradientStop
{ Offset = 1.1d, Color = LabColor.FromLch(0.84d * darkLight, tone.C5, hue2) });
ModMain.frmMain.PanForm.Background = brush;
}
else
{
ModMain.frmMain.PanForm.Background = (Brush)System.Windows.Application.Current.Resources["ColorBrushBackground"];
}
ModMain.frmMain.PanForm.Background.Freeze();
}
// 通用ContextMenu主题刷新
private static void RefreshAllContextMenuThemes()
{
try
{
if (!_contextMenuHandlerRegistered)
{
EventManager.RegisterClassHandler(typeof(ContextMenu), ContextMenu.OpenedEvent,
new RoutedEventHandler(OnContextMenuOpened));
_contextMenuHandlerRegistered = true;
}
foreach (Window window in System.Windows.Application.Current.Windows)
RefreshContextMenusInElement(window);
}
catch (Exception ex)
{
ModBase.Log(ex, "刷新ContextMenu主题时出错");
}
}
private static void OnContextMenuOpened(object sender, RoutedEventArgs e)
{
try
{
if (sender is ContextMenu contextMenu)
{
contextMenu.ClearValue(FrameworkElement.StyleProperty);
contextMenu.UpdateDefaultStyle();
}
}
catch
{
// 忽略个别错误
}
}
private static void RefreshContextMenusInElement(DependencyObject element)
{
if (element is null)
return;
try
{
if (element is FrameworkElement { ContextMenu: not null } fe)
{
fe.ContextMenu.ClearValue(FrameworkElement.StyleProperty);
fe.ContextMenu.UpdateDefaultStyle();
}
var childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childrenCount; i++)
RefreshContextMenusInElement(VisualTreeHelper.GetChild(element, i));
}
catch
{
// 忽略个别元素的错误,继续处理其他元素
}
}
}