feat(node): refine console layout and tray status

This commit is contained in:
caoqianming 2026-08-21 11:05:15 +08:00
parent aba93d6d0b
commit efcf06bf24
5 changed files with 29 additions and 28 deletions

View File

@ -10,7 +10,7 @@
- 修复国际旗舰模型输出思考过程时偶尔无法及时停止的问题;即使模型暂时没有返回新的流式片段,点击“停止”也会迅速中断当前回答。 - 修复国际旗舰模型输出思考过程时偶尔无法及时停止的问题;即使模型暂时没有返回新的流式片段,点击“停止”也会迅速中断当前回答。
- Windows Node 主窗口改为简洁的单层自适应布局:移除固定宽度与重复卡片边框,窗口缩放时内容和页签会随可用宽度调整,专业软件的状态与操作也会在窄窗口中自然换行;能力概览按软件归并,不再为同一软件或同名能力重复显示名称。Origin、ANSYS、Blender 均可手工选择位置Origin 自动检测也会在 COM 路径无效时继续查找注册表和标准安装目录。 - Windows Node 主窗口改为简洁的单层自适应布局:顶部只保留产品名与节点状态,移除固定宽度、重复卡片边框和说明性大标题;默认窗口适当增高,缩放时内容和页签会随可用宽度调整,专业软件的状态与操作也会在窄窗口中自然换行。可用软件按名称归并,不再重复显示同一软件或能力;托盘右键菜单中的长状态也会自动截断,不再把菜单横向撑宽。Origin、ANSYS、Blender 均可手工选择位置Origin 自动检测也会在 COM 路径无效时继续查找注册表和标准安装目录。
- Windows Node 从托盘退出时会保护已有任务:可以停止接单并等待任务完成后退出,也可以经二次确认取消任务并终止对应的软件进程,不再直接关闭节点而留下状态不明的任务。 - Windows Node 从托盘退出时会保护已有任务:可以停止接单并等待任务完成后退出,也可以经二次确认取消任务并终止对应的软件进程,不再直接关闭节点而留下状态不明的任务。

View File

