chore: checkpoint web Blender feature-gap work, docs and tests
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

- update native web_engine sources and rebuilt wasm artifacts
- add task cards, status notes and tooling scripts
- add web e2e specs, tutorial UI sources and tutorial assets
- sync golden task manifests and audit records

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
mes123456
2026-11-11 10:46:55 +08:00
parent f3e21c6b98
commit d1e6b63d54
665 changed files with 59149 additions and 39194 deletions

View File

@@ -0,0 +1,171 @@
import { PARTS_TUTORIAL_REQUIRED_STEPS, PARTS_TUTORIAL_STEPS, PARTS_TUTORIAL_STORAGE_KEY } from "./parts/curriculum";
import { REDUCER_TUTORIAL_REQUIRED_STEPS, REDUCER_TUTORIAL_STEPS, REDUCER_TUTORIAL_STORAGE_KEY } from "./reducer/curriculum";
import type { TutorialStep } from "./parts/types";
export type CourseId = "parts" | "reducer";
export interface TutorialCourseCard {
name: string;
note: string;
icon: string;
}
export interface TutorialGalleryItem {
step: string;
title: string;
note: string;
}
/** Everything a course needs that was previously hardcoded in the parts UI. */
export interface TutorialCourse {
id: CourseId;
title: string;
heading: string;
steps: readonly TutorialStep[];
requiredSteps: readonly TutorialStep[];
storageKey: string;
opfsFile: string;
assetDir: string;
startUrl: string;
saveStepId: string;
summaryStepId: string;
/** Checkpoint every N advances; heavy courses can checkpoint less often. */
checkpointEvery?: number;
/** Set only when the course must own a deterministic OPFS project id. */
defaultProjectId?: string;
defaultProjectName: string;
pythonFileName: string;
overlayTitle: string;
reopenAria: string;
heroTitle: string;
heroAccent: string;
eyebrow: string;
lead: string;
partsLead: string;
galleryLead: string;
bottomCta: string;
bottomCtaContinue: string;
parts: readonly TutorialCourseCard[];
summaryParts: readonly string[];
gallery: readonly TutorialGalleryItem[];
skills: readonly string[];
}
const PARTS_CARDS: readonly TutorialCourseCard[] = [
{ name: "底座", note: "立方体 + 倒角 + 布尔孔", icon: "base" },
{ name: "轴", note: "圆柱 + 编辑模式 + 平滑", icon: "shaft" },
{ name: "密封圈", note: "贝塞尔曲线 + 倒角深度", icon: "ring" },
{ name: "垫圈", note: "布尔差集 + 阵列", icon: "washer" },
{ name: "螺栓", note: "六角头 + 合并 + 文字 M8", icon: "bolt" },
{ name: "支架", note: "实体化 + 镜像 + 细分", icon: "bracket" },
];
const PARTS_GALLERY: readonly TutorialGalleryItem[] = [
{ step: "base-holes", title: "底座 · 布尔打孔", note: "四个圆柱合并成刀具,再用 Boolean 差集切出安装孔。" },
{ step: "base-detail", title: "底座 · 编辑模式沉台", note: "在顶面内插并向下挤出,做出装配用的沉台。" },
{ step: "shaft-create", title: "轴 · 圆柱与倒角", note: "圆柱参数化创建,加 Bevel 修改器并平滑着色。" },
{ step: "gasket-curve", title: "密封圈 · 曲线建模", note: "贝塞尔圆环设置 Bevel Depth,再转换为网格。" },
{ step: "washer-boolean", title: "垫圈 · 布尔内孔", note: "外圆柱减去内圆柱,再用 Array 叠放三片。" },
{ step: "bolt-shaft", title: "螺栓 · 头杆合并", note: "六角头与螺栓杆 Join 成单个对象,再加倒角。" },
{ step: "bracket-subsurf", title: "支架 · 镜像与细分", note: "Solidify 加厚度,Mirror 对称,Subsurf 圆润边缘。" },
{ step: "assembly-hierarchy", title: "装配 · 集合与父子", note: "零件归入机械零件集合,并以 Empty 作为装配总成。" },
];
const PARTS_SKILLS: readonly string[] = [
"创建与变换:基础体、曲线、文字、Empty,移动/旋转/缩放与数值输入",
"编辑模式:内插、挤出、倒角、选择与法线",
"修改器与布尔:Bevel、Boolean、Mirror、Array、Solidify、Subsurf",
"层级、材质与导出:集合、父子、材质颜色、保存 .blend、导出 GLB",
];
export const COURSES = {
parts: {
id: "parts",
title: "机械零件套装",
heading: "从零制作机械零件套装",
steps: PARTS_TUTORIAL_STEPS,
requiredSteps: PARTS_TUTORIAL_REQUIRED_STEPS,
storageKey: PARTS_TUTORIAL_STORAGE_KEY,
opfsFile: "parts-progress.json",
assetDir: "/tutorial/parts",
startUrl: "/?tutorial=parts&start=1",
saveStepId: "save-export",
summaryStepId: "summary",
checkpointEvery: 1,
defaultProjectName: "机械零件套装.blend",
pythonFileName: "PartsTutorial.py",
overlayTitle: "零件建模互动教程",
reopenAria: "展开零件建模教程",
heroTitle: "从零制作",
heroAccent: "机械零件套装",
eyebrow: "互动教学 · 初学者 · 约 60–90 分钟",
lead: "在真实运行的 Web Blender 里动手完成一套零件:底座、轴、密封圈、垫圈、螺栓与支架。每个步骤都会实时检查你的场景,卡住时可以一键演示。",
partsLead: "从基础体到布尔、曲线与文字,覆盖零件创建的主要手法。",
galleryLead: "以下画面由有头 Chromium 实际执行每一步“演示本步”后截取,不是示意图。",
bottomCta: "进入教程",
bottomCtaContinue: "继续上次进度",
parts: PARTS_CARDS,
summaryParts: ["底座", "轴", "密封圈", "垫圈", "螺栓", "螺栓标记", "支架"],
gallery: PARTS_GALLERY,
skills: PARTS_SKILLS,
},
reducer: {
id: "reducer",
title: "二级减速器",
heading: "从零制作二级减速器",
steps: REDUCER_TUTORIAL_STEPS,
requiredSteps: REDUCER_TUTORIAL_REQUIRED_STEPS,
storageKey: REDUCER_TUTORIAL_STORAGE_KEY,
opfsFile: "reducer-progress.json",
assetDir: "/tutorial/reducer",
startUrl: "/?tutorial=reducer&start=1",
saveStepId: "save-export",
summaryStepId: "summary",
checkpointEvery: 3,
defaultProjectId: "tutorial-reducer",
defaultProjectName: "二级减速器.blend",
pythonFileName: "ReducerTutorial.py",
overlayTitle: "二级减速器互动教程",
reopenAria: "展开二级减速器教程",
heroTitle: "从零制作",
heroAccent: "二级减速器",
eyebrow: "互动教学 · 初学者 · 约 90–120 分钟",
lead: "在真实 Web Blender 中完成一台二级减速器:箱体、箱盖、三根轴、四个齿轮、六套轴承与端盖,最后用集合和 Empty 建立完整装配层级。每一步都会实时检查你的场景。",
partsLead: "从箱体到齿轮,覆盖实体建模、布尔、阵列与标准件的完整流程。",
galleryLead: "以下画面由有头 Chromium 实际执行每一步“演示本步”后截取,不是示意图。",
bottomCta: "进入减速器教程",
bottomCtaContinue: "继续上次进度",
parts: [
{ name: "箱体", note: "立方体 + Solidify + 布尔轴承孔", icon: "base" },
{ name: "箱盖", note: "内插止口 + Mirror 螺栓孔", icon: "bracket" },
{ name: "齿轮", note: "Array 对象偏移 + Boolean 切齿", icon: "bolt" },
{ name: "轴", note: "圆柱 + 轴颈 + 布尔键槽", icon: "shaft" },
{ name: "轴承", note: "Torus + 滚球 + Join", icon: "ring" },
{ name: "附件", note: "通气帽 / 油标 / 放油塞 / 铭牌", icon: "washer" },
],
summaryParts: ["箱体", "箱盖", "输入轴", "中间轴", "输出轴", "齿轮1–4", "轴承1–6", "端盖1–6", "键1–4", "法兰螺栓", "通气帽", "油标", "放油塞", "铭牌"],
gallery: [
{ step: "housing-bores", title: "箱体 · 布尔轴承孔", note: "六个圆柱 Join 成孔刀,再用 Boolean 差集切穿两端壁。" },
{ step: "gear1-cutter", title: "齿轮 · 阵列齿槽刀", note: "齿槽刀原点放在齿轮中心,Empty 旋转 360/17,Array 用对象偏移复制。" },
{ step: "gear2", title: "齿轮组 · 参数化齿数", note: "同一套阵列+布尔流程生成 z=34 的大齿轮。" },
{ step: "bearings-set", title: "轴承 · Torus 与滚球", note: "内外套圈、滚球合并后复制到三根轴两端。" },
{ step: "shaft-assemblies", title: "装配 · 轴组件 Empty", note: "每根轴的零件 parent 到对应 Empty,实时装配树同步更新。" },
{ step: "collections", title: "层级 · 集合归类", note: "箱体组件、传动组件、附件三个子集合。" },
{ step: "materials-assign", title: "材质 · 四种表面", note: "铸铁、钢、黄铜、蓝漆分配到不同零件。" },
{ step: "save-export", title: "交付 · 保存与 GLB", note: "工程写入 OPFS,并导出 GLB 供外部使用。" },
],
skills: [
"零件创建与变换:立方体、圆柱、Torus、Text、Empty,应用缩放与旋转",
"编辑模式:删面、内插、挤出、倒角与平滑着色",
"修改器与布尔:Solidify、Bevel、Boolean 并集/差集、Array 对象偏移、Mirror、Subsurf",
"传动件建模:轴颈与键槽、齿轮齿槽阵列、轴承套圈与滚珠",
"装配与交付:集合、Empty 父子层级、实时装配树、材质、链接复制、保存与 GLB 导出",
],
},
} satisfies Record<CourseId, TutorialCourse>;
export type AvailableCourseId = keyof typeof COURSES;
export function isAvailableCourseId(value: string | null): value is AvailableCourseId {
return value !== null && Object.prototype.hasOwnProperty.call(COURSES, value);
}