教学三尺堂(幼小版)

SKILL教育学习
教育学习 未标注权限 许可证不明确
安装这个技能

教学三件套一键生成上课材料(幼小/小学低龄向):教案 DOCX + 课堂互动课件(单文件 HTML,零依赖双击即用)+ 可选防刷题作业(单文件 HTML,无后端)。支持按学科+年级+知识点/教材课节从零生成,或基于上传的教案/课件/习题整理重做;多学科同批交付按学科独立成文件。一句话即可开工

一句话选型结论

按资料字段整理

教学三尺堂(幼小版)是教育学习类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类教育学习 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_0ed12131/podiumtrio
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

教育学习
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_0ed12131仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验2026-11-06上游最后核验时间 · 数据更新 2026-11-06
正文体量62.8 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
讲解知识点

按水平把知识点讲清楚。

出练习题

按章节生成练习与答案。

制定学习计划

按目标和时间排学习节奏。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在教育学习场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
教学三尺堂(幼小版)当前 未标注权限 未标注 未标注 未标注 有
食谱创建课堂课程 未标注权限 GitHub 仓库 25.5 万 Codex、Claude、Cursor 有
语言学习 联网访问 ClawHub 市场 23.6 万 OpenClaw 有
learning 效率 只读 ClawHub 市场 19.2 万 OpenClaw 有
学习习惯 未标注权限 ClawHub 市场 10.2 万 OpenClaw 有
日语家教 只读 ClawHub 市场 8.2 万 OpenClaw 有

SKILL.md 全文

62.8 KB
模型实际读到的说明书原文,来自技能仓库

教学三件套(教案 + 课件 + 作业)

一次对话交付「上课要用的整套材料」。

先读我:这份文档怎么用(10 秒版)

你是谁 怎么用这份文档
老师 / 非技术用户 只看下面「快速开始」和「产物定义」两节,然后直接对 AI 说需求就行——其余全部技术章节是 AI 的操作手册,不需要你读,也不影响你使用
AI 助手 全文即执行规范:流程照『流程总览』走,各产物实现细节按需查内联章节
维护者 单文件自包含,只改这一个文件;改完重跑构建脚本与冒烟测试(见对应章节)

高频黑话速查(文档里反复出现的词,一次讲清):

词 人话解释
教案批注抽屉 课件每页底部可折叠的「教师活动/学生活动/设计意图」,投影时不显示、老师点开才见
源层 / 成品 源层=working/ 里的小文件(改任何东西只改这里);成品=拼装出来的大 HTML(含图片数据,几 MB 大,别手动改也别打开读)
构建脚本(build.py) 把源层小文件一键拼装成成品的脚本——改完源文件跑一次就出新版课件
冒烟测试全绿 AI 自检程序把课件里每个按钮自动点一遍、每页自动量一遍,全部通过=成品没坏
提交码(HW1:) 学生做完防刷题作业生成的一段代码,发给老师、老师粘贴进核验区即可查看作答,全程不需要服务器

边界说明(这个 Skill 不做什么)

  • 面向幼小衔接与小学低龄段教学场景;高中及以上学科深度不保证
  • 课件为单文件离线 HTML:不含在线多人实时协作、需要服务器/账号系统的在线题库
  • 视频/音频素材以程序化合成与抽轨压缩为主,不承诺处理任意第三方版权素材
  • 教材内容以官方目录核对为准,不凭记忆编造课文(查不到会先说明再动笔)

快速开始(零门槛,一句话即可开工)

用户说 得到
"生成人教版三年级上册《秋天的雨》的课件" 课件 HTML(双击即用)
"做一套初二物理《大气压强》的教案和课件" 教案 DOCX + 课件
"把这份教案转成课件和作业"(附上传的教案) 课件 + 作业(以底稿为蓝本)
"做三年级开学第一课的三科上课材料" 每科一个文件夹,各含独立三件

年级/教材在请求里写明就直接开工;没写且无法推断才问这一个;课件页数、课时划分、主题色等细节自动按教学惯例处理,不追问。本文件是编排层:负责参数收集、输入解析、目录组织、跨件一致性与统一交付;全部实现规范内联于本文档下方章节——单文件自包含,后续修改只改这一个文件:

产物 实现规范
互动课件 HTML 下方『互动课件实现规范』(结构、学科交互组件、绑定坑、jsdom 测试骨架)
防刷题作业 HTML 下方『防刷题作业实现规范』(四机制、确定性乱序算法、提交码、测试清单)
DOCX 流水线排错 下方『Windows 环境修复』(venv 自毁根因与 uv 重建步骤)

产物定义(验收以此为准)

  1. 教案 DOCX:教研标准结构——基本信息、教材分析、学情分析、教学目标(核心素养四维)、重难点、教学准备、教学过程(分环节,每环节含教师活动/学生活动/设计意图)、板书设计、分层作业(基础/提升/拓展)、评价量表、教学反思预设。有图片素材时嵌入正文对应位置。
  2. 课堂互动课件:单文件自包含 HTML。零外部依赖(无 CDN/音频文件/图片外链),双击即用;16:9 统一舞台(锁高下限不缩放);封面页+结束页默认必带;每个教学环节一页,页内嵌教案批注(教师/学生活动、设计意图);学科交互组件按需选配。
  3. 防刷题作业(用户勾选才生成):单文件 HTML。四机制齐上——变式池、选项乱序(确定性可复现乱序)、判据双答(判断+依据各占半分)、提交前零判分;配套行为留痕(用时/切屏/改答/换题)、localStorage 草稿、HW1: 提交码、教师核验区。无后端、无网络请求。

开跑参数(能推断就不问)

默认零门槛:一句话直接开工。 逐项判断,能推断的一律不问:

参数 处理
输入方式 自动判断,永不问——上传了文件=底稿模式;没上传=主题直出
是否生成作业 默认不生成——用户明确提到作业/练习/检测才产出,不追问
年级 + 教材版本 请求或底稿里写得有就直接用;只说"三年级"这类不全信息按主流版本(人教版)补齐;完全无法推断时才用 AskUserQuestion 问这一项

用户未指定的细节(课件页数、课时划分、主题色)按教案内容合理自定,不再追问。主题直出且教材口径拿不准时(单元课文、知识点归属),先 WebSearch 核对教材目录再动笔,禁止凭记忆编造课文名。

流程总览

[0] 参数收集(上方契约) → [1] 输入解析 → [2] 建任务目录
  → [3] 教案(先做,是另两件的蓝本) → [4] 课件 → [5] 作业(可选)
  → [6] 验证(jsdom 全绿)→ [7] present_files 一次性交付

输入解析(上传底稿时)

