chore: 迁移到 pnpm 并更新打包与测试文档

- 用 pnpm 替换 npm,锁定 pnpm@10.33.0,删除 package-lock.json 相关说明
- package.json 将渲染层依赖移入 devDependencies,减小 app.asar 体积
- 新增 electron:smoke 冒烟脚本与 icon 生成脚本
- win 平台设置 AppUserModelID,修复任务栏图标归组
- 清理 tsBuildInfoFile 输出路径至 node_modules/.tmp
- 更新测试断言以匹配画幅遮罩、取景器缩放与模型库外部资产行为
- README 与打包指南补充 pnpm、镜像与常见问题说明
This commit is contained in:
2026-09-16 20:03:57 +08:00
parent 2ad176be4e
commit b9fd289caf
27 changed files with 5538 additions and 8452 deletions

View File

@@ -60,7 +60,9 @@ it("keeps the current orbit distance when native gizmo axis clicks switch viewpo
it("positions the transparent native gizmo hit target over the visible X axis head", () => {
const style = getViewportGizmoHitButtonStyle(DEFAULT_DIRECTOR_VIEW_SNAPSHOT, [1, 0, 0]);
expect(style.left).toBe("92px");
expect(style.top).toBe("52px");
// 命中框以 .viewport-gizmo-overlay 的 80px 图层为基准(见 index.css 契约测试):
// 中心 40px,X 轴正向落在屏幕半径 25px 处,15px 命中框需回退半个身位。
expect(style.left).toBe("57.5px");
expect(style.top).toBe("32.5px");
expect(style.zIndex).toBe(100);
});

View File

@@ -308,7 +308,8 @@ it("renders the viewport aspect ratio overlay when a non-auto frame is selected"
render(<App />);
expect(screen.getByLabelText("视口画幅框")).toBeInTheDocument();
expect(screen.getAllByLabelText("视口画幅遮罩")).toHaveLength(4);
// 遮罩已由「四块矩形」改为单元素 + CSS mask-composite 挖洞(见 index.css 契约测试)。
expect(screen.getAllByLabelText("视口画幅遮罩")).toHaveLength(1);
expect(screen.getByLabelText("视口画幅框")).toHaveAttribute("data-aspect-ratio", "9:16");
});

View File

@@ -6,6 +6,12 @@ import { BODY_TYPE_OPTIONS } from "../runtime/mannequin/bodyTypes";
import { createInitialDirectorState, useDirectorStore } from "../store/directorStore";
import { getCameraRigPositionFromViewSnapshot, getCameraViewSnapshotFromShot } from "../schema/cameraGeometry";
import { ViewportToolbar } from "./ViewportToolbar";
import { getModelLibraryItems } from "../modelLibrary/modelLibraryCatalog";
// 模型库是仓库外的外部资产(../模型库),不随仓库分发。
// 目录缺失时 catalog 解析为空数组,依赖具体模型的用例应跳过而不是失败。
const hasModelLibrary = getModelLibraryItems().length > 0;
const itWithModelLibrary = it.skipIf(!hasModelLibrary);
const mockReadLocalModelFile = vi.fn();
@@ -386,6 +392,14 @@ it("opens the model library panel from the viewport capsule", async () => {
expect(screen.getByRole("tab", { name: "工具配件" })).toBeInTheDocument();
expect(screen.getByRole("tab", { name: "我的模型" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "关闭模型库" })).toBeInTheDocument();
});
itWithModelLibrary("shows Chinese model names and thumbnails for convenience items", async () => {
const user = userEvent.setup();
render(<ViewportToolbar />);
await user.click(screen.getByRole("button", { name: "模型库" }));
expect(screen.getByRole("button", { name: "添加模型 自动取款机" })).toBeInTheDocument();
expect(screen.getByText("自动取款机")).toBeInTheDocument();
expect(screen.queryByText("ATM")).not.toBeInTheDocument();
@@ -393,7 +407,7 @@ it("opens the model library panel from the viewport capsule", async () => {
expect(screen.getByRole("button", { name: "添加模型 自动取款机" }).querySelector("img")).toBeInTheDocument();
});
it("uses category thumbnail folders for outdoor and tools model library items", async () => {
itWithModelLibrary("uses category thumbnail folders for outdoor and tools model library items", async () => {
const user = userEvent.setup();
render(<ViewportToolbar />);
@@ -439,7 +453,7 @@ it("closes the model library panel from its close button", async () => {
expect(screen.queryByRole("dialog", { name: "模型库" })).not.toBeInTheDocument();
});
it("adds a selected model library item into the viewport scene", async () => {
itWithModelLibrary("adds a selected model library item into the viewport scene", async () => {
const user = userEvent.setup();
render(<ViewportToolbar />);