From f1e45178d2a7356abdfb1bd5d3932ca4254d9432 Mon Sep 17 00:00:00 2001 From: zhl Date: Tue, 3 Nov 2026 11:54:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E5=9F=B9=E8=AE=AD=E7=A6=BB=E7=BA=BF=E7=BD=91=E9=A1=B5?= =?UTF-8?q?=E5=8F=8A=E4=BA=92=E5=8A=A8=E7=BB=83=E4=B9=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 32 +- README.md | 17 +- ...1129-知识库与腾讯ima教育应用培训PPT大纲.md | 19 +- outputs/index.html | 557 ++++++++++++++++++ outputs/培训练习材料.zip | Bin 0 -> 3455 bytes outputs/网页预览.png | Bin 0 -> 38630 bytes scripts/build.mjs | 2 +- scripts/build_web.py | 164 ++++++ src/deck.json | 180 +----- src/pptx-deck.json | 152 +++++ src/runtime/player.js | 34 ++ src/shell.html | 20 + src/slides/ask-clearly.html | 11 + src/slides/case-exercises.html | 16 + src/slides/case-lesson-materials.html | 10 + src/slides/case-lesson-plan.html | 8 + src/slides/case-missing-evidence.html | 16 + src/slides/case-overview.html | 18 + src/slides/case-question-review.html | 13 + src/slides/case-research-comparison.html | 8 + src/slides/case-research-sharing.html | 16 + src/slides/case-school-process.html | 10 + src/slides/choose-topic.html | 8 + src/slides/closing.html | 16 + src/slides/cover.html | 26 + src/slides/create-library.html | 18 + src/slides/daily-habits.html | 16 + src/slides/how-it-works.html | 26 + src/slides/ima-capabilities.html | 21 + src/slides/ima-interface.html | 19 + src/slides/ima-intro.html | 10 + src/slides/knowledge-base.html | 14 + src/slides/material-problems.html | 19 + src/slides/material-quality.html | 21 + src/slides/practice-checklist.html | 25 + src/slides/practice.html | 21 + src/slides/prompt-template.html | 9 + src/slides/save-knowledge.html | 26 + src/slides/teacher-workflow.html | 21 + src/slides/tool-comparison.html | 8 + src/slides/training-goal.html | 26 + src/slides/verify-sources.html | 14 + src/styles/theme.css | 12 + 43 files changed, 1513 insertions(+), 166 deletions(-) create mode 100644 outputs/index.html create mode 100644 outputs/培训练习材料.zip create mode 100644 outputs/网页预览.png create mode 100644 scripts/build_web.py create mode 100644 src/pptx-deck.json create mode 100644 src/runtime/player.js create mode 100644 src/shell.html create mode 100644 src/slides/ask-clearly.html create mode 100644 src/slides/case-exercises.html create mode 100644 src/slides/case-lesson-materials.html create mode 100644 src/slides/case-lesson-plan.html create mode 100644 src/slides/case-missing-evidence.html create mode 100644 src/slides/case-overview.html create mode 100644 src/slides/case-question-review.html create mode 100644 src/slides/case-research-comparison.html create mode 100644 src/slides/case-research-sharing.html create mode 100644 src/slides/case-school-process.html create mode 100644 src/slides/choose-topic.html create mode 100644 src/slides/closing.html create mode 100644 src/slides/cover.html create mode 100644 src/slides/create-library.html create mode 100644 src/slides/daily-habits.html create mode 100644 src/slides/how-it-works.html create mode 100644 src/slides/ima-capabilities.html create mode 100644 src/slides/ima-interface.html create mode 100644 src/slides/ima-intro.html create mode 100644 src/slides/knowledge-base.html create mode 100644 src/slides/material-problems.html create mode 100644 src/slides/material-quality.html create mode 100644 src/slides/practice-checklist.html create mode 100644 src/slides/practice.html create mode 100644 src/slides/prompt-template.html create mode 100644 src/slides/save-knowledge.html create mode 100644 src/slides/teacher-workflow.html create mode 100644 src/slides/tool-comparison.html create mode 100644 src/slides/training-goal.html create mode 100644 src/slides/verify-sources.html create mode 100644 src/styles/theme.css diff --git a/AGENTS.md b/AGENTS.md index 5c06f95..e58a31a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,16 +1,24 @@ # 演示文稿协作约定 -- 本仓库用于“知识库与腾讯 ima 教育应用”培训;参考仓库为 `speech/Ai_education`。 -- 当前课程大纲:`docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md`,是课程结构、逐页主题与讲解内容的维护依据。 -- 后续新增、删除、调整顺序或修改 PPT/HTML 演示内容时,必须在同一次任务中同步更新大纲,不得只修改演示文稿。 -- 涉及课程内容时,同步检查培训目标、课程安排、页码、页数、时长、案例、练习及成果要求,确保与演示内容一致;仅视觉或排版调整无需虚增大纲版本。 -- 修订大纲时保留原文件名和 `created_at`,递增 `revision`,向 `revisions` 追加修改时间与内容,必要时更新 `summary`。 -- 开始修改前先读取当前大纲;任务完成前检查大纲与实际演示内容的一致性。尚未制作或验证的演示与产品操作应如实注明。 -- 保留工作区中不属于当前任务的改动。默认不提交、不推送。 +- 本仓库用于“知识库与腾讯 ima 教育应用”培训,参考项目为 `speech/Ai_education`。 +- 当前大纲为 `docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md`。后续修改PPT或网页课程内容,必须在同一次任务中同步维护大纲,检查目标、页序、页数、时长、案例、练习与成果要求。 +- 修订大纲保留文件名及 `created_at`,递增 `revision`,追加 `revisions`;仅视觉调整无需虚增大纲版本。 -## 当前制作源文件 +## 网页维护 -- 当前 PowerPoint 的内容与版式源码为 `scripts/build.mjs`,构建生成 `src/deck.json` 标题索引、预览和 `outputs/*.pptx`。 -- 不直接编辑生成结果。内容改动需同步源码、大纲与实际标题索引,重新生成并检查。 -- `materials/` 是培训模拟资料,必须保留模拟标识,不得描述为真实课标、学校制度或产品实测结果。 -- 当前使用的是未登录官方首页截图;登录后的操作仍需授课账号验证,不得将讲师备注中的演示计划表述为已验证。 +- 网页页面源码为 `src/slides/*.html`,每个文件对应一页,含正文、`data-title`、`data-chapter`、`data-note` 和页内样式。 +- 修改前读取 `src/deck.json`,按稳定页面 ID 定位文件,文件名不使用页码。增删及排序由该清单控制。 +- 页内样式选择器以 `:scope` 开头,公共媒体查询与动画放在 `src/styles/theme.css`。公共容器和播放器分别为 `src/shell.html`、`src/runtime/player.js`。 +- 页内脚本由构建器包裹,可使用 `root` 查询本页。不得查询其他页面或在全局保存页内状态。计时器由公共播放器统一维护。 +- 图片放在 `assets/`,页面通过 `src="asset:文件名.png"` 引用;不在页面源码粘贴 Base64。`materials/` 为练习资料,构建时自动内嵌。 +- 不手写页码、总页数、section ID、active 或可见状态。页脚用 `{{PAGE_NUMBER}}` 与 `{{PAGE_COUNT}}`。 +- `python3 scripts/build_web.py --check` 检查;`python3 scripts/build_web.py` 生成 `outputs/index.html` 与ZIP练习包。`outputs/` 为生成结果,禁止直接编辑。 +- `work/` 仅放验证记录或临时工作,不运行历史修补脚本。 + +## PPT与内容一致性 + +- 已提交PPT首版保存在 `outputs/知识库与腾讯ima教育应用培训-v1.pptx`,其旧制作源码为 `scripts/build.mjs`,标题索引为 `src/pptx-deck.json`。不得用PPT构建覆盖网页清单 `src/deck.json`。 +- 当前30页网页对应PPT首版,后续修改课程内容时同步维护网页、大纲及后续发布的PPT版本。历史PPT保留原样,未重新生成的旧版不能声称已同步。 +- 模拟教学与校务资料必须保留模拟标识,不得描述为真实课标、学校制度或产品实测结果。 +- 官方截图为未登录首页,登录后的导入、来源点击、笔记及共享操作仍需授课账号验证。 +- 保留不属于当前任务的改动。默认不提交、不推送。 diff --git a/README.md b/README.md index 95aad9e..ee6b42c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ ## 课程文件 +- [离线网页演示](outputs/index.html) - [PowerPoint 演示文稿](outputs/知识库与腾讯ima教育应用培训-v1.pptx) - [课程大纲](docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md) - [现场练习单](materials/05_现场练习单.md) @@ -14,7 +15,9 @@ ## 维护方式 -课程内容与样式源码位于 `scripts/build.mjs`,页码与标题索引由构建生成至 `src/deck.json`。改动PPT内容时同步更新大纲及版本记录,具体见 [AGENTS.md](AGENTS.md)。生成文件放在 `outputs/`,不要直接修改后遗漏源码和大纲。 +网页页面源码位于 `src/slides/*.html`,每页一个文件;`src/deck.json` 维护稳定页面ID及播放顺序。修改后运行 `python3 scripts/build_web.py --check`,再运行 `python3 scripts/build_web.py`。双击 `outputs/index.html` 即可离线播放,发送这个单文件即可分享。 + +PPT首版源码位于 `scripts/build.mjs`,其标题索引生成至 `src/pptx-deck.json`。改动PPT内容时同步更新大纲及版本记录,具体见 [AGENTS.md](AGENTS.md)。生成文件放在 `outputs/`,不要直接修改后遗漏源码和大纲。 本机重建使用已安装的官方演示文稿运行库。使用该运行库 Node 执行 `scripts/build.mjs`,参数1为仓库绝对路径,参数2为新的输出文件名,例如 `知识库与腾讯ima教育应用培训-v2.pptx`。构建会生成逐页预览、检查文件并导出新版本,保留旧版本。运行库位置见源码开头,换电脑需要调整。 @@ -27,3 +30,15 @@ - 产品参考:https://ima.tencent.com/download 。参考课程:http://gitea.werich.top/speech/Ai_education 。 默认不提交、不推送。 + +## 网页播放 + +- 方向键或空格翻页,Home / End 到首尾页。 +- M:搜索目录,N:当前页讲师备注,F:全屏,R:阅读模式。 +- 手机可左右滑动,阅读模式适合在小屏上查看全文。截图可点击放大。 +- 第14、27页可复制提问模板;第26页有12分钟计时器,切页后继续;第28页可勾选完成清单。 +- “资料”提供五份Markdown材料及ZIP整包下载,全部内嵌在网页内。 +- 备注会显示在当前投影上,按需打开。刷新页面后练习计时和清单重置。 +- 生成文件无网络依赖,访问官网或使用ima需要联网。网页不调用AI、不上传资料。 + +此版仅生成本地网页,尚未发布到公网,也未自动提交或推送。 diff --git a/docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md b/docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md index f1af933..af1e4d2 100644 --- a/docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md +++ b/docs/261103_1129-知识库与腾讯ima教育应用培训PPT大纲.md @@ -1,7 +1,7 @@ --- created_at: 2026-11-03 11:29:02 +0800 type: 面向教育行业的知识库与腾讯 ima 应用培训 PPT 大纲 -revision: 第2次 +revision: 第3次 revisions: - time: 2026-11-03 11:29:02 +0800 version: 第1次 @@ -11,6 +11,10 @@ revisions: version: 第2次 author: GPT-6 content: 完成30页可编辑PPT及逐页讲师备注,同步实际页标题与演示内容,加入五份模拟练习材料,明确未登录官方截图与待验证操作。 + - time: 2026-11-03 11:52:52 +0800 + version: 第3次 + author: GPT-6 + content: 新增与PPT首版对应的30页离线网页,加入目录搜索、讲师备注、阅读模式、截图放大、提问复制、练习计时与资料下载,更新源码维护路径。 summary: 面向中小学教师、教研组长及学校管理人员,设计120分钟知识库与腾讯ima应用培训,通过知识库基础、产品操作、单元备课、练习设计、教研共享、校务查询和现场练习,掌握资料入库、提问、查证与成果沉淀。 --- @@ -303,7 +307,18 @@ summary: 面向中小学教师、教研组长及学校管理人员,设计120 ### 五、演示文稿与材料 - 演示文稿:`outputs/知识库与腾讯ima教育应用培训-v1.pptx`,30 页,16:9,可编辑文字、图形和六张原生表格,含逐页讲师备注。 -- 制作源文件:`scripts/build.mjs`;实际页码与标题索引:`src/deck.json`。 +- PPT 首版制作源文件:`scripts/build.mjs`;其标题索引独立保存在 `src/pptx-deck.json`。网页版页面源码位于 `src/slides/*.html`,稳定页面 ID 与顺序位于 `src/deck.json`。 - 练习资料:`materials/01_主题说明.md`、`02_活动建议.md`、`03_课堂反思.md`、`04_校务办理模拟指南.md`、`05_现场练习单.md`。 - 所有教学与校务案例均为模拟示例,不是 ima 实测输出,也不替代课程标准、教材或学校正式制度。 - 练习与交流 30 分钟细分为:12 分钟动手、8 分钟分享、10 分钟使用习惯与答疑。 + +### 六、网页演示版 + +- 输出文件:`outputs/index.html`。共30页,与已提交PPT首版的课程内容一致,采用原深绿、薄荷绿和浅色版式。 +- 单页源码:`src/slides/*.html`,包含正文、标题、章节、讲师备注及页内样式;顺序由 `src/deck.json` 管理。 +- 公共文件:`src/shell.html`、`src/styles/theme.css`、`src/runtime/player.js`,使用 `python3 scripts/build_web.py` 构建;`--check` 仅检查。 +- 网页功能:键盘与触屏翻页、目录搜索与跳转、讲师备注、全屏、长文阅读模式、截图放大、第14/27页提示词复制、第26页12分钟计时器、第28页完成清单、五份材料单独下载与ZIP整包下载。 +- 网页将图片和练习材料内嵌,可离线播放。官方链接与ima内操作需要联网,网页不会调用AI或上传资料。 +- 练习计时按实际经过时间计算,切页后继续,支持暂停和重置;刷新网页后重置。完成清单仅保存在当前页面会话中。 +- 已在浏览器检查30页文本与表格溢出,核对正文与PPT一致,验证目录搜索、备注、阅读模式、放大、复制、资料下载、跨页计时及清单更新。 +- PPT 首版文件保留不变;以后修改课程内容时,需同步维护网页、大纲及后续发布的PPT版本。 diff --git a/outputs/index.html b/outputs/index.html new file mode 100644 index 0000000..9e0006a --- /dev/null +++ b/outputs/index.html @@ -0,0 +1,557 @@ + +让教学资料用起来 + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

