Skip to content

[Pull Request] WorkflowV6: v6 蓝图编辑器 + 前后端分离适配 - #40

Open
StardustSeemsInk wants to merge 158 commits into
dev=mainfrom
dev=v6-grammar
Open

[Pull Request] WorkflowV6: v6 蓝图编辑器 + 前后端分离适配#40
StardustSeemsInk wants to merge 158 commits into
dev=mainfrom
dev=v6-grammar

Conversation

@StardustSeemsInk

@StardustSeemsInk StardustSeemsInk commented Aug 3, 2026

Copy link
Copy Markdown
Member

What does this PR do?

将 dev=v6-grammar 分支(Dashboard v6 蓝图编辑器 + 前后端分离适配)合并向 dev=main。

前后端分离适配(dev=UI-separate-SI)

  • 业务逻辑迁入 KitX.Core / KitX.Core.Contract,Dashboard 仅保留 UI 层;DI 容器重构(ServiceHost 统一服务获取、移除 Instances 静态类)
  • 删除遗留 KitX.Dashboard.Core 项目;VM 构造函数可测化(ILens 接口 + 去服务定位),建立 Dashboard 测试项目(P0/P1 测试)

v6 蓝图编辑器主体(dev=v6-grammar)

  • 双模式编辑器:KS 编辑器(Trigger/HelperFunc/常量面板/元信息/保存,完整复刻 v5.1 功能)+ BP 画布(只读渲染 → Connect/强约束预检 → Palette 工具箱/NodeFactory → 节点删除/Reverse 反向同步)
  • 共享 IR 管线:BuildIrForCurrentMode(KS: ParseLowering / BP: ReverseWithNodePaths),Run/DebugRun/Save 三处复用
  • 调试:RealBlueprintDebugger 状态机(Step/Continue/Pause)、节点高亮 + wire tooltip + 变量监视面板、常量持久化
  • Trigger 复苏:KS↔BP round-trip + Plugin 调色板;ITriggerManager 重建 + WorkflowSessionManager v6 run-by-id
  • DictNew 定义节点前端(键值对编辑区 + 配对引脚组扩展);嵌套节点 Embedded/SubEditor 模式 + 展开模式设置项

断点与调试

  • 断点跨重投影迁移:DebugRunAsync reload 前快照断点,按 Reverse 节点映射迁移(palette 随机 ID 归一化为 FNV),断点不再随重投影消失、Continue 精确命中
  • 右键 Toggle Breakpoint 实时同步调试器(会话内新增断点立即生效)
  • 修复调试单步失效:Continue 后 StepOver 退化为自由运行(StepNext 重新武装断点停检)

BP 节点位置持久化(T5)

  • 保存端写 BlueprintLayout 信封字段(canonical 节点 ID 键);加载端查表覆写;拖动节点置 dirty
  • KS↔BP 切换前自动保存 + 自动保存循环化

连线交互

  • 恢复 DisconnectConnectorCommand 绑定(端点 Alt+点击断连回归修复)
  • 连线 Alt+点击断连首次生效;右键连线"断开连线"菜单(替代被占位弹层覆盖的 ContextMenu)

Related issues

#289 #291

StardustSeemsInk and others added 30 commits August 3, 2025 22:05
- ConstantTable 引用 KitX.Core.ConstantTable 获取端口信息
- AppViewModel 订阅 PluginDisconnected 事件,从 UIStateService.PluginInfos 移除断开插件
- 使用 PluginInfo.Name 属性比较替代 Equals() 引用比较
- PluginBarViewModel 和 RepoPageViewModel 响应插件状态变化
- WorkflowScriptEditorWindowViewModel 传递已连接插件列表给脚本引擎
Fix: normalize Csharpell reference path casing for Linux
- 新增 VariableNode 节点 UI(调色板按钮、类型选择器模板,Teal 主题色)
- 实现 ResolveVariablePinType 动态类型解析,ConstNode/VariableNode 类型变更自动传播到 Get/Set 端口
- BlueprintConnectionVM 订阅源端口 PinType 变化,连线颜色实时更新
- 加载蓝图时批量解析 Get/Set 端口类型并初始化 Const 输出端口颜色
- CallHelperNode 返回类型推断,Get/Set VarName 变更触发重新解析
- ExportDrawingToBlueprint 从当前 VM 重建 ConstValues
- WorkflowScriptEditorWindowViewModel 构建 constantOverrides 同步用户编辑值到执行
- BlueprintEditorViewModel: Branch 节点输出引脚按 descriptor.RelativeY
  排序(True在上/False在下), 替代字母序排序
