# 开发提示词：窗口内 3D —— WebView2 内嵌新迪查看器（方案 B）

> 用途：把本文件交给 AI 会话执行。任务完成后按"验收标准"逐条核对，并把结果回写 OnebotCatalog\项目总览.md（进展记录 + 踩坑表 + 最后更新行）。
> 背景必读：OnebotCatalog\项目总览.md（§12 新迪查看器联动）、OnebotCatalog\src\App\MainWindow.cs、OnebotCatalog\src\App\LocalViewerServer.cs。

## 一、角色与任务

你是资深 WPF/桌面软件工程师，在既有项目 **OnebotCatalog**（C# / .NET Framework 4.8，代码式 WPF，零 XAML）上实现：**真实 CAD 数模（STEP）在软件窗口内直接显示 3D，不再弹出外部浏览器**。

## 二、现状（先读懂再动手）

当前桌面 3D 显示链（`src\App\MainWindow.cs` 的 `TryUpdateSelection()` / `TryLegacyRender()` / `FallbackToBrowser()` / `OpenBrowser3D()`）：

1. 占位简化几何（自产极简 STEP）→ 内置 `ViewerControl` 自绘渲染（窗口内）
2. 真实数模且有 STL 网格侧车（`<数据包目录>\mesh\<step同名>.stl`，见 `FindMeshSidecar`）→ 开源 Helix Toolkit 窗口内直渲（已实现，2026-08-23）
3. 真实数模且无网格 → **当前弹出外部浏览器**（`Process.Start(url)` + `LocalViewerServer` 本机微型服务器 + 本地新迪查看器副本 `viewer\STEPViewer\`）

本次目标：把第 3 条从"弹外部浏览器"改成"**窗口内嵌 WebView2 加载同一 URL**"。第 1、2 条保持不动。

## 三、硬性约束（违反即返工）

1. **完全离线**：WebView2 加载的是本机 127.0.0.1 微型服务器的 URL，无任何外网请求
2. 目标平台 Win10/11 x64；客户机免安装（发布目录拷走即用）
3. 编译环境：无 .NET SDK，只有 VS 2022 Build Tools 的 csc.exe + .NET Framework 4.8（`tools\build.ps1` 已封装，新增引用只能加 DLL `/reference`，不能用 csproj/NuGet 还原）
4. **编辑任何含中文的 .cs/.ps1 后必须跑 `sample-data\tools\fix-bom.ps1`**（csc 对无 BOM 文件按 GBK 读取）
5. 客户版与开发版同一套代码（`#if CUSTOMER_BUILD` 区分）；新功能两版都要生效
6. 不碰 `Z:\web\FTP_ObjectStorage\上海新迪3D\dev` 原项目（只读红线）
7. 不改动既有数据管线（catalog\ / .opc / publish 六步），本次只动桌面 3D 显示层与发布附带

## 四、实施步骤（严格按序）

### 步骤 1：引入 WebView2 控件库（需用户已批准下载）