@ -24,7 +24,7 @@
- **08-21 / Unreleased / Blender 多视图与剖切 v2**:以 `blender.scene.author@v2` 直接替换 v1目标节点为 Blender 5.2 LTS`rotary_kiln` 一次建模后可连续渲染等轴测、侧视、窑头、窑尾、纵向剖切和横截面,完整工程保持未剖切状态;固定等轴测预览外,其他选定 PNG 可动态自动回传GLB 排除仅供剖切渲染的辅助几何。专业软件提交工具改为区分缺失参数与合法空数组,允许 Blender 这类无输入、无额外导出请求的纯生成任务进入合同校验90 项 Blender/合同/Job/Node/预览专项 unittest、.NET build、独立 adapter 打包和 Blender 3.6.12 兼容烟测通过,全量 698 项仍仅 3 个既有数据库集成模块因显式测试库缺少 `users` 表未通过(另跳过 4 项),目标节点 5.2 真机验收待部署后执行,未连接或迁移生产数据库。 - **08-21 / Unreleased / Blender 多视图与剖切 v2**:以 `blender.scene.author@v2` 直接替换 v1目标节点为 Blender 5.2 LTS`rotary_kiln` 一次建模后可连续渲染等轴测、侧视、窑头、窑尾、纵向剖切和横截面,完整工程保持未剖切状态;固定等轴测预览外,其他选定 PNG 可动态自动回传GLB 排除仅供剖切渲染的辅助几何。专业软件提交工具改为区分缺失参数与合法空数组,允许 Blender 这类无输入、无额外导出请求的纯生成任务进入合同校验90 项 Blender/合同/Job/Node/预览专项 unittest、.NET build、独立 adapter 打包和 Blender 3.6.12 兼容烟测通过,全量 698 项仍仅 3 个既有数据库集成模块因显式测试库缺少 `users` 表未通过(另跳过 4 项),目标节点 5.2 真机验收待部署后执行,未连接或迁移生产数据库。
- **08-21 / Unreleased / Windows Node 软件地址选择、Origin 检测与自适应界面**专业软件保留一层带边界的独立配置行和通栏只读地址框Origin、ANSYS、Blender 均可手选位置。Origin 自动检测由单一 COM 路径扩展为 COM、卸载注册表和标准目录回退手选的 `Origin*.exe` 同时用于 Worker 版本识别COM 执行健康仍由 probe 独立校验。主窗口移除固定宽度居中容器和普通内容卡片边框,改为单层全宽页面;页签使用 WinForms 内置 `FillToRight` 模式跟随窗口调整,避免手工尺寸回调递归触发布局栈溢出;软件状态与操作分行并允许按钮自然换行,能力概览按软件与能力名称归并旧、新 adapter 的重复文案。Windows Node 23 项源码 unittest、.NET build、启动烟测与 diff 检查通过,无 DB、schema、HTTP API、依赖或运行方式变化。 - **08-21 / Unreleased / Windows Node 软件地址选择、Origin 检测与自适应界面**专业软件保留一层带边界的独立配置行和通栏只读地址框Origin、ANSYS、Blender 均可手选位置。Origin 自动检测由单一 COM 路径扩展为 COM、卸载注册表和标准目录回退手选的 `Origin*.exe` 同时用于 Worker 版本识别COM 执行健康仍由 probe 独立校验。主窗口移除固定宽度居中容器、普通内容卡片边框和重复说明标题,顶部压缩为产品名与状态单行,默认高度增至 1050;页签使用 WinForms 内置 `FillToRight` 模式跟随窗口调整,软件状态与操作分行并允许按钮自然换行,可用软件按名称归并旧、新 adapter 的重复文案。托盘右键状态规范为单行并在 24 字符处省略,完整信息继续在主窗口显示,托盘悬浮保留较长摘要。Windows Node 23 项源码 unittest、.NET build、启动烟测与 diff 检查通过,无 DB、schema、HTTP API、依赖或运行方式变化。
- **08-20 / Unreleased / Windows Node 专业软件独立配置与 runtime 安装**:主窗口新增 Origin、ANSYS Mechanical 2024 R2、Blender 软件卡,分别显示应用位置来源和受管 runtime 状态;三种软件均可由 UI 选择自定义位置清除后按各自的环境变量、COM、卸载注册表、PATH 或标准目录自动检测。每个软件可单独创建或更新隔离的 Python 3.12 runtime安装只使用内置 runtime 定义与固定 requirements拒绝活动任务期间替换并先在临时目录完成后再切换配置后的路径由 Host 作为固定子进程环境传给 Worker任务请求仍不能提供路径或命令。移除重复承担软件安装和开机启动的统一 BAT发布包直接运行 EXE当前账号的开机启动也完全由 UI 管理;全新节点默认使用可写的 `%LocalAppData%` 数据根目录,已有 `%ProgramData%` 节点原位兼容。 - **08-20 / Unreleased / Windows Node 专业软件独立配置与 runtime 安装**:主窗口新增 Origin、ANSYS Mechanical 2024 R2、Blender 软件卡,分别显示应用位置来源和受管 runtime 状态;三种软件均可由 UI 选择自定义位置清除后按各自的环境变量、COM、卸载注册表、PATH 或标准目录自动检测。每个软件可单独创建或更新隔离的 Python 3.12 runtime安装只使用内置 runtime 定义与固定 requirements拒绝活动任务期间替换并先在临时目录完成后再切换配置后的路径由 Host 作为固定子进程环境传给 Worker任务请求仍不能提供路径或命令。移除重复承担软件安装和开机启动的统一 BAT发布包直接运行 EXE当前账号的开机启动也完全由 UI 管理;全新节点默认使用可写的 `%LocalAppData%` 数据根目录,已有 `%ProgramData%` 节点原位兼容。

View File

