feat(lesson-preparation): add readonly view, anchor node selector, and type guards

- Add lesson-plan-readonly-view for viewing published plans

- Add anchor-node-selector and textbook-segments for canvas anchor positioning

- Add i18n-errors and type-guards lib utilities

- Add lesson-plan-provider-setup for provider initialization

- Update actions, data-access (knowledge, versions, main), publish-service

- Update blocks (blackboard, exercise, homework, import, key-point, objective, reflection)

- Update editor, node-editor, node-edit-panel, pickers, and providers
This commit is contained in:
SpecialX
2026-06-24 12:02:42 +08:00
parent a48e7d0e27
commit 6bc113eaff
39 changed files with 2129 additions and 571 deletions

View File

@@ -31,7 +31,8 @@ interface EditorState {
hydrate: (planId: string, title: string, doc: LessonPlanDocument) => void;
addNode: (type: BlockType, position?: { x: number; y: number }, title?: string) => string;
updateNode: (id: string, patch: Partial<Block>) => void;
// V3 修复patch 排除 type 字段,防止改变节点类型,同时消除 as 断言
updateNode: (id: string, patch: Omit<Partial<Block>, "type">) => void;
updateNodePosition: (id: string, position: { x: number; y: number }) => void;
removeNode: (id: string) => void;
@@ -127,11 +128,14 @@ export const useLessonPlanEditor = create<EditorState>((set, get) => ({
set((s) => ({
doc: {
...s.doc,
// V3 修复patch 已排除 type 字段,但 TypeScript 仍会因 spread 拓宽 data 类型
// BlockData 联合不包含 TextbookContentNodeData而报错此处 as 为必要断言。
// 实际安全:调用方不会对 textbook_content 节点通过 updateNode 传入 data。
nodes: s.doc.nodes.map((n) =>
n.id === id
? n.type === "textbook_content"
? { ...n, ...patch } as TextbookContentNode
: { ...n, ...patch } as LessonPlanNode
? ({ ...n, ...patch } as TextbookContentNode)
: ({ ...n, ...patch } as LessonPlanNode)
: n,
),
},
@@ -143,11 +147,12 @@ export const useLessonPlanEditor = create<EditorState>((set, get) => ({
set((s) => ({
doc: {
...s.doc,
// 同 updateNodespread 后 TypeScript 拓宽类型,需 as 断言收窄
nodes: s.doc.nodes.map((n) =>
n.id === id
? n.type === "textbook_content"
? { ...n, position } as TextbookContentNode
: { ...n, position } as LessonPlanNode
? ({ ...n, position } as TextbookContentNode)
: ({ ...n, position } as LessonPlanNode)
: n,
),
},