- WorkflowScriptEditorWindow: TextChanged 添加 500ms 防抖, 减少频繁重解析
ViewModel层:
- 新增 PluginFunctionPaletteItem / HelperFunctionPaletteItem 模型类
- BlueprintEditorViewModel 新增 PluginFunctions / HelperFunctions 集合
- 通过 IPluginServer.Connections (DI) 获取已连接插件函数列表
- 通过 WorkflowEditorBridge 获取当前脚本 Helper 函数列表
- 订阅 IPluginService.PluginStatusChanged 事件自动刷新
- 新增 AddPluginCallNode / AddHelperCallNode 命令, 带参数引脚和类型标识
- SetBridge 时自动刷新 Helper 函数列表
- Cleanup() 取消事件订阅, OnClosed 时调用

AXAML层:
- Actions Expander 中静态按钮替换为动态 ItemsControl
- Plugin Functions / Helper Functions 子 Expander (折叠)
- 无数据时显示兜底静态按钮
- 修复 Helper 节点重复 Return 引脚 (跳过 descriptor 模板的 Return)
…赖包,迁移 FAColorPicker 至原生 ColorView(FAColorPickler已被FAUI库弃用),移除未使用的Microsoft.Extensions.DependencyInjection
- FindOutputConnector 返回首个非 Exec 输出(Exec pin 在 Output 头部,
  原实现把 w:{nodeId} 运行时值挂到 Exec 端口,数据端口 tooltip 恒空)
- DebugRun 启动预填 RuntimeVariables(const/var 声明 + 覆盖值)——无赋值语句的工作流变量栏不再为空
- StepOver 图标 SkipForward → DebugStepOver
- OnDebugVariableChanged 识别 print: 前缀 → 实时追加 ExecutionOutput
  (调试中即可见 Print 输出,完成时总结替换;Cleanup 重置缓冲)
- SetOutputWireValue: 设输出端口 RuntimeValue 后沿连线同步到目标输入端口
  (单出边约束保证 1:1;控制流输入保留 w:{ctrl}:{pin} 专用 wire)
- OnDebugVariableChanged w:{nodeId} 分支改调 SetOutputWireValue
- E: 条件/源子图虚线背景框 — StatementNodeToPrimary 反算控制流数据子图
  (condition/list/selector 标签),与 Body 实线框区分,While 条件显式可见
- C: 循环 body 内悬空 exec-out 显示 ↺ 回环图标(区别于 if 的 ⎍ 接地,
  tooltip「回到循环头重新求值条件」)
- 样式: ScopeFrame 直角 + 1px 细边框(条件框虚线)+ 网格对齐(15px 步进
  外扩取整)+ 标题弱化 — 读作背景选区而非可拖动容器
- LoadBlueprint/RefreshScopes 双路径同步(条件框 + 循环标记)
- 条件框包围盒加 FramePadding 24 外扩(仿 ScopeAnalyzer.ComputeBox)——此前无
  外扩且远边 Floor 对齐内缩,框紧贴/盖不住节点
- 对齐分 AlignFloor(原点)/AlignCeil(远边):Body 框与条件框统一,父框
  仍含子框、无节点突出框外
- 根因:Continue 清除 _breakOnCheckpoint 后,断点命中/手动 Pause 暂停再点 Step 只释放信号不重新武装,下个 checkpoint 不暂停

- StepNext 先重新武装 _breakOnCheckpoint 再放行:Step 恒为单步,Continue 恒为自由运行
- 断点:DebugRunAsync reload 前快照断点 → 按 Reverse 映射迁移(palette 随机 ID 归一化为 FNV);右键 Toggle Breakpoint 实时同步 debugger(父 VM 命令接线,VM 级重复命令删除)