受管 Python 环境通常装不上 python-docx/python-pptx,不要尝试安装,直接用标准库:

  • .docx:zipfile + xml.etree.ElementTree 解 word/document.xml(命名空间 http://schemas.openxmlformats.org/wordprocessingml/2006/main);文本按 <w:p> 段落、表格按 <w:tbl> 行列拼;图片从 word/media/ 解出,经 word/_rels/document.xml.rels 映射回正文锚点。
  • .pptx:解 ppt/slides/slideN.xml,逐页提取文本与图;.html 直接读。
  • 提取的图片放任务 tmp/,最终嵌入用 base64 data URI。

任务目录与产物归集(成品一课一夹)

每任务独立子目录 output/<request-id>/,多科/多次任务不混目录。成品一课一夹、三件同放,用户拿到手的就是一个可直接转发的文件夹:

output/<request-id>/
├── 成品/                        ← 唯一交付目录(present_files 只从这里出;模型零回读)
│   ├── 01-教案-《课题》.docx
│   ├── 02-课件-《课题》.html
│   └── 03-作业-《课题》.html     ← 用户勾选了才生成,没有则缺席
├── stage3/ courseware/ homework/ ← 流水线中间件,归集后可弃
├── tmp/                         ← 上传文档解出的临时件
└── working/                     ← 构建脚本、题库 json、页面模板、测试脚本

归集规则:
- 单节课:成品/ 下直接放三件,命名 01-教案-课题.ext / 02-课件-课题.ext / 03-作业-课题.ext——数字前缀保证按 教案→课件→作业 排序,课题与教案课题一致,剔除 Windows 非法字符 \/:*?"<>|。
- 多课次/多学科同批:成品/ 下每课一夹,夹名 学科·课题(如 英语·Unit 1 Making friends),夹内同样 01/02/03 三件。
- 构建脚本直接把最终产物按最终命名写进 成品/;docx 流水线落在 stage3/ 的属中间件,最后一步移动改名进成品——不要让用户去中间目录里自己拼文件。

必须按学科各自成文件——多学科同批交付时绝不做单文件标签页切换。任何情况下都不做"母版再拆分":单科直出;多科同批也只写各科源片段,共享部分抽 working/base.css/base.js 由构建脚本拼装。

源层铁律:模型只编辑 working/ 源层小文件(base.css/base.js/page-*.html/lesson.md/smoke.js),禁止 Read / Edit 成品/、courseware/、homework/ 里的产物——产物含 base64 图片,回读一次就是几十万 token。改任何东西都改源层后重跑构建脚本。

教案规范(先做,是另两件的蓝本)

  • 教案只写一份 working/lesson.md 作为唯一真源:docx 流水线、课件页结构、作业考点池全部从它取材,禁止在流水线各阶段或课件/作业里重写教案内容。
  • 环节划分直接决定课件页结构与作业考点池,动笔前定稳。
  • 分层作业设计写进教案,作业件从此取材,两处口径一致。
  • 歌词/课文等原文转写不可靠时,明确写"以谱面/教材为准"并附原文出处,不硬编。
  • DOCX 走 tencent-docx 流水线生成;venv 报 No module named 'click' 时按下方『Windows 环境修复』章节重建,不绕开流水线手搓 docx。

课件与作业执行要点

按上方表格所列内联章节执行,编排层额外盯跨件一致性:

核对项 要求
封面信息 教材版本/单元/课时逐字段对齐教案表头
环节顺序与时长 与教案"教学过程"一致
结束页要点回顾与作业文案 = 教案重难点与分层作业
知识点表述 教案、课件、作业三处一致(概念定义、术语不允许各写各的)
作业 LS_KEY 每份唯一(如 hw.mus.rye.l25.v1),换学科/换课必换,防串草稿
旧学科残留 构建脚本断言替换次数,最后 grep 旧学科关键词确认无残留(含教师核验区标签)
干扰项质量 瞄准该知识点真实迷思概念,不随手写错项

验收标准(Definition of Done)

  1. 课件、作业各跑 jsdom 冒烟测试全绿(本机浏览器起不来时不要卡在截图验证;要点见下方课件规范的 jsdom 章节)。作业另跑:node --check 抽出的 <script>;grep 题干关键词确认源码无明文。
  2. 教案 docx:图片嵌入完整、表格不破版、文件体积非 0。
  3. present_files 一次性交付 成品/ 全部产物(按课次文件夹逐件列出),附简短清单说明每件是什么、给谁用(教案=教研自留,课件=课堂投影,作业=发学生)。
  4. 收尾询问可选优化(A4 打印版作业等),不自动做。

失败处理与用户沟通

任何环节失败(docx 流水线报错、jsdom 测试不过、教材检索无结果、构建脚本异常),必须向用户交代三件事,不许只甩原始报错:

  1. 出了什么——一句人话,如"文档转换环境损坏,正在重建"
  2. 影响哪件产物——教案/课件/作业;已完成的件不受影响,先交付已完成部分
  3. 怎么办——自动修复中(指明按哪节排错)/ 需要用户做什么;修复后重新验证再交付

环境兼容与排错

  • 主验证环境:Windows + Git Bash + 受管 Node/Python(jsdom 装于受管 node workspace,NODE_PATH 指向其 node_modules);macOS/Linux 通用,仅路径与 venv 布局差异(POSIX 用 bin/python)。工具调用一律绝对路径。
  • jsdom 通用坑与断言细节见下方两处『jsdom 冒烟测试』章节(课件 / 作业)。

安全与合规

  • 所有产物本地生成、本地落盘;课件与作业零外链,不采集学生个人数据(作业提交码由学生本机生成、自愿发给老师)。
  • 作业防作弊机制须可关闭(顶栏开关),黄标仅提示教师、不自动扣分,把判断权留给教师。
  • 不生成违反教材审定的内容;涉及价值观表述(寄语、评语)保持中性正向。

反模式清单(禁止)

  • ❌ 多学科塞一个 HTML 用标签页切换
  • ❌ 课件 CDN 引依赖、外链图片/音频
  • ❌ 作业默认附带(用户没勾选就不做)
  • ❌ 题库明文写在 HTML 里(必须 Base64 + 构建脚本注入)
  • ❌ 用 Math.random() 打乱选项后存结果(必须 seed 派生的确定性乱序)
  • ❌ JS 运行时拼接图片占位名("IMG"+n 必裂,复用已有 img.src 或存 data URI)
  • ❌ 能推断的参数还去问(输入方式/是否作业永不问;仅年级教材完全无法推断时问一次)

互动课件实现规范

目标不是把教案贴成幻灯片,而是把教案的每个「环节」变成学生能动手的东西。每个环节保留教案批注(教师活动 / 学生活动 / 设计意图)供老师对照。

> 课后作业页(防刷题 + 可保存提交)规范见下方『防刷题作业实现规范』,两者技术栈同源(单文件 HTML、零依赖、jsdom 冒烟测试)。

输出形态

单文件自包含 HTML,零外部依赖(无 CDN、无音频文件、无图片文件),双击即用、可直接发给任课老师。

必须按学科各自成文件,不要做成一个文件里用标签页切换多科——用户要的是能单独分发给语数外老师的独立课件。

源层与成品分离(铁律,压消耗的根本)

只编辑 working/ 源层小文件,禁止 Read / Edit courseware/、homework/、成品/ 里的任何产物(铁律全文与根因见『任务目录与产物归集』——产物含 base64,回读一次就是几十万 token)。源层文件分工:

源层文件 内容 体积
working/base.css / base.js 全套框架(工具函数/render/go/buildJump/键盘/绑定/顶底栏),改一处全局生效 <15KB
working/page-NN.html 页面片段,一页一文件,图片写 src=&quot;IMG1&quot; 占位符,不嵌任何 base64 每页 <4KB
working/smoke.js 参数化测试骨架(传 html 路径 + 期望页数),复用不重写 ~8KB

build.py 是唯一写产物的通道:读源片段 → 图片按 md5 去重(同一图只嵌一次)→ 大图降采样(PNG 线稿转灰度 JPEG q80,75KB 可压到 ~20KB)→ 注入 base64 → 写入 courseware/ 或 成品/。

改版式 = 改源片段 → 重跑 build.py → 重跑 smoke.js。绝不直接改成品——改了下次重跑就被覆盖,且每次 Edit 都吃满 base64。

技术选型(关键决策)

需求 方案 说明
音效(节拍器、倒计时、对错提示) Web Audio API 实时合成 createOscillator 正弦/方波短音。不引入音频文件
雨声/白噪声环境音 Web Audio 白噪声缓冲 + biquadFilter 高低通 + LFO 调幅 2 秒 buffer 循环即可
英语发音 speechSynthesis 单词用 speak();整段连读必须不要在每次调用里 cancel(),否则前一句被掐断
时钟/秒针动画 内联 SVG + setAttribute(&#x27;transform&#x27;,&#x27;rotate(...)&#x27;) 60 刻度用 JS 生成,秒针 requestAnimationFrame 驱动
计时器 performance.now(),不要用 Date.now() 秒表精度要求
移除画布里的灰色占位图 直接写 SVG 矢量插画 教育场景用户明确反感"灰点噪点/无封面"的廉价观感

所有 API 都要做能力探测(window.AudioContext || window.webkitAudioContext、if(!window.speechSynthesis) return),缺失时静默降级,不能抛错。

课件结构

顶栏:学科标识 + 环节跳转下拉 + 教案批注开关 + 全屏放映
进度条
&lt;main id=&quot;deck&quot;&gt;
  &lt;section class=&quot;slide&quot; data-subject=&quot;math&quot; data-title=&quot;环节名&quot;&gt;
    &lt;div class=&quot;s-head&quot;&gt;环节徽章 / 标题 / 时长&lt;/div&gt;
    &lt;div class=&quot;s-body&quot;&gt;…交互组件…&lt;/div&gt;
    &lt;details class=&quot;tip&quot;&gt;教案批注(教师活动/学生活动/设计意图)&lt;/details&gt;
  &lt;/section&gt;
  …
&lt;/main&gt;
底部导航:上一环节 / 页码 / 下一环节
  • 一页 = 教案里一个环节,data-title 用于环节目录下拉。
  • 键盘:← → 空格 翻页,F 全屏,T 切换教案批注。输入框内不劫持按键。
  • 放映模式给 body.present 加 --scale 放大字号并隐藏教案批注;@media print 里全部展开、一页一张。
  • 教学提示必须留在课件里,不是另附文档。

封面页与结束页(默认要加)

课堂打开投影的第一眼是封面,最后一页是收束。默认就带上,别等用户提。

&lt;section class=&quot;slide&quot; data-subject=&quot;X&quot; data-title=&quot;封面 · &lt;课题&gt;&quot;&gt;   ← 第 1 页
  .cover / .cv-hero(accent 渐变 + 顶部 6px 色条)
    .cv-kicker 教材版本  /  .cv-title 大标题  /  .cv-sub 单元·课型  /  .cv-chips 核心素养
  .cv-body  左:.cv-rows 信息表(学科/教材/单元/课型/课时/授课教师·班级·日期留空)  右:学科定制内联 SVG
  .cv-foot  &lt;button class=&quot;btn solid lg&quot; data-goto=&quot;1&quot;&gt;开始上课 →&lt;/button&gt;
&lt;/section&gt;
…
&lt;section class=&quot;slide&quot; data-subject=&quot;X&quot; data-title=&quot;结课 · 谢谢大家&quot;&gt;   ← 最后一页
  .end / .ed-hero(本课结束 / 谢谢大家 / 学科副题)
  .ed-body  h3.sec「本课要点回顾」+ .ed-recall 四张 .ed-card(要点 / 补充)
            + .note 课后作业(呼应教案分层作业)+ .ed-quote 学科寄语
&lt;/section&gt;
  • 翻页跳转统一用 data-goto:$$(&quot;[data-goto]&quot;).forEach(b =&gt; b.addEventListener(&quot;click&quot;, () =&gt; go(+b.dataset.goto)))。多科同批时各科文件共用同一段绑定,靠 cur 自动落到当前学科第 2 页。
  • 封面/结束页不加 details.tip(它们不是教学环节)。
  • 封面插在学科分隔注释之后、结束页插在下一个学科注释(或 &lt;/main&gt;)之前——这样按学科切片时自动带上,不必改拆分逻辑。
  • 章节信息全部学科定制:教材版本、单元、课时、核心素养维度、寄语各不同;授课教师/班级/日期留空给老师手填。
  • hero 用 linear-gradient + radial-gradient 叠 color-mix(in srgb, var(--accent) …),随主题色变量自动变色,无需按学科重复写 CSS。

统一页面尺寸(默认要做)

各页内容量天然不等,若不管,翻页时画面会忽高忽低。做法是锁 16:9 作高度下限,短页补满、长页自然高——不要用整体缩放,投影场景字号可读性优先。

.slide.active{display:flex; flex-direction:column; min-height:var(--slide-h,660px)}
.slide.active &gt; .s-head{flex:none}
.slide.active &gt; .s-body{flex:1 1 auto; display:flex; flex-direction:column; justify-content:center}
.slide.active &gt; details.tip{flex:none}
/* 封面/结束页没有 .s-body 这一层,必须单独补,否则撑高后底部留白 */
.slide.active &gt; .cover,.slide.active &gt; .end{flex:1 1 auto; display:flex; flex-direction:column}
.cover &gt; .cv-body{flex:1}
.end &gt; .ed-body{flex:1; display:flex; flex-direction:column; justify-content:center}
@media print{.slide{min-height:0!important} .slide .s-body{display:block!important}}
function fitStage(){
  var deck = $(&quot;#deck&quot;); if(!deck) return;
  var w = deck.clientWidth - 40;               /* 减去 main 左右各 20px 内边距 */
  if(w &lt;= 0) return;
  document.documentElement.style.setProperty(&quot;--slide-h&quot;, Math.round(w*9/16) + &quot;px&quot;);
}
fitStage();
window.addEventListener(&quot;resize&quot;, fitStage);
window.addEventListener(&quot;orientationchange&quot;, fitStage);
/* fullscreenchange 里也要调:body.present 把版心 1240 拉到 1500,必须重算 */
  • .s-body 用 flex:1 1 auto(不是 flex:1),靠 min-height:auto 让长页自然撑开。
  • 打印媒体查询里若忘了复位 min-height,一页一张会错乱。

常用交互组件(按学科选配)

语文
- 生字翻卡:.flip 点击翻面显示拼音/组词;会认字与会写字分开两页
- 总起句揭示:点段落卡揭示该段首句,最后汇总出句式规律
- 比喻/拟人配对:.match 左点右点,data-key 比对
- 色卡:点色块高亮,把「五彩缤纷」变成眼前的颜色
- 换词比较:两个词点选后给出表达效果的评语,不只是对错
- 仿说造句器:&lt;select&gt; 选词拼句 + 朗读;仿写工作台带 textarea 与一键复制
- 背诵提示:逐句揭示,先图后文

数学
- 倒计时:大数字 + 每秒音效,收尾用和弦
- 节拍器:setInterval 不行,要用 AudioContext.currentTime 前瞻调度(while(next &lt; ctx.currentTime + 0.2) + requestAnimationFrame)才能不漂移
- 估时挑战:开始后隐藏读数(否则学生偷看),按停后显示实际用时与误差,并给评价与记录表
- 秒针走一圈:提供「6 秒演示」与「真实 60 秒」两档;分针同步走一小格,让学生自己推出 1 分=60 秒
- 秒表:启动/停止/归零
- 单位选择:情境化选择题,每题给「为什么」的解析
- 概念可视化组件库(v1.0.7 沪教数学三上实战沉淀,其他学科可类比):
- 递等式逐步揭晓 .eqbox/.eqline:每行一行等式 + .why 理由标签(乘除步标「份总关系」、加减步标「部总关系」),点击走一行(bindEq:step 从 −1 起,beep(520+step*110) 音阶上行,末行双音庆祝)。把「先算哪一步」从讲解变成逐步揭幕
- 运算顺序点选 .pickrow .tok:算式整体做成可点 token,data-want=&quot;1&quot; 是该先算的部分;点对变绿弹礼花、点错抖红 0.9s 并追问「求出的是什么」。比选择题好——学生点的是算式本身而非答案
- 双算法对比 .dualgrid/.dualbox:三列(正确算法 / 从左往右陷阱 / 意义不清变体),data-ok + data-why,点击即判并在 .dualnote 显示说理。「冲突辨析」环节标配
- 关系模型卡 .relgrid/.relcard:两卡双色(份总/部总),data-rule 点击朗读,.hot 高亮当前分析到的一步
- 情境购物图 .scenebox/.good:emoji 商品卡(价格 .pr + 数量 .qt)+ 题目文本 .scenetxt,替代 AI 生图——纯 CSS 零成本且文字精确
- 分层闯关 .lvlgrid/.lvl:三关并排(⭐/⭐⭐/⭐⭐⭐),每关 .lvq 题面 + .lvp 选项 + .lvn 计数器,答对过一关
- 提示卡 .hintcard:虚线橙框口诀卡,闯关卡底部放「卡住就看这里」
- 计数圆点 .dotgrid:份总关系的可视化(几个几就点几组点)

英语
- 节奏拆句:beatbox 逐拍高亮 + 逐拍发音,先慢后快
- 角色对话:两个 &lt;select&gt; 换人名,动态生成台词 + 朗读
- 找朋友:随机名字卡 + 60 秒倒计时 + 控场提醒
- 造句器:输入自己的名字生成台词
- 星星贴纸:给同伴加星

低龄卡通化组件库(低龄版优先用,v2 实战验证)
- 贴纸卡 .sticker:大 emoji(58px)+ 大字(26px),一点即发音,hover 抬起旋转。把「听一听」按钮换成它,互动感立刻上来
- 集星正反馈:.flip.read 贴星 → #starBar 进度更新 → 集满自动 confetti(46) 礼花。跟读任务从"老师要求读"变成"我要集齐星星"
- 随机抽朋友 pairUp():点击随机抽 2 个角色高亮(.role.pair),抽中者上台对话。比"老师点名"参与感强得多
- 描红动画 .trace:SVG path + stroke-dasharray:var(--len) + drawit 动画,点卡片重播笔顺。书写顺序从"看不见"变成"可反复看"
- 卡通化三要素:贴纸白描边(border:4px solid #fff; outline:3px solid #ffd9a8)+ 底部实体阴影(box-shadow:0 8px 0 rgba(...),不要模糊阴影)+ 幼圆字体栈。模糊阴影是"精致中透露简陋"的主因
- 英文必须用独立儿童体且放字体栈首位:中文栈里的英文字形会被中文字体抢先渲染——幼圆自带的拉丁字母又瘦又呆(用户会嫌"英文字体不好看")。正确做法:font-family:&quot;Comic Sans MS&quot;,&quot;幼圆&quot;,&quot;YouYuan&quot;,...——Comic Sans 无中文字形,英文走它、汉字自动落幼圆,一处改动全局生效。Windows 自带候选:Comic Sans MS(圆润胖乎乎,首选)、Century Gothic(几何圆体,a/g 单层结构与手写一致)、Segoe Print(手写印刷体)、Ink Free(钢笔手写,太瘦不推荐)。可用 ls C:/Windows/Fonts/ 探测(Add-Type 与 reg.exe 常被安全策略拦截,别走那两条路);fontTools 可核对字体是否真含中文字形(ord(&#x27;你&#x27;) in cmap)
- 文字密度红线:低龄版每页可见文字 ≤ 150 字(正文,不含折叠的教案批注);教案内容全部移进 &lt;details class=&quot;tip&quot;&gt; 折叠抽屉,投影时只见画面不见说教

物理
- 缩放舞台(模型建构):&lt;input type=&quot;range&quot;&gt; 驱动 SVG group 的 transform=&quot;translate(x,y) scale(s)&quot;,把「贴近看细节」连续过渡到「只剩一个点」。用于质点、点电荷等从具体到抽象的建模环节——比直接给定义有说服力得多。收尾叠一个色点标记 + 概念文字标签(切 opacity),下方解说文案分 3 档同步切换(别一档到底)
- 参考系切换:同一运动的两种描述。&lt;svg data-ref=&quot;car|ground&quot;&gt; + CSS svg[data-ref=&quot;ground&quot;] .traj-p{opacity:1} 控制两条轨迹(竖直直线/抛物线)显隐;车厢位移用 @keyframes 做往返。切换时同步替换说明文案
- 参数对比判断:刻意选同一对象、不同问题、结论相反的组合(地球公转可/自转不可;排球判断明显出界可/压线不可;小车过杆不可/绕圈可)。这是打破「大小决定论」这类前概念最有效的一组题,比正面讲解管用
- 音效沿用 Web Audio;物理课不需要语音合成,别为了「和语文英语对称」硬加 speechSynthesis

交互绑定的坑(踩过的)

  1. 选择题必须按 .q-opts 分组绑定,不要按 .q-row。卡片里直接写 .q-opts 的题目不在 .q-row 内,按行绑定会漏掉,点了没反应。
  2. 翻卡/纯发音卡要拆开判断:if(f.dataset.say) speak(...); if(!f.dataset.nof) f.classList.toggle(&#x27;on&#x27;)。写成 if(f &amp;&amp; !f.dataset.nof){...} 会把发音也一起挡掉。
  3. buildJump(); render(); bindAll(); 是首屏启动行。拆分/搬运脚本时极易把它丢掉,症状是打开即白屏(没有 .slide.active);若只写 buildJump(); render(); 漏掉 bindAll(),页面能显示但所有翻卡/音频/星星/歌词点击全部无反应(jsdom 里表现为「组件存在但点击无效果」类断言全挂——这是 bindAll 缺失的特征指纹,与绑定选择器写错是两回事)。
  4. 单科文件的初始学科状态要从 document.body.dataset.subject 读,不要硬编码 &quot;chinese&quot;,否则数学/英语文件的页码显示成 1 / 0、环节目录为空。
  5. 加配对时给初始选中做 clearSel() 干净切换;.m-stat 要 grid-column:1/-1 才不会挤进第二列。
  6. 动态生成的按钮(如换角色后重绘的 🔊)依赖事件委托(document 上监听 [data-speak]),不能逐个 addEventListener。
  7. 封面/结束页的存在会改变页码与目录长度:加完必须同步更新断言里的期望页数,并新增「首页有 .cover、末页有 .end、封面页 #prev 禁用、点 data-goto 后落在 .s-head 页且 #pg=2 / N、目录首项含「封面」末项含「结课」」这几条,否则漏渲染不会暴露。
  8. 选择题答错后再答对,要清掉残留的 .wrong:if(ok){ opts.forEach(o =&gt; o.classList.remove(&quot;wrong&quot;)); btn.classList.add(&quot;right&quot;); … }。否则同一张卡片会同时挂着红绿两种状态,投影上看很乱。
  9. buildJump() 若依赖页面列表,必须自取 SLIDES=$$(&quot;.slide&quot;),不要依赖 render() 先行填充——启动行 buildJump(); render(); 里 buildJump 先执行,此时 SLIDES 还是空数组,症状是环节目录下拉为 0 项、跳转失效(jsdom 断言 #jump.options.length 可拦住)。
  10. AI 生图(ImageGen)多张并行调用会同秒撞名互相覆盖——返回同一个 localPath,目录里只剩最后一张。多张插图必须逐张串行生成,每张落盘后立即改名归档再生成下一张;不放心时 Read 图片确认内容与用途一致。
  11. 页面片段里的内联 &lt;script&gt; 先于框架执行:build.py 把 base.js 注入在 body 之后,片段内的 script 排在前面,直接调 confetti() 等框架函数会 ReferenceError。正确姿势:片段里只做标记(如 b.setAttribute(&quot;data-bye&quot;,&quot;1&quot;)),在 base.js 的 bindAll() 里统一绑定。
  12. 节拍器歌词数组相邻项不能重复:[&quot;Hello!&quot;,&quot;Hello!&quot;,...] 前两拍文案相同,屏幕上看起来像卡住不动。滚动歌词必须保证相邻项不同(测试断言:连点 6 拍收集歌词,相邻两拍不重复 且去重数 ≥ 4)。
  13. SAPI TTS 输出格式必须实读验证,不能声明什么信什么:设置 SAFT22K16BITMONO 后 SAPI 可能仍输出 8bit 立体声 12kHz(wave 模块读出 sampwidth=1/ch=2)。若按声明的 16bit mono 拼接字节,波形会被错误重组放大 256 倍、整体削平(Flat factor 74、削平点 3.6 万处),听感"炸"。正确流程:合成后用 wave.open 实读 nchannels/sampwidth/framerate,按实际格式解码(8bit 无符号以 128 为中心,减 128 去直流,×256 升 16bit)再拼接。验收硬指标:Flat factor = 0、Peak ≤ −3 dBFS。
  14. 配音 + BGM 混音配方(ffmpeg 滤镜链):旁白链 highpass=f=110 → acompressor(threshold=-18dB:ratio=3) → alimiter(limit=0.90) → volume=-2dB;BGM 链 lowpass=f=9000 → volume 微调 → afade 入/出;侧链闪避 [vox]asplit=2[voxout][voxkey]; [bgmin][voxkey]sidechaincompress=threshold=0.02:ratio=4:attack=10:release=260[bgduck];amix=weights=1 0.75:normalize=0;最后 loudnorm=I=-16:TP=-1.5:LRA=9 统一响度。BGM 电平要比旁白低约 18dB 才是"轻量伴奏"感;验收分段测音量(说话段 vs 间歇段差 ≥ 15dB 说明闪避生效)。
  15. BGM 无版权做法:不引用外部音乐文件,用 Python 程序化合成(正弦叠加 + ADSR 包络模拟钟琴/八音盒,C 大调 I-vi-IV-V 和弦进行 + 主旋律,120BPM),峰值规范化到 −20dBFS。零依赖零版权,音色贴合低龄课件。
  16. 动画视频帧高度必须是偶数:libx264 + -pix_fmt yuv420p 要求宽高都能被 2 整除,1200×675 直接报 height not divisible by 2。用 1200×676(或加 scale=trunc(iw/2)*2:trunc(ih/2)*2 滤镜兜底)。
  17. SAPI 中文旁白两个坑:① SpMemoryStream.GetData() 返回的是裸 PCM 字节(无 RIFF 头),wave.open 直接报 "file does not start with RIFF id"——必须用 SpFileStream.Open(path, SSFMCREATEFORWRITE) 让 SAPI 自己写 WAV 文件;② 中文旁白要显式选声音:遍历 v.GetVoices() 按 Huihui/Yaoyao/Kangkang 匹配,否则默认英文声音念中文是拼音怪调。
  18. 提交码前缀长度与 slice 偏移必须同步:前缀从 HW1:(4 字符)改成 MHW1:(5 字符)时,verify() 里的 slice(0,6)/slice(6) 若没同步改成 slice(0,5)/slice(5),核验永远报"要以 MHW1: 开头"。改前缀就 grep 一次所有 slice。
  19. 视频时长对齐旁白:时间轴 JSON 中转:先合成旁白并实测各段时长,写 timeline.json({total, starts, durs});视频脚本与 BGM 脚本读同一份 JSON 决定总长与幕次边界(T1=starts[1]-0.25)。顺序千万别反——先定视频时长再配音必然对不上。
  20. 冒烟测试的时序陷阱:测试脚本自己先点的组件会改变页面状态——eqbox 点过 3 次后断言"初始未点亮"必然失败;[data-goto] 点击会跳页,键盘导航断言前必须先用目录 #jump 归位到已知页。断言测的是"当前状态"不是"初始状态"。
  21. ⚠ base.js 里绝不能重新声明 var AUDIO = {};(最隐蔽的哑音 Bug):框架脚本排在注入脚本之后执行,一句空对象声明会把 build.py 注入的全部音频数据清零,症状是「所有音频按钮点了毫无动静、页面不报错、无控制台错误」。第一次交付就被这个坑坑到——用户反馈"很多伴奏没声音",实为全部没有。正确写法只有一句守卫:if(typeof AUDIO===&quot;undefined&quot;){ var AUDIO={}; }。验收时必须断言运行时数据(Object.keys(window.AUDIO).length 等于预期段数 + 每段 base64 >100KB),只断言"HTML 里含 data URI / 含音频键名"这类源码级检查会全部通过却完全听不到声音。
  22. 音频播放层按这个规格写,别偷懒:① 单例播放器——document.createElement(&quot;audio&quot;) 挂到 body(含 playsinline),不要每点一次 new Audio();② 源用 Blob(base64→atob→Uint8Array→new Blob([u8],{type:&quot;audio/mpeg&quot;})→createObjectURL),带 URL.createObjectURL 能力探测降级到 data URI——超长 data URI 在移动端/微信内核里容易哑火,jsdom 则没有 createObjectURL,正好用它验证降级分支;③ 按钮要有三态:▶ 播放 / ❚❚ 播放中 / ⚠ 失败提示,并在按钮内显示 0:12 / 1:44 进度(timeupdate 事件);④ play() 必须 p.catch(...) —— 自动播放策略拦截时提示"再点一次"而不是静默失败;⑤ 同时只允许一个按钮处于播放态。症状鉴别:用户说"没声音"时,先分三步验证——文件本身(volumedetect 看 mean/max dB)、内嵌完整性(解码回文件对 md5)、运行时对象(第 21 条的断言),三层都过才轮到怀疑浏览器兼容。
  23. 课件里"有内容就该有按钮":音频/视频必须覆盖到每个需要它的环节——原稿里只要有音频的页面,课件对应环节都要有可点播放;同一段素材被多条练习共用时(如 m/u/lu 三条练声曲共用一段示范),每条练习各自配按钮并标注来源,不要只在页面角落塞一个"练声示范"。原稿中的大体积视频(数十 MB)抽音频轨压缩后嵌(-vn -ac 1 -b:a 64k,70MB 视频 → 484KB 音频),视频本身不入课件。
  24. 原版截图用 PowerPoint COM 导出(本机有 Office 时最保真):$pres=$ppt.Presentations.Open($src,$true,$false,$false); $pres.Export($outDir,&quot;PNG&quot;,1600,900),一次导出全部页。适用场景:总结性/板书性页面(时值等式、五音表、谱例、课堂小结)直接用原版截图保机构标准讲法,课件在旁边叠互动按钮。media 提取单图(分镜图、角色立绘)比整页截图更清晰,用 _rels/slideN.xml.rels 解析页→媒体映射后再挑。
  25. AI 生图保 IP 一致:用 image1 传原版角色图 + input_fidelity 控制相似度;生成文件名是截断的 prompt,不可读——按工具返回结果里的时间戳映射语义名(调用顺序=时间戳顺序)。风格统一靠同一段风格描述词(如 "3D cartoon render style, soft warm lighting, children's storybook illustration")在每条 prompt 里重复。
  26. "AI 视频"用 TTS 剧场替代,不生成视频文件:剧场组件 = 场景图(Ken Burns 缓推)+ 底部逐句字幕高亮 + TTS 旁白链式朗读(onend 接下一句)。台词直接写在 DOM 字幕里,框架启动时从 .th-sub .ln 收集,不要在页面片段里写内联 script(它们先于框架脚本解析,会引用未定义的全局)。
  27. 课件加 TTS 朗读必须走三层降级(完整实现见 webpage-neural-tts skill):Edge 云神经音色 → 浏览器云音色(Chrome 的 Google 音色)→ 本机系统音色;要点:cancel() 后隔 80ms 再 speak、onerror 兜底本机、音色列表按语言筛选、设置存 localStorage、window.TTS_INFO 记录当前层。jsdom 无 speechSynthesis,测试时在 beforeParse 注入桩(getVoices:()=&gt;[] 等),测 natPick 时再替换成假音色表。本机音色降级时给用户提示一次,别让人以为坏了。
  28. 绝对不要并行 Edit 同一个文件:多个 Edit 并发读写会互相覆盖(后写者胜,先写的改动静默丢失),本次导致 stampGate 补丁丢失、测试连挂两轮才定位到。对同一文件的多处修改:串行,或一次 Edit 用足够大的 old_string 覆盖全部改动点。
  29. 课件布局默认「视口锁定 + 固定画布等比缩放」,页面绝不滚动(课堂投影上下翻页是事故):body overflow:hidden + flex column(顶栏/进度/底栏占固定高度),中间 #deck 剩余空间放 #stage(固定 1280×720 设计稿,position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(var(--s))),JS 只做一件事:--s = min(deck宽/1280, deck高/720)。页面片段全部按 1280×720 排版,内容放不下拆页而不是压缩字号(11 页→13 页拆法:剧情+分镜+简谱混排的页各拆一层)。slide 内元素测量超不超画布别信 jsdom(无布局引擎),用 Edge 无头 + iframe 包装页对每页 getBoundingClientRect 实测 bottom/right,超 720/1280 即 OVER。
  30. .th-img 类自适应图片框别用 aspect-ratio+max-height 组合:Chromium 会把 max-height「迁移」成宽度约束导致元素莫明收窄(右侧露底色),且行为随上下文变化难预测。直接写死 width:100%;height:300px + img object-fit:cover,页内需要不同高度就内联覆盖 height——完全确定性。另一个收缩坑:flex column 容器里子项写 margin:0 auto 居中会退化为 fit-content 宽度(auto margin 吸收空间后 stretch 失效),必须同时给 width:100%。
  31. Edge 无头测量「闭合 &lt;details&gt; 内容」有假阳性:closed details 的子元素不渲染但 getBoundingClientRect 仍返回布局位置,逐页测高时会把不存在的溢出算进来(所有页同报 OVER、worst 全是批注网格)。测量脚本必须跳过闭合 details 的所有后代(沿 parentElement 链找 tagName===&#x27;DETAILS&#x27; &amp;&amp; !open)。

交付前必做:jsdom 冒烟测试

浏览器守护进程在某些机器上起不来,不要卡在截图验证上。用 jsdom 把每个可点元素点一遍,比手动点更彻底。

测试骨架沉淀在 working/smoke.js,参数化复用(node smoke.js &lt;html路径&gt; &lt;期望页数&gt;),换课件只传参不重写。每换学科新增的交互元素才往骨架里补断言,补完骨架本身也归档复用。

> 真实浏览器截图验证:agent-browser 守护进程在本机起不来,不要反复尝试;但 Edge 无头截图是通的——&amp; &quot;C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe&quot; --headless=new --disable-gpu --window-size=1280,1400 --screenshot=out.png &quot;file:///ASCII路径.html&quot;(两次调用间 Start-Sleep 4)。三个前提:① 文件必须放 ASCII 路径(中文路径下打不开);② 要截内页,在 &lt;/body&gt; 前注入 &lt;script&gt;go(N);&lt;/script&gt; 同步跳页——用 setTimeout/hash 会在截图时机之后才执行,截到的永远是首页;③ 无头模式缺中文字体图标会显示为方块,属正常,布局判断不受影响。

# 一次性准备(隔离工作区,勿污染用户环境)
cd &quot;C:/Users/&lt;u&gt;/.workbuddy/binaries/node/workspace&quot;
node &quot;C:/Program Files/nodejs/node_modules/npm/bin/npm-cli.js&quot; install jsdom

> 装前先查:jsdom 多数机器上已装在 workspace 的 node_modules 里,先用文件系统探测 &lt;workspace&gt;/node_modules/jsdom/package.json 是否存在,存在就直接用 NODE_PATH=&lt;workspace&gt;/node_modules 跑测试,不要重复 install——受管环境下 npm install 可能无输出挂死 10 分钟以上(PATH 缺 coreutils 时尤甚),杀掉任务即可,已装的包不受影响。

测试要点:
- beforeParse(w =&gt; { w.scrollTo = () =&gt; {}; }) —— jsdom 未实现 scrollTo,否则首屏渲染就报错
- 加载:new JSDOM(html, { runScripts:&quot;dangerously&quot;, pretendToBeVisual:true, virtualConsole, beforeParse })
- 断言首屏状态:.slide.active 恰好 1 个、#pg 等于 1 / N、#jump.options.length 等于 N。这三条正好能抓住上面第 3、4 条坑
- 逐个切换页面并点击 .flip, .opt, .star, .sw, .beatbox, .m-item, [data-speak], .btn,每个点两次覆盖双态分支
- 跳过 #btnFull(全屏 API 在 jsdom 中不存在)
- 涉及布局的代码要单独断言:jsdom 里 clientWidth 恒为 0,布局函数会提前 return。用 Object.defineProperty(deck,&quot;clientWidth&quot;,{value:1240,configurable:true}) 注入真实宽度,再 window.dispatchEvent(new Event(&quot;resize&quot;)),然后断言 documentElement.style.getPropertyValue(&quot;--slide-h&quot;)。这验证的是算式与写回位置,不是像素级还原,但足以拦住"函数没被调用/算错分母"这类错
- &lt;script&gt; 被外部编辑器注入 data-page-node-id 属性时,构建前用 re.sub(r&#x27;\s+data-page-node-id=&quot;[^&quot;]*&quot;&#x27;, &#x27;&#x27;, html) 清掉;产物若再冒出此属性是文件预览面板回写的,重跑构建脚本即可,属无害属性

NODE_PATH=&quot;C:/Users/&lt;u&gt;/.workbuddy/binaries/node/workspace/node_modules&quot; node smoke_test.js *.html

复用已有源层做框架(换学科最快路径)

新学科课件不必从零写:从上一任务的 working/ 里直接搬源层——base.css(&lt;style&gt; 块全量)、base.js(工具函数/render/go/buildJump/键盘/.q-opts 绑定)、尾段(data-goto/底栏/全屏/resize/buildJump();render();)、顶栏底栏骨架。不要从成品 HTML 里抽——那是含 base64 的大文件,读它本身就烧 token。注意:

  • 抽出来先 re.sub(r&#x27;\s+data-page-node-id=&quot;[^&quot;]*&quot;&#x27;, &#x27;&#x27;, …) 清编辑器属性
  • 谱例/插图用占位符(src=&quot;IMG1&quot;)写在页面片段里,构建脚本统一替换成 base64 data URI;JS 里绝不能运行时拼占位名(img.src = &quot;IMG&quot;+n 替换不到,图片必裂)——要么用隐藏 &lt;img&gt; 复用其 .src,要么把 data URI 存进 JS 对象
  • 学科主题色只需在新 CSS 段覆盖 --accent/--accent-2/--accent-soft/--accent-ink 四个变量
  • 冒烟断言里的「16:9 舞台高度」期望值 = (版心宽 − 40) × 9/16(1240 版心 → 1200×9/16 = 675px)

防刷题作业实现规范

交付形态是单文件自包含 HTML:学生双击打开就能做题,做完点提交会生成一段「提交码」,复制发给老师即可;老师把提交码粘回同一页面的「教师核验区」就能看到全部作答与过程记录。全程不需要任何后端。

配套的互动课件见上方『互动课件实现规范』章节,两者技术栈相同,可同一份教案产出两件。

一、先想清楚防的是什么

刷题的四种路径,以及对应的机制。四条要一起上,缺一条就有明显缺口:

学生的路径 对应机制 为什么有效
反复做同一份卷,记住「第 3 题选 B」 变式池:同一考点准备多个情境,每次随机抽取 题面每次都变,背的答案对不上
记住字母位置 选项乱序:正确项在 A/B/C/D 各位置均匀分布 记字母无效
只记结论不记理由 判据双答:每题要答「判断」+「判断依据」两处,各占一半分 只选对结论只得 5/10,蒙不出来
一遍遍试错直到全对 提交前不判分:作答过程中不给任何对错反馈 没有反馈就无法逼近答案
拿到卷子先翻答案/搜题 题库 Base64 编码存放 + 禁选中禁复制 提高成本,非密码学防线,配合前三条足够
事后改答案、换个卷重刷 过程留痕:改答次数、切屏次数、换题次数、总用时随成绩单一并上报 老师看得见,且「换卷无收益」——各变式难度等价

关键设计前提:变式之间必须难度等价(同一考点的不同情境,判据结构相同)。否则学生靠换题挑软柿子,机制就废了。

二、题目结构:判据双答

客观题不要做成「单选一题一答」,做成两问:

【情境】研究地球绕太阳公转一周所用的时间 —— 能否把地球看成质点?
  ① 你的判断(5 分)    A 可以看成质点   B 不能看成质点
  ② 你这样判断的依据(5 分)
     [正确] 地球的直径远小于公转轨道半径,各部分运动差异对所研究问题的影响可以忽略
     [干扰] 地球的体积那么小,小物体才可以看成质点          ← 针对「大小决定论」迷思
     [干扰] 因为地球在做圆周运动,做曲线运动的物体都能看成质点
     [干扰] 质点就是数学里的一个点,任何物体都可以直接看成一个点  ← 针对「质点=几何点」迷思

干扰项必须瞄准真实迷思概念,不要随手写错的。设计时先列出这个知识点学生最容易犯的 3 个错,每条干扰项对应一个。这样这份作业同时也是诊断工具——看错选项就知道是哪种误解。

主观题要点:
- 设字数下限(实时显示「已 N 字 / 至少 M 字」,未达标变色)
- 设举例禁用名单(banWords):要求"举一个本节课没出现过的例子",题干里用过的情境都列入名单,检测到就标给老师看
- 主观题不自动判分,只报完成度,写清「20 分由老师评阅」

三、技术架构

作业页(单文件)
├─ 题库:const POOL_ENC = &quot;&lt;Base64&gt;&quot;      ← 明文 5KB → 编码后 ~14KB
├─ 解码:自写 b64→bytes→utf8             ← 不依赖 atob / TextDecoder
├─ 出卷:paper = { seed, A:[sid...], B:[sid...], C:sid }
│        · 抽题与选项顺序全部由 seed 派生
│        · paper 只存「种子 + 选中的 sid」,不存答案
├─ 渲染:从题库 + seed 现算选项顺序
├─ 判分:同样的算法再算一遍 → 比对
├─ 状态:localStorage 存 { paper, ans, behavior, history }
└─ 提交码:HW1: + Base64(JSON)

3.1 确定性乱序(核心算法)

必须用可复现的伪随机,不能用 Math.random() 打乱后把结果存下来——那样只能存明文答案,或者存了顺序却还原不出正确项。

function hashSeed(s){ let h=2166136261; for(let i=0;i&lt;s.length;i++){ h^=s.charCodeAt(i); h=Math.imul(h,16777619);} return h&gt;&gt;&gt;0; }
function makeRng(seed){ let a=seed&gt;&gt;&gt;0; return function(){ a=(a+0x6D2B79F5)|0;
  let t=Math.imul(a^(a&gt;&gt;&gt;15),1|a); t=(t+Math.imul(t^(t&gt;&gt;&gt;7),61|t))^t; return ((t^(t&gt;&gt;&gt;14))&gt;&gt;&gt;0)/4294967296; }; }
function shuffled(arr, seedStr){ const r=makeRng(hashSeed(seedStr)), a=arr.slice();
  for(let i=a.length-1;i&gt;0;i--){ const j=Math.floor(r()*(i+1)); const t=a[i]; a[i]=a[j]; a[j]=t; } return a; }

选项顺序 = shuffled([正确项, ...干扰项], paper.seed + &quot;|&quot; + 题号 + &quot;|&quot; + sid),
正确位置 = shuffled(...).indexOf(正确项)。渲染算一次、判分再算一次,两次必然一致。

好处:localStorage 里只有种子和 sid,没有任何答案信息;刷新后题目和选项顺序完全不变。

3.2 utf8 安全的 Base64

浏览器 btoa 不支持中文,atob 在 jsdom 里也可能缺失。自己写,30 行,无处不兼容:

var B64C = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;;
// b64ToBytes / bytesToUtf8 / utf8ToBytes / bytesToB64 / encodeText / decodeText
// 注意 4 字节 UTF-8 分支要正确合成代理对,否则 emoji 会炸

3.3 提交码

&quot;HW1:&quot; + encodeText(JSON.stringify({
  v:1, hw:题库ID, bv:版本, n:姓名, c:班级, s:学号,
  t:时间戳, at:第几次提交, ob:客观得分, ot:客观满分,
  d:[[题号, 部分, 判断对否, 依据对否, 得分], ...],
  sub:[{k:题号, dv:判断, big:&quot;&quot;, small:&quot;&quot;, tx:&quot;主观原文&quot;}, ...],
  bh:{ sec:用时秒, blur:切屏次数, blk:拦截次数, ed:改答次数, rd:换题次数 },
  seed:卷种子, p:卷面
}))
  • 一段包含全部信息(含主观题原文)约 2000 字符,微信里能发
  • 键名用短名压缩长度
  • 教师核验区粘贴 → 解码 → 渲染完整档案;解析失败要给友好报错,不能崩

3.4 必做:把「保存」做扎实

  • localStorage 每次作答后 debounce 700ms 自动保存,顶栏显示「已保存 HH:MM:SS」
  • 页面 beforeunload 再存一次
  • 刷新后必须完全还原:同一份卷(seed 不变)、每道题的选项选中态、主观题原文、学生信息
  • 没存储时要能降级到内存对象(try/catch 包住 localStorage),别让隐私模式下整页崩掉
  • 保存 ≠ 提交:提交是显式动作,提交后锁定全部控件、显示成绩单、生成提交码;刷新后仍能看到上次成绩单

四、行为留痕清单

记录项 实现 成绩单上如何呈现
总用时 Date.now() - startedAt 秒表;< 5 分钟标黄
切屏 visibilitychange + document.hidden 计数 次数;≥3 次标黄
复制/快捷键拦截 contextmenu/copy/cut/keydown(Ctrl+C/X/P/S/U、F12),preventDefault() 并计数 次数;>0 标黄
改答 同字段值变化时 edits++ 次数;≥6 次标黄
首次作答时间 记录 firstAt,与渲染时间比对 可用于识别「秒答」
换题 「换一套题」按钮计数 次数;>0 标黄

黄标项在成绩单里写一句「提示(供老师参考,不作为扣分依据)」,把判断权留给老师,不要自动扣分。

防作弊要可关闭(顶栏开关,老师演示时关掉),关闭后 body.noguard 放开 user-select。

五、标签与组件样式

  • 选中态 .opt.sel(靛蓝实底白字);提交后 .sel-ok(绿)/ .sel-bad(红)/ .miss(绿框,标出漏掉的正确项)
  • 下拉答错要在旁补一行「正确答案:xxx」——下拉框本身没法染色标正确项
  • 纯 CSS、不依赖任何图片;打印样式里隐藏顶栏/工具/教师区

六、交付前必做:jsdom 冒烟测试

测试骨架沉淀在 working/smoke_homework.js,参数化复用(传 html 路径 + 题库 json 路径),换学科只传参不重写;只有新增题型时才往骨架补断言。

浏览器守护进程在部分机器上起不来,不要卡在截图验证上。 用 jsdom 把整条链路跑通:

const dom = new JSDOM(html, { runScripts:&quot;dangerously&quot;, pretendToBeVisual:true,
  url:&quot;http://localhost/hw&quot;,                     // 不设 url 则 localStorage 不可用
  virtualConsole: vc,                            // 捕获 jsdomError 与 console.error
  beforeParse(w){ w.scrollTo=function(){}; } }); // scrollIntoView/scrollTo 在 jsdom 不存在

断言清单(本项目实测 66 条,可作骨架):

  1. 纯逻辑(不开 DOM):同 seed 可复现;不同 seed 结果不同;抽题覆盖池内全部变式;正确项在 4 个位置的分布均匀(这条直接证明乱序生效)
  2. 首屏:题量、进度 0、成绩单隐藏、无任何得分标记、无任何对错高亮、源码里搜不到题干明文
  3. 作答:点击选中态、进度更新、草稿落盘、改答计数、首次时间
  4. 行为:右键/复制被 preventDefault(用 cancelable:true 的事件查 defaultPrevented);切屏计数
  5. 校验:信息未填拦截、未答完二次确认并报剩余数量
  6. 判分:全对得满分、全错得 0 分(这两条一起才能证明判分与乱序映射一致)
  7. 提交码:生成 → 教师核验区解析 → 身份/得分/主观原文一致;非法码友好报错
  8. 刷新恢复:把 localStorage 内容塞进新 JSDOM 实例的 beforeParse,验证选项/下拉/标签/文本全部还原
  9. 换题:卷面变化、计数 +1、提交状态复位

测试脚本必须自己复制一遍 hashSeed/makeRng/shuffled,用它从 paper.seed 反推每道题的正确答案位置——页面内部变量在闭包里,外部拿不到。

测试脚手架的两个坑

  • 自动保存有 700ms 防抖:断言 localStorage 前先点一下「保存草稿」按钮强制落盘,否则读到的是上一次状态
  • 脚本末尾必须 process.exit(0) + 关掉所有 JSDOM 实例(window.close()):页面里的 setInterval 计时器会让 Node 进程永不退出,被外部超时 SIGTERM 杀掉时块缓冲的 stdout 会整段丢失,表现为"测试跑到一半就没输出了"

七、构建流程

题库不要直接写在 HTML 里(手写 1200 行 HTML 容易出引号错误),用三步:

working/hw_bank.json      题库明文(便于人工核对)
working/hw_template.html  页面模板,题库位置写 __POOL__ 占位
working/build_homework.py 读 JSON → Base64 → 替换占位 → 输出到 homework/

同源层铁律(见上):只改 json/模板再重跑构建脚本,绝不 Read / Edit homework/、成品/ 里的产物。

生成后立刻做两件事:node --check 抽出的 &lt;script&gt;(能抓住引号不配对这类致命错误),以及 grep 题干关键字确认源码里搜不到。

八、踩过的坑

  1. 引号开双闭单:&#x27;...&#x27; + x + &quot;]&#x27;) 这种拼接,第三个字符串写成 &quot;]&#x27;(开双引号闭单引号),解析器会把后续整段吞进字符串,报错位置却在很远的地方(missing ) after argument list)。排查方法:逐行统计单/双引号出现次数,奇数的那行就是嫌疑,比读二分法快得多。
  2. 复杂属性选择器拼接(&#x27;.opt[data-&#x27; + f + &#x27;&quot;]&#x27;)极易写错,且 else 分支往往根本不会执行 —— 优先用 closest(&quot;.grp&quot;) 这类结构定位,少拼选择器。
  3. 用 d.body.textContent 做文案断言会把 &lt;script&gt; 里的代码也匹配进去(模板字符串里常有同样的中文),要限定到具体容器(如 #paper)。
  4. 加「正确项提示」这类只在提交后出现的 DOM,要确认对应容器存在锚点元素(下拉框要 append 到 .sel-q),否则静默失败。
  5. 每新加一种题型(下拉、标签),记得同步 paintPaper 的标记逻辑和 qmark 锚点,否则那一部分永远没有得分标记。

九、跨学科复用模板的替换清单

模板带一批学科硬编码文案,换学科时逐条替换并在构建脚本里断言替换次数 == 1,最后 grep 断言无旧学科词残留:

  • &lt;title&gt;、顶栏 brand、页脚来源行
  • A 部分标题/导语/题干拼串(物理是 能否把X看成质点?,含 it.sc.object 字段)/ data-v 两个按钮文案 / 成绩单 myV、okV / 错题归纳与全对文案(x.part === &#x27;A&#x27; ? … 三元)
  • B/C/E 部分标题与导语、E 的 textarea placeholder(句式模板)
  • 教师核验区主观题标签:所选模型、大物体举例、小物体举例、模型迁移 —— 这四处藏在 verify() 里最容易被漏掉
  • LS_KEY 必须换(物理是 hw.phy.*),否则同一台机器做过多学科作业的学生会互相串草稿
  • 表单占位(年级/班级示例)

注意:核验表是紧凑格式([key, part, vOk, rOk, score]),只显示 ✓/✗ 和得分,不含作答原文——断言别去找答案文本,找主观题标签即可。


Windows 环境修复(tencent-docx venv)

症状

在 Windows 上跑 tencent-docx 插件流水线的 Stage 3(doc-converter → html-to-docx)时:

  1. 直接调用转换报错:ModuleNotFoundError: No module named &#x27;click&#x27;
  2. bash &lt;plugin_root&gt;/scripts/wb/local/setup-html-to-docx.sh 看起来跑通了(打印 ✓ venv ready),但跑完 ~/.venv-html-to-docx 目录凭空消失
  3. 手动 uv venv 时输出 Activate with: C:\c\Users\&lt;user&gt;\.venv-html-to-docx\Scripts\activate —— 这是误建路径的信号

根因

setup-html-to-docx.sh 是按 POSIX venv 布局写的(判断和调用都用 $VENV_DIR/bin/python),但 Windows 上 uv venv 产出的是 Scripts/python.exe。于是:

# 脚本第 86 行:Windows 上永远判定为&quot;venv 不存在&quot;
if [ ! -x &quot;$VENV_DIR/bin/python&quot; ] || ...
    rm -rf &quot;$VENV_DIR&quot;          # ← 把之前建好的 venv 删掉
    uv venv --python 3.12 &quot;$VENV_DIR&quot;
fi
# 第 94 行:PY=&quot;$VENV_DIR/bin/python&quot; 仍指向不存在的路径
# → import 探测全部失败 → uv pip install --python &lt;不存在&gt; 失败

结果:venv 被删、依赖没装、脚本却因为 | tail 吃掉退出码而"看起来成功"。

另外,在 Git Bash 里把 /c/Users/... 这类 MSYS 路径传给 Windows 版 uv.exe,uv 会当成相对路径处理,落到 C:\c\Users\...——所以传路径必须用 C:/Users/... 形式。

修复步骤(PowerShell / Git Bash 通用)

1. 确认 uv 位置

uv.exe 通常已在 C:\Users\&lt;user&gt;\.local\bin\uv.exe(插件自带的 SessionStart hook 装过),不要再 curl 装一遍。

export PATH=&quot;$HOME/.local/bin:$PATH&quot;
uv --version

2. 用 Windows 风格路径重建 venv(关键)

# 注意:必须写成 C:/Users/... ,不能写成 /c/Users/...
uv venv --python 3.12 &quot;C:/Users/&lt;user&gt;/.venv-html-to-docx&quot;

若提示 A virtual environment already exists,加 UV_VENV_CLEAR=1。

3. 安装依赖

cd &quot;&lt;plugin_root&gt;/skills/html-to-docx/scripts&quot;
uv pip install --python &quot;C:/Users/&lt;user&gt;/.venv-html-to-docx/Scripts/python.exe&quot; \
  --only-binary=:all: -r requirements.txt

依赖清单:python-docx html-for-docx beautifulsoup4 lxml httpx Pillow click pytest(20 个包,约 3 秒装完)。

4. 验证

&quot;C:/Users/&lt;user&gt;/.venv-html-to-docx/Scripts/python.exe&quot; -c &quot;import click, docx, bs4, lxml, html4docx; print(&#x27;deps OK&#x27;)&quot;

> 注意:html-for-docx&gt;=1.2 的实际模块名是 html4docx(不是 html_for_docx),转换器源码 from html4docx import HtmlToDocx 也是这个名字。若照旧命令 import 报 No module named &#x27;html_for_docx&#x27;,先查 site-packages 里的真实目录名再判定,不要盲目重装。

5. 执行转换

cd &quot;&lt;plugin_root&gt;/skills/html-to-docx/scripts&quot;
&quot;C:/Users/&lt;user&gt;/.venv-html-to-docx/Scripts/python.exe&quot; -m html_to_docx convert \
  &quot;&lt;input.html&gt;&quot; -o &quot;&lt;output.docx&gt;&quot; --page-size A4 --orientation portrait

成功输出:{&quot;success&quot;: true, &quot;docx_path&quot;: &quot;...&quot;, &quot;warnings&quot;: [], &quot;fields&quot;: []}

> 转换时 stderr 会打印 MarkupResemblesLocatorWarning(bs4 对 URL 样文本的误报),属正常噪声,可忽略。

6. 清理误建目录

若见过 C:\c\Users\...,删掉它:

rm -rf &quot;/c/c&quot;

判定成功的标准

  • Scripts/python.exe -c &quot;import html_to_docx&quot; 无报错
  • 转换 JSON 的 success 为 true
  • 目标 .docx 落盘且体积非 0(三份教案的文档约 70 KB)

备注

  • setup-html-to-docx.sh 是上游插件的幂等脚本,在修好的机器上不要再重复跑——每次跑都会把 venv 删掉(见根因)。跨会话如果转换报 No module named &#x27;click&#x27;,按上面步骤 2–4 重来一遍即可。

版本记录

> 版本号口径:对外发布版本以本表最上方带「发布版」标记的条目为准;其下 v1.0.1–v1.0.7 为内部迭代史(能力并入正文,编号仅供追溯)。

v1.0.3(2026-09-28)· 发布版:可读性优化

  • 针对 SkillHub TRACE 评测 C 规范性 4.5:新增「先读我」读者导航(老师/AI/维护者各读哪节)与「高频黑话速查」表——非技术背景老师只看两节即可使用,无需通读技术规范
  • 针对适用性反馈新增「边界说明」:明确适用学段、不做在线协作/服务器类需求、素材处理口径
  • 版本号对齐发布口径(1.0.1→1.0.3),规范能力无增删

v1.0.1(2026-09-17)· 精修发布

  • 修正舞台高度断言算式(653→675px);清除「合并母版」流程残留措辞;源层铁律/学科独立/jsdom 通用坑去重(单一出处 + 引用);版本记录压缩 55→28 行。全文 57.4KB→54.1KB(−6%),规范能力无删减。

v1.0.0(2026-09-16)· 品牌定版

  • 定名「教学三尺堂(幼小版)」,英文名 Podium Trio · Early Primary Edition(slug: podium-trio;Podium 取「三尺讲台」意象,Trio 指教案/课件/作业三件套),面向幼小衔接与小学低龄教学场景,以此为发布版本打包上架(内部迭代史 v1.0.1 ~ v1.0.7 见下,规范能力等同并入本版)

v1.0.7(2026-09-16)

  • 数学首战(沪教2024三上《两步四则运算》)全套落地:新增数学概念可视化组件库(递等式揭晓/顺序点选/双算法对比/关系模型卡/情境购物图/分层闯关/提示卡/计数圆点,核心思想:把「运算顺序」从讲解变成点击与揭幕)与坑位 16-20(视频高度偶数、SAPI 中文 SpFileStream、提交码 slice 同步、时间轴 JSON 驱动视频时长、测试时序陷阱)。
  • 程序化 PIL 动画代替 AI 视频:概念讲解类视频数字/算式必须精确,程序化绘制 100% 可控且超小体积(182KB);封面仍用 ImageGen。
  • 教材版本歧义(新旧教材两套目录)先 WebSearch 核对,歧义时 AskUserQuestion。

v1.0.6(2026-09-16)

  • 修复音频削波重大 Bug(坑 13):SAPI 声明 16bit mono 实出 8bit stereo,波形放大 256 倍削平(Flat factor 74.4→0)。
  • 新增 ffmpeg 侧链闪避混音 + Python 程序化无版权 BGM 配方(坑 14/15),响度 −16 LUFS,BGM 比旁白低约 18dB。

v1.0.5(2026-09-15)

  • 新增低龄卡通化组件库(贴纸卡/集星礼花/抽朋友/描红)与卡通化三要素;坑:片段内联 script 先于 base.js(data 标记 + bindAll 统一绑)、节拍歌词相邻不重复;文字密度红线 ≤150 字/页。

v1.0.4(2026-09-15)

  • 修正 html4docx 模块名;坑:buildJump 必须自取页面列表、ImageGen 并行同秒撞名须逐张串行;低龄可裁「判据双答」但变式池/乱序/零判分/提交码底线保留(3 选 1 断言分布)。

v1.0.3(2026-09-15)

  • 零门槛开工(参数可缺省,仅年级教材无法推断才问);失败沟通三件事;清理与现行规则矛盾的「母版拆分」旧流程章节。

v1.0.2(2026-09-14)

  • 源层/成品分离铁律 + 教案单一真源 lesson.md;测试骨架参数化沉淀(smoke.js / smoke_homework.js 传参复用)。
  • 禁止「母版+拆分」双份输出;构建脚本图片治理:md5 去重(曾占 26.2%)+ 大图降采样(PNG 线稿→灰度 JPEG q80)。

v1.0.1(2026-09-10)

  • 新增「成品/」唯一交付目录:三件同放、多课每课一夹(学科·课题),01-教案/02-课件/03-作业 数字前缀定序;stage3/courseware/homework 降为中间件;交付清单标明用途。

v1.0.0(2026-09-10)

  • 首个公开发布版本:三件套编排、双输入模式(主题直出/上传底稿)、16:9 单文件课件、防刷题作业四机制 + HW1 提交码、多学科独立文件、jsdom 质量门禁、单文件自包含分发。(能力明细已并入上方产物定义与实现规范,不再重复列出)

常见问题

5 问
答案由本站依据条目字段整理
教学三尺堂(幼小版)怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_0ed12131/podiumtrio。安装方式为未标注。装完刷新技能列表或重开客户端。

教学三尺堂(幼小版)支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

教学三尺堂(幼小版)权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

教学三尺堂(幼小版)收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

教学三尺堂(幼小版)用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 2026-11-06。