课程目录

30 页,120 分钟

+

讲师备注 · 打开时会显示在投影上

+

现场练习

资料与播放说明

先用材料 01—03 建立“光与影”专题库。材料 04 用于独立的校务查询案例,材料 05 用于记录练习成果。

官方来源与使用说明

官方截图:腾讯 ima 未登录 Web 首页,2026-11-03。产品介绍:腾讯 ima 官网。

本网页可离线播放,不调用 AI,也不上传资料。产品操作需要联网。教学与校务案例均为模拟示例,不能替代课程标准、教材或学校正式制度。登录后的导入、来源点击、笔记和共享操作需在授课前验证。

← → / 空格翻页,Home / End 到首尾页,M 目录,N 备注,F 全屏,R 阅读模式,Esc 关闭弹窗。手机可左右滑动翻页,或切换阅读模式。截图可点击放大。

+

腾讯 ima 官方首页

腾讯 ima 官方未登录首页放大图
+

手动复制提问模板

当前浏览器未允许自动复制,可选择上面的文字手动复制。

+

完成后逐项勾选

你的成果准备好了吗?

已完成 0 / 5 项

+
+ diff --git a/outputs/培训练习材料.zip b/outputs/培训练习材料.zip new file mode 100644 index 0000000000000000000000000000000000000000..a177f79bbe8f95c67fdb4072ad04141dc460df00 GIT binary patch literal 3455 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 literal 0 HcmV?d00001 diff --git a/scripts/build.mjs b/scripts/build.mjs index 7e8f64b..ed99679 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -108,7 +108,7 @@ text(s,'从一个单元开始,建立自己的教学资料库',68,518,670,78,25 await fs.mkdir(path.join(BASE,'build','previews'),{recursive:true}); await fs.mkdir(path.join(BASE,'outputs'),{recursive:true}); -await fs.writeFile(path.join(BASE,'src','deck.json'),JSON.stringify(manifest,null,2)); +await fs.writeFile(path.join(BASE,'src','pptx-deck.json'),JSON.stringify(manifest,null,2)); const draft=path.join(BASE,'build','candidate.pptx'); await (await PresentationFile.exportPptx(p)).save(draft); console.log('Draft exported: '+draft); diff --git a/scripts/build_web.py b/scripts/build_web.py new file mode 100644 index 0000000..bf3ae33 --- /dev/null +++ b/scripts/build_web.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +"""Build the offline deck using only Python's standard library.""" +import argparse +import io +import zipfile +import base64 +import html +import json +import mimetypes +import re +from collections import Counter +from html.parser import HTMLParser +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = ROOT / "outputs/index.html" + + +class Markup(HTMLParser): + def __init__(self, text): + super().__init__(convert_charrefs=True) + self.ids = [] + self.sections = [] + self.resources = [] + self.feed(text) + + def handle_starttag(self, tag, attrs): + attrs = dict(attrs) + if "id" in attrs: + self.ids.append(attrs["id"]) + if tag == "section": + self.sections.append(attrs) + if tag in ("img", "script", "iframe", "video", "audio", "source"): + if attrs.get("src"): + self.resources.append(attrs["src"]) + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def extract(text, tag): + blocks = re.findall(rf"<{tag}>(.*?)", text, re.S) + return re.sub(rf"<{tag}>.*?", "", text, flags=re.S), blocks + + +def scoped_css(css, page_id): + """Require every local selector to begin at this page's root.""" + # Page styles deliberately use ordinary rules; global animations belong in theme.css. + clean = re.sub(r"/\*.*?\*/", "", css, flags=re.S) + rules = re.findall(r"([^{}]+)\{([^{}]*)\}", clean) + require(not re.sub(r"[^{}]+\{[^{}]*\}", "", clean).strip(), + f"{page_id}: 页内样式不支持嵌套规则,请将公共媒体规则放入 theme.css") + for selectors, _ in rules: + require(all(re.match(r"^:scope(?=[\s.#:[>+~]|$)", selector.strip()) + for selector in selectors.split(",")), + f"{page_id}: 每个专用样式选择器必须以 :scope 开头") + require(not re.search(r":scope\s*[+~]", selectors), + f"{page_id}: 页内样式不能选择其他页面") + return css.replace(":scope", f':where([data-page="{page_id}"])') + + +def build(): + ids = json.loads((ROOT / "src/deck.json").read_text()) + require(isinstance(ids, list) and ids, "deck.json 必须是非空页面 ID 列表") + require(all(isinstance(x, str) and re.fullmatch(r"[a-z][a-z0-9-]*", x) for x in ids), + "页面 ID 只能包含小写字母、数字和连字符") + require(len(ids) == len(set(ids)), "deck.json 中存在重复页面 ID") + pages, styles, scripts = [], [], [] + styles.append((ROOT / "src/styles/theme.css").read_text()) + materials = [{"name": p.name, "content": p.read_text()} for p in sorted((ROOT / "materials").glob("*.md"))] + require(len(materials) == 5, "练习资料必须包含5份Markdown文件") + buffer = io.BytesIO() + with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as archive: + for item in materials: + info = zipfile.ZipInfo(item["name"], date_time=(1980, 1, 1, 0, 0, 0)) + info.compress_type = zipfile.ZIP_DEFLATED + archive.writestr(info, item["content"]) + material = buffer.getvalue() + scripts.append("const MATERIALS=" + json.dumps(materials, ensure_ascii=False) + ";") + scripts.append("const MATERIAL_ZIP=" + json.dumps(base64.b64encode(material).decode()) + ";") + scripts.append((ROOT / "src/runtime/player.js").read_text()) + image_types = {} + + def inline_asset(match): + name = match[1] + path = (ROOT / "assets" / name).resolve() + require((ROOT / "assets").resolve() in path.parents, f"图片路径越界: {name}") + require(path.is_file(), f"图片不存在: {name}") + mime = ("image/" + image_types[name] if name in image_types + else mimetypes.guess_type(name)[0]) + require(mime and mime.startswith("image/"), f"无法识别图片类型: {name}") + return "data:" + mime + ";base64," + base64.b64encode(path.read_bytes()).decode() + + for number, page_id in enumerate(ids, 1): + path = ROOT / "src/slides" / (page_id + ".html") + text = path.read_text() + text, page_styles = extract(text, "style") + text, page_scripts = extract(text, "script") + parsed = Markup(text) + require(len(parsed.sections) == 1, f"{path.name}: 必须恰好有一个 section") + attrs = parsed.sections[0] + require("slide" in attrs.get("class", "").split(), f"{path.name}: 缺少 slide 类") + require(all(attrs.get(key) for key in ("data-title", "data-chapter", "data-note")), + f"{path.name}: 缺少标题、章节或讲稿备注") + require(not any(key in attrs for key in ("id", "data-page", "aria-label", "aria-hidden")), + f"{path.name}: 页码、标识和可见状态由构建生成,请移除手写属性") + require("active" not in attrs["class"].split(), f"{path.name}: 不要手写 active") + title = html.escape(attrs["data-title"], quote=True) + text = text.replace("