- 布局:SaveAsync BP 模式写入 BlueprintLayout(canonical 键)+ 同步内存表;KS 模式保存保留旧表防覆盖;加载端 RenderBlueprintFromKs/ReloadCanvasFromIr 查表覆写(DetachedGraph/Entry 天然排除);拖动置 dirty(NotifyBlueprintEdited)

- 保存链路:SwitchToBlockScript/SwitchToBlueprint 切换前 dirty 自动保存;自动保存自循环化(3s 轮询替代一次性调度)

- 连线交互(附带修复):恢复 DisconnectConnectorCommand 绑定(端点 Alt+点击回归);连线 DisconnectCommand 绑定(Alt+点击连线首次生效);右键连线"断开连线"菜单(替代被占位 Popup 覆盖的 ContextMenu)
- NodeFactoryV6.CreateBuiltinFunctionNode 特判 DictNew(无 Exec pin,跳过 registry);新增 CreateDictNewDefinitionNode(Key0/Value0 初始对 + Dict 输出,DeclKind=var)
- VariadicPairHelper:配对组纯索引逻辑(ComputeNextIndex/EnumeratePair)+ 硬编码 DictNew spec(PinNamePrefixes/PinTypes 首次消费)
- TryExpandVariadicSide 重写:配对组模式按前缀收集 pin、连接组尾追加整对;legacy 单类型模式保留;AppendVariadicGroup Contract+VM 双写
- 调色板新增 DictNew 定义条目(不进 registry);ConvertNodeToViewModel 定义节点判定 + DeclName 标题 + RebuildDictPairs 加载
- 节点卡片键值对编辑区(Key/Value 文本框 + 删除/添加按钮,回写 pin DefaultValue + 脏标记);DictPairRowVM 防回写加载
- KitX.Dashboard.Test.Xunit 建立首个测试源文件(配对索引 5 项,全部通过);主项目构建 0 错误
- SwitchToBlockScript 与 BuildIrForCurrentMode(Run/DebugRun/Save)的 Reverse/ReverseWithNodePaths 调用传入 _lastIr 作为 ksPrivileged baseline,重挂块前 doc/文件尾注释(T7 K5)
- _lastIr 更新点覆盖:KS→BP 解析(RenderBlueprintFromKs)、.kcs 加载、BP→KS 反投影、保存后
- BuildIrForCurrentMode KS 分支与 RenderBlueprintFromKs(KS→BP 切换)的 Parse/ParseLowering 调用传入 _lastIr 作为 bpPrivileged baseline,重挂 DetachedGraphs(B1)
- 与 SwitchToBlockScript/BuildIrForCurrentMode BP 分支的 ksPrivileged(doc 注释)修复完全对称
…DictNew const 条目