@ -187,7 +187,7 @@ class WindowsNodeSourceTests(unittest.TestCase):
def test_configuration_window_is_resizable_and_dpi_safe(self) -> None: def test_configuration_window_is_resizable_and_dpi_safe(self) -> None:
form = (PROJECT / "ConfigurationForm.cs").read_text(encoding="utf-8") form = (PROJECT / "ConfigurationForm.cs").read_text(encoding="utf-8")
self.assertIn("ClientSize = new Size(1080, 720)", form) self.assertIn("ClientSize = new Size(1080, 1050)", form)
self.assertIn("MinimumSize = new Size(760, 560)", form) self.assertIn("MinimumSize = new Size(760, 560)", form)
self.assertIn("FormBorderStyle.Sizable", form) self.assertIn("FormBorderStyle.Sizable", form)
self.assertIn("AutoScaleMode.Dpi", form) self.assertIn("AutoScaleMode.Dpi", form)
@ -210,7 +210,10 @@ class WindowsNodeSourceTests(unittest.TestCase):
self.assertIn("Appearance = TabAppearance.FlatButtons", form) self.assertIn("Appearance = TabAppearance.FlatButtons", form)
self.assertNotIn("protected override void OnResize", form) self.assertNotIn("protected override void OnResize", form)
self.assertIn("tab.Controls.Add(content)", form) self.assertIn("tab.Controls.Add(content)", form)
self.assertIn("CreateSubsectionTitle(\"能力概览\")", form) self.assertIn("CreateSectionTitle(\"节点信息\")", form)
self.assertIn("CreateSubsectionTitle(\"可用软件\")", form)
self.assertIn("heading.Controls.Add(state, 1, 0)", form)
self.assertNotIn("headingHint", form)
self.assertIn("panel.SetColumnSpan(path, 2)", form) self.assertIn("panel.SetColumnSpan(path, 2)", form)
self.assertIn("panel.SetColumnSpan(status, 2)", form) self.assertIn("panel.SetColumnSpan(status, 2)", form)
self.assertIn("panel.SetColumnSpan(actions, 2)", form) self.assertIn("panel.SetColumnSpan(actions, 2)", form)
@ -235,6 +238,8 @@ class WindowsNodeSourceTests(unittest.TestCase):
tray = (PROJECT / "TrayApplicationContext.cs").read_text(encoding="utf-8") tray = (PROJECT / "TrayApplicationContext.cs").read_text(encoding="utf-8")
self.assertIn("form.ReconnectRequested += RestartConnection", tray) self.assertIn("form.ReconnectRequested += RestartConnection", tray)
self.assertIn("statusItem.Text = CompactMenuText(status.Message, 24)", tray)
self.assertIn('singleLine[..(length - 1)] + ""', tray)
def test_tray_exit_waits_for_or_cancels_active_jobs(self) -> None: def test_tray_exit_waits_for_or_cancels_active_jobs(self) -> None:
tray = (PROJECT / "TrayApplicationContext.cs").read_text(encoding="utf-8") tray = (PROJECT / "TrayApplicationContext.cs").read_text(encoding="utf-8")

View File

