Interactive Learn App
从模板生成单文件交互式学习应用,自带 AI 问答、测验、练习、进度保存。
快速启动
用户说"帮我做一个 XX 的学习应用"时,按以下步骤:
- 复制模板:
assets/template.html - 修改
<title> - 填充
CONTENT_CONFIG(见下文) - 填充
AI_CONFIG - 编写
COURSE[]课程数据 - 用浏览器打开测试
模板结构
单 HTML 文件,三大可配置区域 + 固定引擎:
┌─────────────────────────────────────────────────┐
│ <style> 固定 CSS(不要动) │
│ ─────────────────────────────────────────────── │
│ <body> 固定 HTML 结构(不要动) │
│ ─────────────────────────────────────────────── │
│ <script> │
│ 1. CONTENT_CONFIG ← 改:题材基本信息 │
│ 2. AI_CONFIG ← 改:AI 角色和提示词 │
│ 3. COURSE[] ← 改:全部课程内容 │
│ ───────────────────────────────────────────── │
│ 固定 JS 引擎(不要动) │
│ </script> │
└─────────────────────────────────────────────────┘
CONTENT_CONFIG
var CONTENT_CONFIG = {
topic: '课程标题', // 显示在侧边栏顶部
subtitle: '交互式学习系统', // 副标题
codeLang: 'java', // 语法高亮语言:java/python/javascript/text
practiceMode: 'code', // 'code'=写代码 'text'=文字回答
codefixEnabled: true, // 是否启用改错练习
analogyBox: { title: 'MC 类比', icon: '🎮' }, // 类比框标题和图标
pathHighlightRegex: /正则/g, // 代码中文件路径高亮
syntaxHighlight: {
keywords: '...'.split(' '), // 语言关键字
types: '...'.split(' '), // 类型名
singleLineComment: '//',
multiLineComment: ['/*', '*/']
}
};
AI_CONFIG
var AI_CONFIG = {
qaSystemPrompt: '你是 XX 专家...\n回答按以下格式(Markdown):\n## 📋 是什么\n简洁解释。\n## 🔧 怎么用\n代码示例 + 文件路径。\n## ⚠️ 注意事项\n常见坑。\n## 🎮 实际场景\n具体应用。',
quizGenPrompt: '根据课程内容出 3 道选择题,不要和已有题目重复。严格返回 JSON:[{"q":"题目","opts":["A","B","C","D"],"a":0,"explain":"解释"}]。选项不要加 A./B./C./D. 前缀。',
exerciseGenPrompt: '出一道编程练习题。严格返回 JSON:{"prompt":"题目","hint":"提示","answer":"正确代码","review":"带注释的参考代码"}',
explainPrompt: '用简洁中文讲解以下知识点,配合实际例子,300 字以内。',
compressPrompt: '压缩对话为摘要:主题 + 知识点 + 重要代码 + 遗留问题',
quickQuestions: [
{ label: '快捷问题1', text: '问题内容' },
// 4-5 个
]
};
AI 高级功能
模板内置三个 AI 增强功能,通过 QA 面板顶部的开关按钮控制:
流式输出(⚡ 流式)
AI 回答逐字显示,不等待全部生成。使用 SSE(Server-Sent Events)实现:
// 请求体加 stream: true
body.stream = true;
// 用 ReadableStream 逐块读取
var reader = response.body.getReader();
// 解析 data: 前缀的 SSE 行
// delta.reasoning_content → 思考过程
// delta.content → 正式回答
注意:aiCallStream 是独立函数,不要修改原有的 aiCall(出题、改错等功能仍用非流式)。
思考过程可见(💭)
MiMo 模型会先输出 reasoning_content(思考链),再输出 content(正式回答)。
实现方式:
- 流式过程中,
reasoning_content实时显示在折叠区域 - 点击「💭 思考中...」可展开/收起
- 显示字数统计
CSS 样式:
.qa-thinking { background: rgba(155,142,196,0.06); border: 1px dashed var(--lavender); }
.qa-thinking-label { cursor: pointer; } /* 点击展开 */
.qa-thinking-body { display: none; } /* 默认折叠 */
.qa-thinking-body.show { display: block; }
联网搜索(🔍 联网)
使用 DuckDuckGo API(免费、支持 CORS)实现真正的搜索:
// 搜索前:调用 API
var url = 'https://api.duckduckgo.com/?q=' + encodeURIComponent(query) + '&format=json';
// 解析:摘要、相关主题、定义、链接
// 注入到发送给 AI 的上下文中
网页抓取(自动检测 URL)
用户问题中包含 URL 时,自动抓取网页内容:
// 检测 URL
var urls = extractUrls(question); // 正则提取 http(s):// 链接
// 通过 CORS 代理抓取
var proxy = 'https://api.allorigins.win/raw?url=' + encodeURIComponent(url);
// 提取纯文本(去 script/style 等),截取前 8000 字
var text = htmlToText(html);
最多同时抓取 3 个 URL,避免请求过多。
优先级逻辑
有 URL?→ 抓取网页
↓
开了联网?→ 搜索
↓
合并上下文 → 发送给 AI(流式/非流式)
COURSE[] 课程数据
每课结构:
{
id: 1,
title: '课程标题',
phase: 1,
phaseName: '阶段名称',
content: '<h2>💡 概念</h2><p>解释...</p><div class="mc"><h3>🎮 类比</h3>...</div><pre><code>代码</code></pre>',
quizzes: [
{ q: '题目(支持 Markdown)', opts: ['选项A','选项B','选项C','选项D'], a: 1, explain: '解释' }
// 6 道
],
practice: [
{ prompt: '题目', hint: '提示', answer: '正确代码|备选答案', review: '带注释的参考代码' }
// 2-4 个
],
codefix: [
{ prompt: '题目', buggy: '错误代码', fixed: '正确代码', hint: '提示', review: '解析' }
// 2-4 个,可选
]
}
content HTML 格式
- 知识点用
<h2>,带 emoji 前缀 - 类比框用
<div class="mc"> - 代码块用
<pre><code>,逐行中文注释 - 表格用内联样式的
<table> - 不要用外部 Markdown 库,引擎内置
mdInline()处理行内格式
内容质量要求
课程内容必须专业、丰富、饱满,不能只有干巴巴的概念定义:
- 每个知识点都要有完整的代码示例,不能只写文字描述
- 代码示例要带实际应用场景,比如"在 MC 模组中,这通常用于 XXX"
- 类比要具体,不能只说"就像 XX",要展开解释为什么像
- 表格对比要详细,列出类型、用途、代码示例、注意事项
- 测验题目要覆盖细节,不能只考表面概念,要考易错点和实际用法
- 练习和改错要有代表性,选最常见的错误和最实用的写法
测验规则
- 每课 6 道选择题
- 选项不加
A.B.前缀(引擎自动添加) explain可选(答错时显示)- 不交叉出题,每题对应当前知识点
练习规则
answer用|分隔多个正确答案review是带注释的参考代码hint简短一行
改错练习规则
buggy必须有明显错误fixed是修正后的完整代码hint指出方向,不说答案
踩坑清单(必读)
1. 不要用 file:/// 打开
本地 file:// 会阻止外部 CDN 加载(字体、图标等)。必须用 HTTP 服务打开:
# 方法一:Python
python -m http.server 8080
# 方法二:VS Code Live Server
# 方法三:直接部署到 GitHub Pages
2. CDN 在国内可能失效
jsdelivr、cdnjs 在国内加载不稳定。当前模板已内联所有 CSS/JS,不依赖外部 CDN。
如果需要 Mermaid 图表,用 mermaid.ink 在线渲染服务,不要加载 mermaid.min.js:
https://mermaid.ink/img/<base64 编码的 Mermaid 代码>
3. MiMo 模型的 reasoning_content
MiMo 模型会先输出 reasoning_content(思考过程),再输出 content。如果设了 max_tokens,思考过程可能耗尽所有 token,导致 content 为空。
解决方案:不设 max_tokens,让用户自行控制。
4. 语法高亮必须跳过隐藏元素
如果 highlight.js 在练习编辑器之前运行,会修改隐藏的 answer 内容,导致答案比对失败。
解决方案:练习的参考答案硬编码在 JS 中,不从 DOM 解析。
5. 大括号平衡是致命问题
对 <script> 区域做增量编辑时,极易导致 {} 不匹配,整个 JS 报错且难以定位。
解决方案:
- 始终以
assets/template.html为基础 - 编辑后检查大括号平衡
- 如果出问题,回退到模板重新开始,不要试图逐行修复
6. 事件委托优于内联 onclick
内联 onclick 在动态生成的 HTML 中可能失效。用事件委托:
document.addEventListener('click', function(e) {
if (e.target.classList.contains('quiz-option')) { /* ... */ }
});
7. localStorage key 要唯一
不同题材的应用必须用不同的 key,避免进度数据冲突:
var PROGRESS_KEY = '剪辑学习_progress'; // 不要用默认的
var CFG_KEY = '剪辑学习_cfg';
8. API 协议自动检测
模板支持 OpenAI 和 Anthropic 两种协议,通过 URL 自动判断:
- URL 含
anthropic→ Anthropic 协议 - 其他 → OpenAI 协议
9. 代码块用 white-space: pre 不用 pre-wrap
pre-wrap 会折行,破坏树形目录结构。代码块统一用 pre。
10. Mermaid 单行问题
AI 生成的 Mermaid 代码经常是单行,本地渲染会失败。用 mermaid.ink 服务可以自动处理。
11. 切换课程必须回滚顶部
goLesson() 中必须加 window.scrollTo(0,0),否则切换课程后页面停留在底部,用户体验差。
12. AI 测验题目会重复 push
restoreAIQuizzes() 每次切换课程都会被调用,如果直接 push 到 c.quizzes 会导致题目重复。修复方式:先截掉旧的 AI 题目再重新 push。
示例配置
剪辑学习
var CONTENT_CONFIG = {
topic: '视频剪辑', subtitle: '从零开始学剪辑',
codeLang: 'javascript', practiceMode: 'text', codefixEnabled: false,
analogyBox: { title: '生活类比', icon: '🎬' },
pathHighlightRegex: /([A-Z]:\\[^\s<]+|\.mp4|\.prproj|\.aep)/g,
syntaxHighlight: { keywords: 'const let var function return if else for while'.split(' '), types: 'Array Object String Number'.split(' '), singleLineComment: '//', multiLineComment: ['/*','*/'] }
};
3D 建模
var CONTENT_CONFIG = {
topic: '3D 建模', subtitle: 'Blender 建模入门',
codeLang: 'python', practiceMode: 'code', codefixEnabled: true,
analogyBox: { title: '手工类比', icon: '🎨' },
pathHighlightRegex: /([A-Z]:\\[^\s<]+|\.blend|\.obj|\.fbx)/g,
syntaxHighlight: { keywords: 'def class if elif else for while return import from as with try except finally raise yield lambda and or not in is None True False'.split(' '), types: 'int float str list dict tuple set bool bytes type object'.split(' '), singleLineComment: '#', multiLineComment: ['"""','"""'] }
};
英语学习
var CONTENT_CONFIG = {
topic: '实用英语', subtitle: '场景化英语学习',
codeLang: 'text', practiceMode: 'text', codefixEnabled: false,
analogyBox: { title: '中文类比', icon: '📚' },
pathHighlightRegex: /(不可匹配)/g,
syntaxHighlight: null // 禁用语法高亮
};
资源
assets/template.html— 完整模板文件(907 行),复制后直接修改三大配置区域即可references/content-rules.md— 课程内容编写详细规范