- 下载 NuGet 官方源包 **Microsoft.Web.WebView2**（最新稳定版，确认 `lib\net462\` 目标；.NET 4.8 兼容 net462）
- 解出三个文件放进 `OnebotCatalog\lib\`：
  - `Microsoft.Web.WebView2.Wpf.dll`（WPF 控件）
  - `Microsoft.Web.WebView2.Core.dll`
  - `WebView2Loader.dll`（**native 加载器，分 x86/x64**：anycpu 程序在 x64 系统跑 64 位，须放 **x64** 版本与 exe 同目录；必要时 build.ps1 里按位宽复制）
- `tools\build.ps1`：`$refs` 追加两个托管 DLL；编译成功后把两个托管 DLL + WebView2Loader.dll 复制到输出目录（现有 Helix DLL 复制的同一位置照抄）
- `tools\publish.ps1`：发布目录复制同样的 DLL（照抄现有 Helix DLL 复制行）

### 步骤 2：MainWindow 3D 标签页加 WebView2 控件

- 字段：`Microsoft.Web.WebView2.Wpf.WebView2 _webView2;`
- 在 `grid3d`（3D 标签页 Grid）中创建，初始 `Visibility = Visibility.Collapsed`；背景与 Helix 视口一致（浅灰）
- **初始化顺序（WebView2 特有坑）**：`EnsureCoreWebView2Async()` 完成前不能设置 `Source`、不能调用 Core 属性；构造时先 `InitializeAsync()`：
  - `CoreWebView2Environment.CreateAsync(null, userDataFolder)`，`userDataFolder` 用 `%LOCALAPPDATA%\OnebotCatalog\WebView2`（离线无权限问题），环境带默认浏览器开关
  - 初始化失败（WebView2 Runtime 未安装）→ 捕获异常置 `_webView2Available = false` → 走降级（见步骤 3）
- 窗口关闭时 `Dispose()` 释放（`OnClosed` 或 `Closed` 事件）

### 步骤 3：显示优先级重构（核心逻辑）

`TryUpdateSelection()` 中 3D 分支改为四级优先链，每级失败自动降级：

```
1. FindMeshSidecar 有网格          → Helix 窗口内直渲（现有 LoadHelixMesh）
2. 真实 B-rep 且 WebView2 可用      → _webView2.Source = LocalViewerServer 的 URL（新建），不 Process.Start
3. 真实 B-rep 且 WebView2 不可用     → 现有浏览器回退面板（FallbackToBrowser 保持原样）
4. 占位简化几何                     → 内置 ViewerControl 自绘（现有 TryLegacyRender 逻辑）
```

- `OpenBrowser3D` 拆成两件事：`GetPreviewUrl()`（PublishStep 返回 URL，纯函数）与 `OpenBrowser3D`（Process.Start 打开）。WebView2 分支只调用前者
- 每次切换型号：WebView2 分支要**隐藏 Helix 视口与回退面板**、显示 `_webView2`；其他分支反过来
- 状态栏文案：窗口内显示时提示"已在窗口内显示真实数模 3D（WebView2）"，与英文对照 `_lang.T(zh, en)`

### 步骤 4：WebView2 可用性检测与降级

- Runtime 检测（二选一，双保险）：① `EnsureCoreWebView2Async` 抛异常即不可用；② 启动时查注册表 `HKLM\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}` 的 pv 值存在
- 不可用 → 3D 分支走第 3 级（浏览器回退面板），体验与现在一致；状态栏提示"未检测到 WebView2 运行时，使用浏览器预览（Win11 自带，Win10 装 Edge 即有）"

### 步骤 5：发布与交付

- `publish.ps1` 发布目录附带 WebView2 三件套 DLL（步骤 1）
- 发布目录 `README_zh.txt` 增加一行：Win10 若提示缺少 WebView2 运行时，安装 Edge 或微软 WebView2 Evergreen 运行时即可（可选附离线安装包到发布目录 `webview2\`）
- 客户版与开发版行为一致

### 步骤 6：回归验证

- `bin\OnebotCatalog.exe --selftest sample\OnebotCatalog_2026.08.opc` → 31 项全过（本次不改数据，必须不变）
- 编译开发版 + 客户版都成功；`tools\publish.ps1` 全流程过
- 人工验收清单（见下节）逐条过，结果写进项目总览进展记录

## 五、验收标准（人工，逐条核对）

1. 打开开发版 → 选「气动夹紧缸 UCBM → 规格 32」（真实数模、无网格场景要先临时移走 `sample\mesh\UCBM32.stl` 验证）：**3D 在软件窗口内显示**，无任何浏览器窗口弹出；拖拽旋转/滚轮缩放正常；剖切/线框/透明/视角/测量按钮可用（menu=min 精简菜单）
2. 移回网格后选同一型号：窗口内 Helix 渲染优先（画面切换无闪烁）
3. 选占位型号（如 UCBM25）：仍是内置自绘渲染，无 WebView2 介入
4. 客户版发布目录双击运行：同 1~3 行为一致；完全断网验证（拔网线/禁网卡）可用
5. 状态栏提示文案中英切换正确（右上角语言切换按钮）
6. 关闭窗口进程干净退出（无残留 OnebotCatalog.exe）

## 六、已知陷阱（本项目血泪史 + WebView2 专属）

| # | 陷阱 | 对策 |
|---|---|---|
| 1 | PowerShell/csc 把无 BOM 中文文件按 GBK 读 → 乱码报错 | 改完必跑 fix-bom.ps1（步骤见约束 4） |
| 2 | GUI 子系统 exe 用 `&` 启动后 PowerShell 不等待，$LASTEXITCODE 空 | 一律 `Start-Process -Wait -PassThru`，核对 build.log/selftest.log 时间戳 |
| 3 | WebView2 在 `EnsureCoreWebView2Async` 完成前设置 Source/属性 → 崩溃 | 全部操作放在 await 之后；初始化失败路径显式处理 |
| 4 | WebView2Loader.dll 位宽不匹配 → 创建控件时 TypeInitializationException | anycpu + x64 系统用 x64 loader；发布时按位宽复制正确版本 |
| 5 | userDataFolder 无权限（Program Files 等）→ 初始化失败 | 用 %LOCALAPPDATA%\OnebotCatalog\WebView2 |
| 6 | 发布前程序/服务器占文件 → 复制失败 | 发布前停 serve-web 和运行中的 exe（红线） |
| 7 | 无头自动化难验 WebView2 渲染 | 用人工验收清单 + 降级路径自动测试；WebView2 失败必须静默降级到浏览器方案 |
| 8 | 新迪查看器 file 参数已改为站点根绝对路径 `/step/...`（踩坑 #20） | LocalViewerServer 的 URL 构造保持现状，不要改回相对路径 |

## 七、交付物清单

1. 可运行的开发版 exe（bin\）+ 客户版 exe（release 目录）
2. `lib\` 新增 WebView2 三件套；build.ps1 / publish.ps1 相应改动
3. MainWindow.cs 改动（含注释，风格与现有代码一致）
4. 文档回写：项目总览.md（进展记录新条目 + 踩坑表新增 WebView2 相关 + §12 更新 + 最后更新行）、docs\维护手册.md §7、README.md 已知简化段、数据维护说明书.md 桌面 3D 相关说明
5. 本文件验收标准逐条核对结果

## 八、不做的事（范围外）

- 不碰网页版（web\ 与 release\web_2026.08）
- 不改数据管线与 .opc 格式
- 不做 WebView2 离线运行时安装器（只附可选安装包说明）
- 不把 WebView2 用于除 3D 标签页以外的任何界面