- 问题 1(前端):usage 节点 Kind 切换(const/var)——const 引用移除 Value 输入 pin(只读),var 补回;Value 输入已有数据连线时拒绝切 const 并提示;标题/加载按 VarKind 显示
- 问题 2:usage 标题去掉 'PubVar:' 前缀(与契约 GetDisplayTitle 裸名一致);usage 面板加 Kind 行
- 问题 3:UpdateUsageNodeName 校验(UsageNameValidator.IsDeclaredName,Ordinal)——无效/空名回滚 VM + ErrorInfo 错误条;空文本 GotFocus 弹出完整候选列表(Dispatcher.Post)
- 问题 4:name 栏点击编辑三态(复用 Comment 模式:TextBlock ↔ AutoCompleteBox,Enter/Esc/失焦)
- 问题 5:KS const dict 合法 → 调色板新增 'DictNew(新建 const dict 定义)'(CreateDictNewDefinitionNode(declKind));DefinitionKindText 读 Properties.DeclKind;RefreshDefinitionNames 含 DictNew DeclName(对齐后端 KS130)
- 测试 +9(validator 6 + factory 3),14/14 通过;构建 0 错误
- 问题 1:移除 Kind 切换 ComboBox 与 UpdateUsageNodeKind/UpdateUsageNodeValuePinShape 逻辑;调色板新增 '常量(使用)'(VarKind=Const 无 Value 输入 pin,CreateConstUsageVariableNode)与 '变量(使用)'(PubVar 双 pin)两种独立使用型节点,与定义型节点同构;'常量(字面量)'保留为管道字面量源
- UsageVarKind 改为只读(加载/创建时固定),新增 IsConstUsageRefNode/IsVarUsageRefNode 只读指示;axaml 移除 Kind ComboBox,改只读 const/var 标签
- 问题 2:焦点 bug 根因——IsEditingUsageName 翻转 IsVisible 在下一布局周期生效,同步 Focus() 对不可见控件静默无效。修复:Focus 移入 Dispatcher.UIThread.Post 延迟执行(控件可见后聚焦,GotFocus 再弹候选列表);点击进入编辑时 e.Handled 防冒泡到画布抢焦点;GotFocus 空文本/非空文本统一弹列表
- 移除死代码 ApplyUsageKindFromContract;构建 0 错误(无新增警告),测试 14/14
- 根因:上一轮把文本点击当编辑入口,点击后 AutoCompleteBox 与画布争夺焦点;下拉 Popup 打开/关闭引发的 LostFocus 风暴会抢先提交(编辑框一闪而逝);Esc 取消后隐藏控件触发的 LostFocus 还会复活已丢弃的文本
- 交互重构(用户建议的 Edit 按钮方案):进入编辑的唯一入口 = Name 行 Pencil 图标按钮(Material.Icons,与注释指示器同模式);文本块不再可点击(不再与画布抢指针/焦点);编辑中再次点击按钮 = 提交
- 提交保护:CommitUsageName 增加 IsEditingUsageName 守卫——Esc 取消后迟到的 LostFocus 不再复活已丢弃文本;下拉 Popup 不抢焦点,点击候选不触发提前提交
- 构建 0 错误(无新增警告),测试 14/14
- 交互逻辑重构(重构成本小于打补丁):彻底移除焦点事件驱动(GotFocus 自动弹列表 / LostFocus 提交全部删除)——状态只由 Pencil 按钮点击与 Enter/Esc 键决定
- 按钮不移动:行改为固定三列 Grid(34/110/24),文本/占位符/AutoCompleteBox 三者重叠在中间同一格,Pencil 固定在右列——两种状态下按钮位置、行宽恒定,不再跳动
- 点击 Pencil:非编辑态=进入编辑(Post 聚焦 + 空文本弹列表,纯便利不驱动逻辑);编辑态=提交
- Enter=提交 / Esc=取消;无失焦提交(点击画布不会误提交)
- 构建 0 错误(无新增警告),测试 14/14
- 原点击框直接附着在 14px 图标上,只有绘制字形部分可点;Border(24x22,Background=Transparent)包裹后整列可点,点击事件移到 Border,图标仅作绘制
- AppFramework.cs:241 devicesServer.ConfigurePort 改用 UserSpecifiedDevicesServerPort(原误用 UserSpecifiedPluginsServerPort,导致用户配置的设备端口不生效、DevicesServer 绑定随机端口或与插件服务器抢端口)
- 删除 Services/ 下 WorkflowStorageService/WorkflowSessionManager/TriggerManager/RealBlueprintDebugger(已迁入 WorkflowV6 库)
- App.axaml.cs 移除对应 DI 注册(由 AddKitXWorkflowV6 提供);WorkflowEditorViewModelV6 引用改用新命名空间
- 删除 Services/PluginHostAdapter.cs 与 DashboardPluginServiceProvider.cs(已迁入 KitX.Core/Plugin)
- App.axaml.cs 移除 Kscript IPluginServiceProvider/IPluginManager/IPluginHost 三注册(由 AddCoreServices 提供)
- AppFramework WebManager 区 55 行编排缩为 INetworkService.StartAsync 调用(删除 new Thread/Thread.Sleep/3 处强转/ConfigurePort)
- EnsureExit 停止逻辑改 StopAsync;DevicesPageViewModel 按钮改 Restart/StopDevicesServersAsync
- WorkflowEditorViewModelV6 Run/DebugRun 改走 WorkflowRunner(执行路径与 SessionManager 统一)
…evices/PluginsLaunch/Editor)