@ -74,7 +74,7 @@ internal sealed class ConfigurationForm : Form
adapters = NodeAdapterRegistry.CreateDefault(jobInbox); adapters = NodeAdapterRegistry.CreateDefault(jobInbox);
Text = "zcbot Windows Node"; Text = "zcbot Windows Node";
AutoScaleMode = AutoScaleMode.Dpi; AutoScaleMode = AutoScaleMode.Dpi;
ClientSize = new Size(1080, 720); ClientSize = new Size(1080, 1050);
MinimumSize = new Size(760, 560); MinimumSize = new Size(760, 560);
StartPosition = FormStartPosition.CenterScreen; StartPosition = FormStartPosition.CenterScreen;
Font = new Font("Microsoft YaHei UI", 9); Font = new Font("Microsoft YaHei UI", 9);
@ -109,19 +109,20 @@ internal sealed class ConfigurationForm : Form
{ {
AutoSize = true, AutoSize = true,
Dock = DockStyle.Top, Dock = DockStyle.Top,
ColumnCount = 1, ColumnCount = 2,
Margin = new Padding(2, 0, 2, 14), Margin = new Padding(2, 0, 2, 14),
}; };
heading.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
heading.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
heading.Controls.Add(new Label heading.Controls.Add(new Label
{ {
Text = "zcbot Windows Node", Text = "zcbot Windows Node",
AutoSize = true, AutoSize = true,
Font = new Font("Microsoft YaHei UI", 18, FontStyle.Bold), Anchor = AnchorStyles.Left,
Font = new Font("Microsoft YaHei UI", 16, FontStyle.Bold),
ForeColor = Color.FromArgb(15, 23, 42), ForeColor = Color.FromArgb(15, 23, 42),
}); }, 0, 0);
var headingHint = CreateHint("连接本机科研软件与 zcbot安全执行受控任务"); heading.Controls.Add(state, 1, 0);
headingHint.Font = new Font("Microsoft YaHei UI", 10);
heading.Controls.Add(headingHint);
page.Controls.Add(heading); page.Controls.Add(heading);
var tabs = new NavigationTabControl var tabs = new NavigationTabControl
@ -139,22 +140,11 @@ internal sealed class ConfigurationForm : Form
page.Controls.Add(tabs, 0, 1); page.Controls.Add(tabs, 0, 1);
var statusCard = CreateSection(); var statusCard = CreateSection();
var statusHeader = new TableLayoutPanel statusCard.Controls.Add(CreateSectionTitle("节点信息"));
{
AutoSize = true,
Dock = DockStyle.Top,
ColumnCount = 2,
Margin = new Padding(0, 0, 0, 8),
};
statusHeader.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
statusHeader.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
statusHeader.Controls.Add(CreateSectionTitle("节点状态"), 0, 0);
statusHeader.Controls.Add(state, 1, 0);
statusCard.Controls.Add(statusHeader);
statusCard.Controls.Add(detail); statusCard.Controls.Add(detail);
statusCard.Controls.Add(identity); statusCard.Controls.Add(identity);
statusCard.Controls.Add(CreateDivider()); statusCard.Controls.Add(CreateDivider());
statusCard.Controls.Add(CreateSubsectionTitle("能力概览")); statusCard.Controls.Add(CreateSubsectionTitle("可用软件"));
statusCard.Controls.Add(capabilitySummary); statusCard.Controls.Add(capabilitySummary);
var resetActions = CreateActions(); var resetActions = CreateActions();
resetActions.Controls.Add(reconnect); resetActions.Controls.Add(reconnect);
@ -1356,9 +1346,7 @@ internal sealed class ConfigurationForm : Form
{ {
var selected = eventArgs.Index == SelectedIndex; var selected = eventArgs.Index == SelectedIndex;
var bounds = eventArgs.Bounds; var bounds = eventArgs.Bounds;
using var background = new SolidBrush(selected using var background = new SolidBrush(Color.White);
? Color.White
: Color.FromArgb(241, 245, 249));
eventArgs.Graphics.FillRectangle(background, bounds); eventArgs.Graphics.FillRectangle(background, bounds);
TextRenderer.DrawText( TextRenderer.DrawText(
eventArgs.Graphics, eventArgs.Graphics,

View File

@ -201,7 +201,7 @@ internal sealed class TrayApplicationContext : ApplicationContext
{ {
current = status; current = status;
form.ApplyStatus(status, config); form.ApplyStatus(status, config);
statusItem.Text = status.Message; statusItem.Text = CompactMenuText(status.Message, 24);
tray.Text = Limit($"zcbot Windows Node - {status.Message}", 63); tray.Text = Limit($"zcbot Windows Node - {status.Message}", 63);
var oldIcon = tray.Icon; var oldIcon = tray.Icon;
tray.Icon = TrayIconFactory.Create(status.State); tray.Icon = TrayIconFactory.Create(status.State);
@ -279,4 +279,12 @@ internal sealed class TrayApplicationContext : ApplicationContext
private static string Limit(string value, int length) => private static string Limit(string value, int length) =>
value.Length <= length ? value : value[..length]; value.Length <= length ? value : value[..length];
private static string CompactMenuText(string value, int length)
{
var singleLine = value.Replace('\r', ' ').Replace('\n', ' ').Trim();
return singleLine.Length <= length
? singleLine
: singleLine[..(length - 1)] + "…";
}
} }