chore: checkpoint web Blender feature-gap work, docs and tests
- 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:
171
web/app/src/tutorial/course.ts
Normal file
171
web/app/src/tutorial/course.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user