- WorkflowPageViewModel/DevicesPageViewModel/PluginsLaunchWindowViewModel/WorkflowEditorViewModelV6 无参构造器改 DI 构造器注入,移除全部 KitX.Core.DI.ServiceHost 直调(5+1 处)
- View 创建点改 App.GetService;App.axaml.cs 补 4 个 AddTransient 注册
- DeviceCase 改注入依赖后直接 new(行为等价);View 层 App.GetService 按 Avalonia 常规模式保留(无 ServiceHost)
- ViewModel 层 ServiceHost 零残留
- 调试 free-run 按 16ms 窗口聚合 Dispatcher Post(步进/断点语义不变),万级循环 Post 从 ~2 万降到 ~60;变量表改 Dictionary 索引 O(1) 更新
- FileDialogService 关窗(标题栏 X)不再永久挂起(Closed → TrySetResult(null))
- WorkflowEditorWindowV6 关闭前保存改为 e.Cancel + 完成后 Close,消除丢数据窗口
- UIStateService/WorkflowPageViewModel 事件退订(页面每次导航重建,退订防泄漏),WorkflowPage Unloaded 触发 Dispose
- 移除 Fleck/Serilog.AspNetCore/Swashbuckle(零使用);补 CodeAnalysis.Scripting 直接引用(DebugService 用,原依赖传递)与 Serilog.Sinks.Console 显式引用
- 删除 Commands/DelegateCommand.cs(零调用)、ConstantTable 死代理字段与 ExchangeDeviceKeyWindow 死赋值
D-15/D-13/D-16: Settings_Performence→Performance(文件+类+XAML+7 语言文件键名批量统一,修复 DynamicResource 解析断裂);AfterInitialization;ExperimentalFlags 死开关删除;SignalsNames 改常量类;DebugService 引擎单例+开发者门禁;空 if 清理;LanguageLoader 白名单校验。
D-14: 剩余全部 VM(App/MainWindow/Announcements/ExchangeKey/PluginDetail/Home 系/Settings 系/Repo/Lib/Market/Account 等)构造器注入服务;View 创建点统一 App.GetService;DI 注册全部 VM;ViewModelBase 静态访问器仅 Settings_UpdateViewModel(保留项)继续使用;VM 内 App.GetService 零残留。
D-1..D-13: ① Release 构建修复(#if DEBUG 字段移出条件编译);② 四页面搜索框实现过滤;③ DebugRunAsync 清理统一;④ Bitmap 缓存;⑤ 悬停预览 200ms 去抖+图版本缓存(LoadBlueprint/Clear 补 MarkGraphEdited);⑥ 复制粘贴收敛(NavigationPaneViewModelBase/ThemeColorPalette/LanguageLoader/LoggerConfigurator);⑦ Home 最近使用属性名统一+管线接通;⑧ 错误码常量类 KsConstraintErrorCodes;⑨ 占位 throw 清理;⑩ 强转清理;⑪ 订阅泄漏 D5 模式推广(含 BlueprintConnectionVMV6.Detach、Home_Count);⑫ IO 后台化;⑬ Timer 防抖/热路径日志降级/魔法字符串收敛;适配:节点引脚播种(描述器机制删除补偿)、LogLevel 契约枚举映射、ConstantTable 死常量删除、ConfigLoadTrail 残留删除。
D-REG: ① Settings_Personalise/About/Update VM 构造器为 internal,MS DI 仅匹配 public 构造器导致页面无法打开——改 public;② SettingsPage.axaml/AppViewModel 残留 Text_Settings_Performence 旧键(语言文件已改名)导致性能页标题消失——同步为新键;③ AppViewModel 插件事件/DevicesPage 发现事件在服务器/UDP 线程跨线程修改 ObservableCollection(Lib 页计数与 TrayIcon 不一致、设备卡片重复)——封送 UI 线程;④ LibPage/DevicesPage 过滤集合缺初始填充(CollectionChanged 只响应未来变化)——构造器补 ApplyFilter。
@StardustSeemsInk StardustSeemsInk changed the title [Pull Request] Dev=v6-grammar [Pull Request] WorkflowV6: v6 蓝图编辑器 + 前后端分离适配 Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants