{"id":"1e47d536-aa92-484d-907b-8f6ec8ace0e4","entityType":"agent","slug":"clawhub-zhouchang1988-codebase-to-course-cn","name":"codebase-to-course-cn","canonicalUrl":"https://www.xpersona.co/agent/clawhub-zhouchang1988-codebase-to-course-cn","canonicalPath":"/agent/clawhub-zhouchang1988-codebase-to-course-cn","generatedAt":"2026-10-11T16:01:20.264Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T13:00:04.874Z","emptyReason":null},"description":"将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。 Skill: codebase-to-course-cn Owner: zhouchang1988 Summary: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。 Tags: latest:1.2.0 Version history: v1.2.0 | 2026-05-21T09:15:07.115Z | user Release v1.2.0 from GitHub v1.1.4 | 2026-05-19T04:32:06.966Z | user Release v1.1.4 from GitHub v1.1.3 | 2026-05-15T02:27:17.173Z | user Release v1.","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s1720bd1njpgkfeygpnhevb7z184dag4:codebase-to-course-cn","sourceUrl":"https://clawhub.ai/zhouchang1988/codebase-to-course-cn","homepage":"https://clawhub.ai/zhouchang1988/skills/codebase-to-course-cn","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/zhouchang1988/codebase-to-course-cn","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/zhouchang1988/skills/codebase-to-course-cn","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。 Skill: codebase-to-cour"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T13:00:04.874Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T13:00:04.874Z","emptyReason":null},"stars":null,"forks":null,"downloads":1061,"packageName":null,"latestVersion":"1.2.0","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T13:00:04.810Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T13:00:04.874Z","lastCrawledAt":"2026-10-11T13:00:04.810Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T13:00:04.810Z","lastVerifiedAt":null,"highlights":[{"version":"1.2.0","createdAt":"2026-05-21T09:15:07.115Z","changelog":"Release v1.2.0 from GitHub","fileCount":17,"zipByteSize":59092},{"version":"1.1.4","createdAt":"2026-05-19T04:32:06.966Z","changelog":"Release v1.1.4 from GitHub","fileCount":16,"zipByteSize":57521},{"version":"1.1.3","createdAt":"2026-05-15T02:27:17.173Z","changelog":"Release v1.1.3 from GitHub","fileCount":16,"zipByteSize":56806},{"version":"1.1.2","createdAt":"2026-05-14T10:34:23.197Z","changelog":"Release v1.1.2 from GitHub","fileCount":16,"zipByteSize":56806},{"version":"1.1.1","createdAt":"2026-05-14T08:03:16.869Z","changelog":"Release v1.1.1 from GitHub","fileCount":16,"zipByteSize":56805},{"version":"1.1.0","createdAt":"2026-05-14T07:13:21.255Z","changelog":"Release v1.1.0 from GitHub","fileCount":16,"zipByteSize":56753},{"version":"1.0.0","createdAt":"2026-05-13T09:08:30.363Z","changelog":"Release v1.0.0 from GitHub","fileCount":16,"zipByteSize":58167}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s1720bd1njpgkfeygpnhevb7z184dag4:codebase-to-course-cn","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T16:01:20.261Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhouchang1988-codebase-to-course-cn/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T13:00:04.874Z","emptyReason":null},"readme":"Skill: codebase-to-course-cn\n\nOwner: zhouchang1988\n\nSummary: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。\n\nTags: latest:1.2.0\n\nVersion history:\n\nv1.2.0 | 2026-05-21T09:15:07.115Z | user\n\nRelease v1.2.0 from GitHub\n\nv1.1.4 | 2026-05-19T04:32:06.966Z | user\n\nRelease v1.1.4 from GitHub\n\nv1.1.3 | 2026-05-15T02:27:17.173Z | user\n\nRelease v1.1.3 from GitHub\n\nv1.1.2 | 2026-05-14T10:34:23.197Z | user\n\nRelease v1.1.2 from GitHub\n\nv1.1.1 | 2026-05-14T08:03:16.869Z | user\n\nRelease v1.1.1 from GitHub\n\nv1.1.0 | 2026-05-14T07:13:21.255Z | user\n\nRelease v1.1.0 from GitHub\n\nv1.0.0 | 2026-05-13T09:08:30.363Z | user\n\nRelease v1.0.0 from GitHub\n\nArchive index:\n\nArchive v1.2.0: 17 files, 59092 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3479b), references/_base.html (2850b), references/_footer.html (61b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (5970b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35313b), skill-card.md (2618b), SKILL.md (13246b), _meta.json (140b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: codebase-to-course-cn\ndescription: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。\n---\n\n# 代码库转课程\n\n将任意代码库转换为精美的交互式课程。输出是一个**目录**，包含预构建的 `styles.css`、`main.js`、每个模块的 HTML 文件以及组装好的 `index.html` — 可直接在浏览器中打开，无需任何设置。\n\n**支持两个版本：**\n- **产品版（默认）**：面向产品经理，聚焦业务逻辑、用户流程和系统架构，不含代码细节\n- **开发版**：面向程序员，包含架构图、数据流动画、技术方案描述\n\n**重要：中国大陆可访问性要求**\n\n此技能面向中国大陆用户，**必须确保生成的课程在中国大陆网络环境下可以完全离线运行**：\n- **禁止使用 Google Fonts** 及任何 Google CDN 资源\n- **禁止使用外部 CDN** 加载字体、样式或脚本\n- **禁止依赖任何需要翻墙才能访问的资源**\n- 所有字体必须使用系统字体或内嵌字体\n- 所有资源必须本地化，确保零网络依赖\n\n## 首次运行互动流程\n\n当技能触发时，按顺序向用户收集以下信息，**全部收集完毕后才开始执行**：\n\n### 第 1 步：选择版本\n\n> 你希望生成哪个版本？\n> 1. **产品版** — 面向产品经理，聚焦业务逻辑、用户流程和整体架构（默认）\n> 2. **开发版** — 面向专业程序员，聚焦架构图、数据流和技术方案\n>\n> 也可以带上版本继续，例如\"将此转换为开发版课程\"。\n\n### 第 2 步：指定关键词\n\n> 请提供一个**中文关键词**和一个**英文关键词**，用于课程命名：\n> - **中文关键词**：用于课程标题和导航显示（例如：\"任务调度\"、\"支付系统\"）\n> - **英文关键词**：用于输出目录名和文件命名（例如：`task-scheduler`、`payment`）\n\n### 输出目录规则\n\n根据用户选择的版本和英文关键词，确定课程的输出目录名：\n- **产品版**：`{英文关键词}-course-pm`（例如：`task-scheduler-course-pm`）\n- **开发版**：`{英文关键词}-course-pro`（例如：`task-scheduler-course-pro`）\n\n**关于测验：**\n- 产品版 **不包含测验**\n- 开发版 **默认不需要测验**，仅当用户明确要求\"增加测验\"时才添加\n\n**关于代码库来源：**\n\n如果用户提供 GitHub 链接，在开始分析之前先克隆仓库（`git clone <url> /tmp/<repo-name>`）。如果他们说\"此代码库\"或类似表述，使用当前工作目录。\n\n**快捷方式：** 用户也可以在一条消息中同时提供所有信息，例如：\n- \"开发版，中文关键词：任务调度，英文关键词：task-scheduler\"\n- \"产品版 / 支付系统 / payment\"\n此时跳过逐步询问，直接开始执行。\n\n---\n\n## 产品版 vs 开发版 对比\n\n| 特性 | 产品版（默认） | 开发版 |\n|---|---|---|\n| **目标受众** | 产品经理、业务人员 | 专业程序员 |\n| **教学方式** | 业务流程图、用户旅程、系统架构概览 | 架构图、数据流动画、技术方案 |\n| **代码展示** | **不展示代码** | 精选10-15行核心代码 + 设计意图 |\n| **测验** | **无** | **默认不需要**（用户要求时才添加） |\n| **视觉元素** | 流程图、业务架构图、用户旅程图、功能模块图 | 架构图、时序图、数据流图 |\n| **语言风格** | 业务语言、产品术语、结构化清晰 | 简洁、信息密度高、技术术语 |\n\n---\n\n## 产品版详细指南\n\n目标学习者是**产品经理和业务人员** —— 需要理解系统做什么、业务逻辑怎么流转、各模块如何协作，但不需要知道代码细节。\n\n**关键原则：**\n- 不展示任何代码\n- 用业务语言解释系统行为，避免技术实现细节\n- 聚焦\"系统做了什么\"而非\"代码怎么写\"\n- 用流程图和架构图可视化业务逻辑\n- 关注用户旅程、数据流转、模块职责\n- 解释清楚各功能模块的输入/输出/边界\n\n**强制交互元素（每个模块必须包含）：**\n- **业务流程图** — 至少一个（展示核心业务逻辑流转）\n- **系统架构概览图** — 整个课程至少2个（模块关系、职责划分）\n- **用户旅程图** — 整个课程至少一个（端到端用户操作路径）\n- **功能模块卡片** — 展示各模块职责、输入输出\n- **数据流转动画** — 整个课程至少一个（业务数据如何在系统中流动）\n\n**禁止包含：**\n- 代码片段（任何形式）\n- 代码翻译块\n- 测验题\n- 技术实现细节（如算法、数据结构、设计模式名称）\n- 词汇表提示（产品经理不需要学习编程术语）\n\n---\n\n## 开发版详细指南\n\n目标学习者是**专业程序员** —— 需要快速理解代码库架构、准备技术分享或代码评审。\n\n**核心原则：图表驱动，代码精简。**\n\n**关键原则：**\n- 信息密度优先，减少\"废话\"\n- 直接使用技术术语，无需解释基础概念\n- **图表和动画优先于代码贴片**\n- 代码仅作为补充（每片段10-15行）\n- 60%+ 图表/动画，25% 简洁文字，15% 精选代码\n\n**测验为可选功能：** 默认不包含。仅当用户明确要求\"增加测验\"或\"需要测验题\"时才添加。\n\n**强制交互元素：**\n- **架构图** — 每个课程至少2个（交互式架构图，点击组件显示描述）\n- **数据流动画** — 每个课程至少2个（请求/响应流转）\n- **时序图/状态图** — 每个课程至少1个\n- **代码分析块** — 整个课程2-3个（不是每个模块都需要）\n\n**内容比例：**\n- 60%+ 图表、动画、交互式可视化\n- 25% 文字描述（技术方案、设计决策）\n- 15% 代码片段（每片段10-15行）\n\n---\n\n## 流程\n\n### 阶段 0：收集参数\n\n在开始前，确认以下三个参数已收集完毕：\n\n| 参数 | 用途 | 示例 |\n|---|---|---|\n| **版本** | 决定内容风格和输出目录后缀 | 产品版 / 开发版 |\n| **中文关键词** | 课程标题和导航显示 | \"任务调度\" |\n| **英文关键词** | 输出目录名和文件命名 | `task-scheduler` |\n\n**输出目录名规则：**\n- 产品版：`{英文关键词}-course-pm`\n- 开发版：`{英文关键词}-course-pro`\n\n如果用户已在首次互动中提供了所有参数，直接记录并继续。如果缺少任何参数，先补充询问。\n\n**记录版本和关键词**，后续所有内容创作都遵循对应版本的规则，输出目录使用规则生成的名称。\n\n### 阶段 1：代码库分析\n\n**产品版分析重点：**\n- 系统整体做了什么（产品定位、核心价值）\n- 主要功能模块及其职责（用业务语言描述）\n- 核心用户旅程（用户怎么用这个系统）\n- 业务数据流转（数据从哪来、到哪去、经过什么处理）\n- 模块间的协作关系（谁调用谁、谁依赖谁）\n- 系统边界（与外部系统的交互点）\n- 关键业务规则和约束\n\n**开发版分析重点：**\n- 架构风格（分层？微服务？单体？事件驱动？）\n- 核心抽象（领域模型、关键接口、主要数据结构）\n- 模块边界（职责划分、依赖关系、通信机制）\n- 数据流路径（从请求到响应的完整链路）\n- 设计决策痕迹（从代码和注释推断\"为什么\"）\n- 技术选型理由（为什么用 X 而不是 Y？）\n- 扩展机制（插件系统？钩子？配置？）\n- 测试策略、部署拓扑\n\n**自己弄清楚应用做什么**，通过阅读 README、主要入口点和核心代码。不要让用户解释产品。\n\n### 阶段 2：课程设计\n\n将课程结构化为 **4-6 个模块**。\n\n**产品版模块结构示例：**\n| 模块 | 目的 |\n|---|---|\n| 1 | 产品全景（系统做什么、解决什么问题、核心价值） |\n| 2 | 功能模块地图（各模块职责与边界） |\n| 3 | 核心用户旅程（端到端操作流程） |\n| 4 | 数据流转（业务数据如何在系统中流动） |\n| 5 | 模块协作（各部分如何配合完成业务） |\n| 6 | 系统边界与扩展（外部集成、未来可扩展方向） |\n\n**开发版模块结构示例：**\n| 模块 | 目的 |\n|---|---|\n| 1 | 架构全景（系统边界、主要组件） |\n| 2 | 核心抽象与领域模型 |\n| 3 | 数据流与请求生命周期 |\n| 4 | 设计模式与实现技巧 |\n| 5 | 扩展点与自定义 |\n| 6 | 技术债务与演进方向 |\n\n**每个模块应包含：**\n- 3-6 个屏幕（模块内流动的子节）\n- **产品版**：至少一个业务流程图或架构概览图、功能模块卡片\n- **开发版**：至少一个架构图或数据流图、至多一个关键代码分析块\n\n**测验要求：**\n- **产品版**：不包含测验\n- **开发版**：默认无测验，仅当用户明确要求\"增加测验\"时才添加\n\n**不要提交课程计划供审批 —— 直接构建它。**\n\n**设计课程计划后，决定使用哪种构建路径：**\n- **简单代码库**（单一用途 CLI、小型库、清晰入口点、5 个或更少模块）→ 直接进入阶段 3 顺序路径\n- **复杂代码库**（全栈应用、多个服务、单体仓库或 6+ 个模块）→ 先进入阶段 2.5，然后阶段 3 并行路径\n\n### 阶段 2.5：模块简报（仅限复杂代码库）\n\n阅读对应版本的参考文件：\n- **产品版**：`references/content-philosophy.md`\n- **开发版**：`references/module-brief-template.md` + `references/content-philosophy-pro.md`\n\n**对于每个模块，将简报写入 `{英文关键词}-course-{pm|pro}/briefs/0N-slug.md`，包含：**\n- 教学目标（学习者应获得什么）\n- 预提取的代码片段\n- 交互元素清单\n- 相关设计决策文档\n\n### 阶段 3：构建课程\n\n课程输出是一个**目录**。\n\n**输出结构：**\n```\n{英文关键词}-course-pm/  （产品版）\n{英文关键词}-course-pro/ （开发版）\n  styles.css       ← 从 references/styles.css 逐字复制\n  main.js          ← 从 references/main.js 逐字复制\n  _base.html       ← 定制的外壳（标题使用中文关键词、强调色、导航点）\n  _footer.html     ← 从 references/_footer.html 逐字复制\n  build.sh         ← 从 references/build.sh 逐字复制\n  briefs/          ← 模块简报（仅限复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 由 build.sh 组装\n```\n\n**步骤 1：设置** — 读取并复制四个基础文件\n\n**步骤 2：定制 `_base.html`** — 替换标题（使用中文关键词）、强调色、导航点\n\n**GitHub 仓库链接处理：**\n- 如果课程来源于 GitHub 仓库（用户提供了 GitHub URL），保留 `_base.html` 中的 `.nav-repo-link` 元素，将 `REPO_URL` 替换为实际的 GitHub 仓库地址\n- 如果课程来源于本地目录或当前项目（非 GitHub），**删除整个 `.nav-repo-link` 元素**\n\n**步骤 3：编写模块** — 根据版本选择参考文件\n\n选择对应的 content-philosophy 文件：\n- **产品版**：阅读 `references/content-philosophy.md`\n- **开发版**：阅读 `references/content-philosophy-pro.md`\n\n同时阅读：\n- `references/gotchas.md` — 常见失败点\n- `references/interactive-elements.md` — 交互元素实现模式\n- `references/design-system.md` — 视觉约定\n\n对于每个模块，编写 `{英文关键词}-course-{pm|pro}/modules/0N-slug.html`，只包含 `<section class=\"module\" id=\"module-N\">` 块及其内容。\n\n**步骤 4：组装** — 从课程目录运行 `build.sh`：\n```bash\ncd {英文关键词}-course-{pm|pro} && bash build.sh\n```\n\n### 阶段 4：审查和打开\n\n运行 `build.sh` 后，在浏览器中打开 `index.html`。引导用户浏览构建的内容，并征求反馈。\n\n---\n\n## 设计身份\n\n视觉设计应该像一个**精美的开发者笔记本** —— 温暖、诱人且独特。\n\n- **产品版**：温暖调色板（米白背景、温暖灰色）、大胆强调色（朱红、珊瑚、青色）、充足留白、信息层次清晰\n- **开发版**：简洁调色板（浅灰背景、深色文字）、专业强调色（蓝、绿、橙）、信息密度高\n\n两者使用相同的 CSS/JS 基础，但通过内容组织和视觉元素来实现不同的体验。\n\n---\n\n## 参考文件\n\n`references/` 目录包含详细规范。**只在到达相关阶段时阅读它们**。\n\n**共享参考文件：**\n- `references/design-system.md` — CSS 自定义属性、调色板、排版\n- `references/interactive-elements.md` — 交互元素实现模式\n- `references/gotchas.md` — 常见失败点\n- `references/module-brief-template.md` — 模块简报模板\n\n**版本特定参考文件：**\n- `references/content-philosophy.md` — 产品版内容规则\n- `references/content-philosophy-pro.md` — 开发版内容规则\n\n## 输出语言\n\n本技能的输出内容默认使用中文。除非用户明确要求其他语言，否则：\n- 文档和说明使用中文\n- 技术术语保持原文或使用中英对照\n\nFile v1.2.0:README.md\n\n# codebase-to-course-cn\n\n> 将任意代码库转换为精美的交互式课程，直接在浏览器中打开，无需任何设置。\n\n**灵感来自 [codebase-to-course](https://github.com/zarazhangrui/codebase-to-course)** — 在原项目思路基础上进行了中文本地化适配和功能增强。\n\n## 它是什么\n\n一个 Claude Code 技能（Skill），能够分析任意代码库并生成交互式 HTML 课程。输出是一个完整目录，包含 `styles.css`、`main.js`、模块 HTML 文件和组装好的 `index.html`，双击即可在浏览器中浏览。\n\n## 两个版本\n\n| | 产品版（默认） | 开发版 |\n|---|---|---|\n| **面向谁** | 产品经理、业务人员 | 专业程序员 |\n| **怎么教** | 业务流程图、用户旅程、系统架构概览 | 架构图、数据流动画、技术方案 |\n| **代码展示** | 不展示代码 | 精选 10-15 行核心代码 |\n| **测验** | 无 | 默认不含，按需添加 |\n| **视觉风格** | 温暖亲切，像翻阅教材 | 简洁专业，信息密度高 |\n\n## 交互元素\n\n- **代码↔中文翻译块** — 左侧代码，右侧通俗解释（开发版）\n- **群聊动画** — 组件之间的对话模拟（产品版）\n- **业务流程图** — 核心业务逻辑流转可视化（产品版）\n- **数据流/消息流动画** — 请求从 A 到 B 的可视化\n- **架构图** — 点击组件查看描述（开发版）\n- **时序图/状态图** — 生命周期与状态流转（开发版）\n- **嵌入式测验** — 多选、场景、拖放、找 bug（开发版，按需添加）\n- **词汇表提示** — 技术术语首次出现时自动标注（产品版）\n\n## 使用方式\n\n在 Claude Code 中触发：\n\n```\n将 ./my-project 转换为课程\n```\n\n或指定版本：\n\n```\n将此转换为开发版课程\n```\n\n或从 GitHub 仓库生成：\n\n```\n从 https://github.com/user/repo 制作课程\n```\n\n## 中国大陆适配\n\n本技能面向中国大陆用户，**所有生成的课程均可完全离线运行**：\n\n- 禁止使用 Google Fonts 及任何 Google CDN 资源\n- 禁止使用外部 CDN 加载字体、样式或脚本\n- 字体使用系统字体回退（PingFang SC、Microsoft YaHei）\n- 零网络依赖，断网也能正常浏览\n\n## 生成的课程结构\n\n```\ncourse-name/\n  styles.css       ← 预构建样式\n  main.js          ← 交互逻辑\n  _base.html       ← 页面外壳\n  _footer.html     ← 页脚\n  build.sh         ← 组装脚本\n  briefs/          ← 模块简报（复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 组装后的完整课程\n```\n\n## 技术细节\n\n- **视觉设计**：教育出版物风格，温暖米白背景（`#FAF7F2`），朱红强调色（`#D94F30`），Catppuccin 语法高亮\n- **布局**：滚动吸附（scroll-snap），逐模块翻页浏览\n- **动画**：滚动触发的淡入效果，渐进式内容揭示\n- **响应式**：支持桌面、平板、手机三种断点\n\n## 与原项目的关系\n\n本项目是 [codebase-to-course](https://github.com/zarazhangrui/codebase-to-course) 的中文本地化衍生版本，主要变更：\n\n- 课程内容默认输出中文\n- 移除所有外部 CDN 依赖，确保中国大陆可访问\n- 字体栈替换为中文系统字体回退\n- 新增产品版和开发版双模式（产品版聚焦业务逻辑，开发版聚焦架构图、数据流动画、技术方案）\n- 交互元素实现模式本地化适配\n\n## 许可证\n\n与原项目保持一致。\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn7a799d479xj5aftb4bqm6ea984cv4m\",\n  \"slug\": \"codebase-to-course-cn\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1779354907115\n}\n\nFile v1.2.0:references/content-philosophy-pro.md\n\n# 内容哲学 — 专业程序员版\n\n> **何时阅读此文件：** 在阶段 3（编写模块 HTML）期间。这些原则指导每个内容决策。\n\n这些原则是将优秀技术课程与普通教程区分开来的关键。\n\n### 信息密度优先\n\n专业程序员阅读速度快，理解能力强。课程应该信息密度高，减少\"废话\"。\n\n**文字原则：**\n- 每个段落有明确信息增量。如果没有新信息，删掉它。\n- 直接使用技术术语，无需解释基础概念。\n- **图表和动画优先于代码贴片。** 能用架构图、数据流动画、时序图表达的，就用可视化。\n- **代码仅作为补充。** 仅在图表无法替代时才展示代码，且每片段控制在 10-15 行。\n- 60%+ 的屏幕应该是图表、动画或交互元素，25% 是简洁的文字描述，15% 是精选代码。\n\n**将描述转换为可视化（优先级排序）：**\n- \"组件 A 调用组件 B\" → **数据流动画**（首选）或**时序图**\n- \"数据从 X 流向 Y\" → **数据流动画**\n- \"系统架构\" → **交互式架构图**（点击组件显示描述）\n- \"请求处理流程\" → **流程图** + **步骤卡片**\n- \"状态变化\" → **状态图动画**\n- \"这个函数的核心逻辑\" → **精简代码片段（10-15 行）+ 设计意图注释**（仅在可视化不足以表达时使用）\n\n**避免的做法：**\n- 不要贴超过 15 行的代码块 — 如果需要展示更多代码，拆分为多个小片段，每个片段聚焦一个概念\n- 不要连续放置两个代码块而中间没有可视化元素\n- 不要用代码来展示可以用图表表达的关系和流程\n\n### 代码分析块 — 精选且聚焦\n\n面向专业程序员的代码分析，重点不是\"这段代码做什么\"（他们能看懂代码），而是\"为什么要这样设计\"。**一个课程中只需要 2-3 个代码分析块**，每个聚焦最核心的设计决策。\n\n**代码选取原则：**\n- 每片段 **不超过 10-15 行** — 选取最能体现设计意图的核心代码\n- 优先选取：接口定义、核心数据结构、关键算法的核心逻辑\n- 不选取：配置代码、样板代码、工具函数、导入语句\n\n**结构：**\n- 左侧：精选的核心代码（保持格式，允许水平滚动）\n- 右侧：设计意图注释（不是逐行翻译）\n\n**示例：**\n\n```html\n<div class=\"translation-block animate-in\">\n  <div class=\"translation-code\">\n    <span class=\"translation-label\">CODE</span>\n    <pre><code>\n<span class=\"code-line\"><span class=\"code-keyword\">export class</span> <span class=\"code-function\">EventBus</span> {</span>\n<span class=\"code-line\">  <span class=\"code-keyword\">private</span> listeners = <span class=\"code-keyword\">new</span> <span class=\"code-function\">Map</span>&lt;string, Set&lt;Function&gt;&gt;();</span>\n<span class=\"code-line\"></span>\n<span class=\"code-line\">  <span class=\"code-function\">subscribe</span>(event: string, fn: Function) {</span>\n<span class=\"code-line\">    <span class=\"code-keyword\">if</span> (!<span class=\"code-keyword\">this</span>.listeners.has(event)) {</span>\n<span class=\"code-line\">      <span class=\"code-keyword\">this</span>.listeners.set(event, <span class=\"code-keyword\">new</span> Set());</span>\n<span class=\"code-line\">    }</span>\n<span class=\"code-line\">    <span class=\"code-keyword\">this</span>.listeners.get(event)!.add(fn);</span>\n<span class=\"code-line\">  }</span>\n<span class=\"code-line\">}</span>\n    </code></pre>\n  </div>\n  <div class=\"translation-english\">\n    <span class=\"translation-label\">DESIGN INTENT</span>\n    <div class=\"translation-lines\">\n      <p class=\"tl\"><strong>为什么用 Map + Set？</strong></p>\n      <p class=\"tl\">Map 提供 O(1) 事件查找，Set 自动去重，防止同一监听器被多次注册。这是比数组更高效的选择。</p>\n      <p class=\"tl\"><strong>扩展点：</strong>可替换为 WeakMap 避免内存泄漏，或添加优先级支持。</p>\n    </div>\n  </div>\n</div>\n```\n\n**关键：**\n- 代码保持原始格式，**允许水平滚动**（与 cn 版本不同）\n- 右侧解释\"为什么\"，不是\"什么\"\n- 指出设计权衡、扩展点、潜在陷阱\n\n### 架构图 — 课程的核心视觉元素\n\n架构图是课程中**最重要的元素**。每个课程必须包含至少 2 个架构图。使用交互式架构图（点击组件显示描述）。\n\n**架构图类型：**\n- **组件图** — 展示模块边界和依赖关系\n- **层次图** — 展示分层架构（表现层、业务层、数据层）\n- **部署图** — 展示服务拓扑和数据流\n\n**设计原则：**\n- 清晰的边界和箭头方向\n- 使用颜色区分不同类型的组件\n- 标注关键数据流路径\n- 包含图例\n- **每个组件可点击**，显示职责描述和关键接口\n\n### 数据流动画 — 追踪请求生命周期\n\n数据流动画是仅次于架构图的第二重要元素。每个课程至少 2 个。展示请求从入口到响应的完整旅程。\n\n**内容：**\n- 入口点（路由、控制器）\n- 中间件链\n- 业务逻辑层\n- 数据访问层\n- 外部服务调用\n- 响应返回路径\n\n**动画化：** 使用 `data-steps` 属性创建分步动画，让学习者逐步追踪数据流。\n\n### 技术方案描述 — 简洁文字替代大段代码\n\n对于技术方案和实现细节，优先使用**简洁的文字描述 + 可视化**，而不是贴大段代码。\n\n**描述模式：**\n- **方案概述** — 用 2-3 句话说明技术方案的核心思路\n- **关键决策** — 用提示框（callout）标注为什么选择这种方案\n- **实现要点** — 用编号步骤卡片（step-cards）列出关键实现步骤\n- **核心代码** — 仅在上述方式无法充分表达时，展示 10-15 行最核心的代码\n\n**示例（好的方式）：**\n> 认证系统采用 JWT + Refresh Token 双 token 方案。Access Token 有效期 15 分钟，存储在内存中；Refresh Token 有效期 7 天，存储在 HttpOnly Cookie 中。Token 验证通过中间件统一处理，失败后自动尝试刷新。\n>\n> [数据流动画：用户请求 → 中间件验证 → Token 过期 → 刷新流程 → 重新请求]\n\n**示例（差的方式）：**\n> [贴 50 行 auth middleware 代码]\n> [贴 30 行 token refresh 代码]\n> [贴 20 行 cookie 设置代码]\n\n### 测验（可选功能）\n\n**重要：测验默认不包含。** 仅当用户明确要求\"增加测验\"或\"需要测验题\"时才添加。\n\n测验应该测试学习者能否**应用**知识解决新问题，而不是回忆事实。\n\n**测验类型（按价值排序）：**\n\n1. **设计决策题** — \"如果要添加 X 功能，你会选择哪种方式？为什么？\"\n   ```\n   题目：团队决定添加一个批量导出功能，预期数据量可能达到 100 万条。\n   你会选择哪种实现方式？\n\n   A. 同步导出，直接在内存中处理后返回文件\n   B. 异步任务队列，处理完成后发送邮件通知\n   C. 流式处理，边读边写，不占用太多内存\n\n   正确答案：C 或 B（取决于具体需求）\n   解释：同步导出会超时；异步适合大规模但需要用户等待通知；\n   流式处理内存效率最高，但需要实现复杂的流控逻辑...\n   ```\n\n2. **架构修改题** — \"如果要将单体拆分为微服务，你会如何划分边界？\"\n\n3. **问题诊断题** — \"用户报告 X 功能变慢，根据你学的架构，可能是什么原因？\"\n\n4. **代码定位题** — \"如果要修改 Y 行为，你需要修改哪些文件？\"\n\n**不测验的内容：**\n- 文件名记忆\n- 语法细节\n- 可以通过 Ctrl+F 找到的事实\n\n### 设计笔记提示框\n\n使用提示框标注重要的设计决策和权衡：\n\n```html\n<div class=\"callout callout-info\">\n  <div class=\"callout-title\">设计笔记</div>\n  <p>这里使用策略模式而不是 if-else，是为了支持未来添加新的支付方式而不修改核心逻辑。这是开闭原则的应用。</p>\n</div>\n```\n\n**类型：**\n- **设计决策** — \"为什么这样设计\"\n- **权衡说明** — \"选择了 A 而不是 B，因为...\"\n- **陷阱警告** — \"注意：这里容易出错\"\n- **扩展点** — \"如果要添加 X，可以在这里扩展\"\n\n### 技术术语处理\n\n专业程序员了解基础术语，但可能不熟悉：\n- 项目特定的术语和缩写\n- 特定框架/库的概念\n- 公司内部约定\n\n**处理方式：**\n- 项目特定术语：首次出现时用词汇表提示\n- 通用技术术语：直接使用，不解释\n- 框架特定概念：简要说明与项目的关联\n\n### 模块间的技术深度递进\n\n模块之间的深度应该递进，可视化复杂度也应递进：\n\n1. **模块 1-2**：架构层面 — 交互式架构图、组件关系图、层次图，用文字描述整体设计思路\n2. **模块 3-4**：实现层面 — 数据流动画、时序图、精选核心代码片段（2-3 个），用步骤卡片描述关键流程\n3. **模块 5-6**：进阶层面 — 状态图、场景对比图，用提示框讨论扩展策略和技术债务\n\n不要在早期模块深入代码细节，也不要在后期模块重复架构概念。\n\n### 实用性导向\n\n每个模块都应该回答一个实用问题：\n- \"我在哪里添加新功能？\"\n- \"我如何定位 bug？\"\n- \"我如何理解这个错误？\"\n- \"我如何扩展这个系统？\"\n\n如果模块不能帮助学习者解决实际问题，重新设计它。\n\nFile v1.2.0:references/content-philosophy.md\n\n# 内容哲学 — 产品经理版\n\n> **何时阅读此文件：** 在阶段 2.5（编写模块简报）和阶段 3（编写模块 HTML）期间。这些原则指导每个内容决策 —— 展示什么、如何解释以及用什么方式呈现。\n\n这些原则是让产品经理真正看懂系统的关键。\n\n### 核心原则：业务语言，零代码\n\n产品经理需要理解\"系统做了什么\"和\"业务逻辑怎么流转\"，不需要知道\"代码怎么写\"。\n\n**绝对禁止：**\n- 任何代码片段（不论长短）\n- 代码翻译块\n- 技术实现细节（算法名称、数据结构、设计模式术语）\n- 编程术语的词汇表提示\n- 测验题\n\n**语言规范：**\n- 用业务语言描述系统行为：\"用户提交订单后，系统自动校验库存\"而非\"调用 checkInventory() 方法\"\n- 用\"模块\"、\"服务\"、\"功能\"等产品经理熟悉的词汇\n- 避免\"类\"、\"函数\"、\"接口\"、\"中间件\"等编程术语\n- 可以提及技术组件的名字（如\"数据库\"、\"缓存\"、\"消息队列\"），但只解释它在业务中的角色\n\n### 展示，不要讲述 —— 极致视觉化\n\n产品经理习惯看流程图和架构图。课程应该更像产品文档而不是技术文档。\n\n**文字限制：**\n- 每个文字块最多 **3-4 个句子**。如果你在写第五句，停下来把它转换为视觉元素。\n- 每个屏幕必须 **至少 60% 是视觉内容**（流程图、架构图、卡片、模块关系图）。\n\n**将文字转换为视觉元素：**\n- 步骤描述 → **流程图**（带清晰的开始/结束/分支）\n- \"模块 A 和模块 B 协作\" → **系统架构图**（带箭头和标注）\n- \"这个功能包含 X、Y、Z\" → **功能模块卡片**（图标 + 一句话描述）\n- \"用户先做 A，再做 B\" → **用户旅程图**（带步骤编号）\n- \"数据从 X 流到 Y\" → **数据流转动画**\n- 列表描述 → **带图标的卡片网格**\n- 条件逻辑 → **决策树/分支流程图**\n\n**视觉呼吸空间：**\n- 在元素之间使用充足的间距\n- 在全宽视觉元素和窄文字块之间交替，创造节奏\n- 每个模块应该至少有一个\"英雄视觉\" —— 一个主导屏幕的图表或交互元素\n\n### 业务流程图 — 课程的核心\n\n业务流程图是产品版课程中**最重要的元素**。每个模块至少一个。\n\n**流程图类型：**\n- **用户操作流程** — 用户从开始到完成某个目标的完整路径\n- **业务逻辑流程** — 系统内部业务规则的执行顺序和分支\n- **审批/状态流程** — 数据或实体的状态变迁\n- **异常处理流程** — 出错时系统的应对策略\n\n**设计原则：**\n- 用业务语言标注每个节点（\"校验用户身份\"而非\"auth middleware\"）\n- 分支条件用业务条件表述（\"库存充足？\"而非\"inventory > 0\"）\n- 标注关键的输入和输出\n- 使用颜色区分正常路径和异常路径\n\n### 系统架构概览 — 让PM看到全景\n\n用简化的架构图展示系统的\"大图\"。不需要技术细节，只需要模块边界和职责。\n\n**展示内容：**\n- 各功能模块的名称和一句话职责\n- 模块之间的调用/依赖关系（用箭头和简短标注）\n- 外部系统的集成点\n- 数据存储的位置和用途\n\n**不展示：**\n- 具体的技术实现（框架名、库名）\n- 代码层面的依赖关系\n- 部署细节\n\n### 用户旅程图 — 端到端的产品体验\n\n从用户的视角描述使用系统的完整流程。\n\n**结构：**\n- 明确的起点（用户意图）和终点（目标达成）\n- 每一步标注：用户做了什么 → 系统做了什么 → 用户看到什么\n- 标注关键的决策点和分支路径\n- 标注可能出错的地方和系统的应对\n\n### 功能模块卡片 — 职责一目了然\n\n每个模块用一张卡片展示其核心信息。\n\n**卡片包含：**\n- 模块名称（业务名称，不是代码文件名）\n- 一句话职责描述\n- 核心功能列表（3-5 条）\n- 与其他模块的关系（谁给它数据、它给谁数据）\n\n### 数据流转 — 讲清楚\"数据去哪了\"\n\n产品经理最关心的问题之一：数据从哪来、怎么处理、到哪去。\n\n**展示方式：**\n- 用动画展示数据在系统中的流动路径\n- 每个节点标注\"对数据做了什么\"（而非\"用什么技术做的\"）\n- 标注数据格式的变化（如\"用户填的表单\" → \"存储的订单记录\"）\n- 标注数据持久化的位置\n\n### 每个屏幕一个概念\n\n不要堆砌信息。模块内的每个屏幕讲清楚一件事。\n\n**好的屏幕主题：**\n- \"订单创建的完整流程\"\n- \"支付模块和库存模块如何配合\"\n- \"系统如何处理退款请求\"\n\n**差的屏幕主题：**\n- \"后端架构和所有API\" — 太大了，拆分\n- \"技术栈介绍\" — 产品经理不需要这个\n\n### 业务规则可视化\n\n系统中的业务规则是产品经理最需要理解的部分。\n\n**展示方式：**\n- 用决策树展示条件逻辑（\"如果 VIP 用户 → 免运费；如果新用户 → 首单折扣\"）\n- 用状态图展示实体的生命周期（订单：待支付 → 已支付 → 已发货 → 已完成）\n- 用表格对比不同场景的处理方式\n\n### 模块间内容递进\n\n模块之间应该从宏观到微观递进：\n\n1. **模块 1-2**：全景层 — 系统做什么、有哪些大模块、核心价值\n2. **模块 3-4**：流程层 — 核心业务流程、用户旅程、数据流转\n3. **模块 5-6**：细节层 — 模块协作细节、边界情况、扩展方向\n\n### 实用性导向\n\n每个模块都应该回答产品经理关心的问题：\n- \"这个系统的核心业务逻辑是什么？\"\n- \"用户操作后系统内部发生了什么？\"\n- \"各模块之间是怎么配合的？\"\n- \"数据是怎么流转和存储的？\"\n- \"如果要加新功能，会影响哪些模块？\"\n- \"系统的边界在哪里？和哪些外部系统有交互？\"\n\n如果模块不能帮助产品经理理解业务逻辑或做出产品决策，重新设计它。\n\nFile v1.2.0:references/design-system.md\n\n# 设计系统参考\n\n课程的完整 CSS 设计 token。将整个 `:root` 块复制到课程 HTML 中，并调整强调色以适应项目的个性。\n\n## 目录\n1. [调色板](#调色板)\n2. [排版](#排版)\n3. [间距与布局](#间距与布局)\n4. [阴影与深度](#阴影与深度)\n5. [动画与过渡](#动画与过渡)\n6. [导航与进度](#导航与进度)\n7. [模块结构](#模块结构)\n8. [响应式断点](#响应式断点)\n9. [滚动条与背景](#滚动条与背景)\n\n---\n\n## 调色板\n\n```css\n:root {\n  /* --- 背景 --- */\n  --color-bg:             #FAF7F2;       /* 温暖的米白色，像旧纸张 */\n  --color-bg-warm:        #F5F0E8;       /* 稍微更温暖，用于交替模块 */\n  --color-bg-code:        #1E1E2E;       /* 深靛蓝炭黑，用于代码块 */\n  --color-text:           #2C2A28;       /* 深炭黑，对眼睛友好 */\n  --color-text-secondary: #6B6560;       /* 温暖的灰色，用于次要文本 */\n  --color-text-muted:     #9E9790;       /* 柔和的，用于时间戳、标签 */\n  --color-border:         #E5DFD6;       /* 微妙的温暖边框 */\n  --color-border-light:   #EEEBE5;       /* 更浅的边框 */\n  --color-surface:        #FFFFFF;       /* 卡片表面 */\n  --color-surface-warm:   #FDF9F3;       /* 温暖的卡片表面 */\n\n  /* --- 强调色（根据项目调整 — 选择一个大胆的颜色）---\n     默认：朱红色。替代方案：珊瑚色 (#E06B56)、青色 (#2A7B9B)、\n     琥珀色 (#D4A843)、森林色 (#2D8B55)。避免紫色渐变。 */\n  --color-accent:         #D94F30;\n  --color-accent-hover:   #C4432A;\n  --color-accent-light:   #FDEEE9;\n  --color-accent-muted:   #E8836C;\n\n  /* --- 语义色 --- */\n  --color-success:        #2D8B55;\n  --color-success-light:  #E8F5EE;\n  --color-error:          #C93B3B;\n  --color-error-light:    #FDE8E8;\n  --color-info:           #2A7B9B;\n  --color-info-light:     #E4F2F7;\n\n  /* --- 角色颜色（分配给主要组件）---\n     代码库中的每个主要\"角色\"获得一个独特的颜色\n     用于聊天气泡、图表和高亮 */\n  --color-actor-1:        #D94F30;       /* 朱红色 */\n  --color-actor-2:        #2A7B9B;       /* 青色 */\n  --color-actor-3:        #7B6DAA;       /* 柔和的梅红色 */\n  --color-actor-4:        #D4A843;       /* 金色 */\n  --color-actor-5:        #2D8B55;       /* 森林色 */\n}\n```\n\n**规则：**\n- 偶数模块使用 `--color-bg`，奇数模块使用 `--color-bg-warm`（交替背景创造视觉节奏）\n- 角色颜色应该在视觉上彼此区分并与强调色区分\n- 代码块始终使用 `--color-bg-code` 配浅色文本\n\n---\n\n## 排版\n\n```css\n:root {\n  /* --- 字体 ---\n     使用系统字体回退，确保中国大陆用户无需翻墙即可正常显示。\n     展示/正文字体：中文优先系统字体\n     等宽字体：开发者友好的等宽系统字体回退链 */\n  --font-display:  'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n  --font-body:     'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n  --font-mono:     'JetBrains Mono', 'Fira Code', 'Source Code Pro', 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;\n\n  /* --- 类型比例（1.25 比例）--- */\n  --text-xs:   0.75rem;    /* 12px — 标签、徽章 */\n  --text-sm:   0.875rem;   /* 14px — 次要文本、代码 */\n  --text-base: 1rem;       /* 16px — 正文文本 */\n  --text-lg:   1.125rem;   /* 18px — 引导段落 */\n  --text-xl:   1.25rem;    /* 20px — 屏幕标题 */\n  --text-2xl:  1.5rem;     /* 24px — 子模块标题 */\n  --text-3xl:  1.875rem;   /* 30px — 模块副标题 */\n  --text-4xl:  2.25rem;    /* 36px — 模块标题 */\n  --text-5xl:  3rem;       /* 48px — 英雄文本 */\n  --text-6xl:  3.75rem;    /* 60px — 模块编号 */\n\n  /* --- 行高 --- */\n  --leading-tight:  1.15;  /* 标题 */\n  --leading-snug:   1.3;   /* 小标题 */\n  --leading-normal: 1.6;   /* 正文文本 */\n  --leading-loose:  1.8;   /* 轻松阅读 */\n}\n```\n\n**注意：使用系统字体，无需外部字体链接**\n本技能面向中国大陆用户，所有字体使用系统字体回退链，无需加载 Google Fonts 或任何外部 CDN。\n\n**规则：**\n- 模块编号：`--text-6xl`、font-display、weight 800、`--color-accent` 配 15% 不透明度\n- 模块标题：`--text-4xl`、font-display、weight 700\n- 屏幕标题：`--text-xl` 或 `--text-2xl`、font-display、weight 600\n- 正文文本：`--text-base` 或 `--text-lg`、font-body、`--leading-normal`\n- 代码：`--text-sm`、font-mono\n- 标签/徽章：`--text-xs`、font-mono、大写、letter-spacing 0.05em\n\n---\n\n## 间距与布局\n\n```css\n:root {\n  --space-1:  0.25rem;   /* 4px */\n  --space-2:  0.5rem;    /* 8px */\n  --space-3:  0.75rem;   /* 12px */\n  --space-4:  1rem;      /* 16px */\n  --space-5:  1.25rem;   /* 20px */\n  --space-6:  1.5rem;    /* 24px */\n  --space-8:  2rem;      /* 32px */\n  --space-10: 2.5rem;    /* 40px */\n  --space-12: 3rem;      /* 48px */\n  --space-16: 4rem;      /* 64px */\n  --space-20: 5rem;      /* 80px */\n  --space-24: 6rem;      /* 96px */\n\n  --content-width:     800px;   /* 标准阅读宽度 */\n  --content-width-wide: 1000px; /* 用于并排布局 */\n  --nav-height:        50px;\n  --radius-sm:  8px;\n  --radius-md:  12px;\n  --radius-lg:  16px;\n  --radius-full: 9999px;\n}\n```\n\n**模块布局：**\n```css\n.module {\n  min-height: 100dvh;       /* 回退：100vh */\n  scroll-snap-align: start;\n  padding: var(--space-16) var(--space-6);\n  padding-top: calc(var(--nav-height) + var(--space-12));\n}\n.module-content {\n  max-width: var(--content-width);\n  margin: 0 auto;\n}\n```\n\n---\n\n## 阴影与深度\n\n```css\n:root {\n  --shadow-sm:  0 1px 2px rgba(44, 42, 40, 0.05);\n  --shadow-md:  0 4px 12px rgba(44, 42, 40, 0.08);\n  --shadow-lg:  0 8px 24px rgba(44, 42, 40, 0.1);\n  --shadow-xl:  0 16px 48px rgba(44, 42, 40, 0.12);\n}\n```\n\n使用温暖色调的 RGBA（44, 42, 40）— 绝不使用纯黑色阴影。\n\n---\n\n## 动画与过渡\n\n```css\n:root {\n  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);\n  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  --duration-fast:   150ms;\n  --duration-normal: 300ms;\n  --duration-slow:   500ms;\n  --stagger-delay:   120ms;\n}\n```\n\n**滚动触发显示模式：**\n```css\n.animate-in {\n  opacity: 0;\n  transform: translateY(20px);\n  transition: opacity var(--duration-slow) var(--ease-out),\n              transform var(--duration-slow) var(--ease-out);\n}\n.animate-in.visible {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n/* 子元素交错显示 */\n.stagger-children > .animate-in {\n  transition-delay: calc(var(--stagger-index, 0) * var(--stagger-delay));\n}\n```\n\n**交错的 JS 设置：**\n```javascript\ndocument.querySelectorAll('.stagger-children').forEach(parent => {\n  Array.from(parent.children).forEach((child, i) => {\n    child.style.setProperty('--stagger-index', i);\n  });\n});\n```\n\n**Intersection Observer（触发显示）：**\n```javascript\nconst observer = new IntersectionObserver((entries) => {\n  entries.forEach(entry => {\n    if (entry.isIntersecting) {\n      entry.target.classList.add('visible');\n      observer.unobserve(entry.target); // 只动画一次\n    }\n  });\n}, { rootMargin: '0px 0px -10% 0px', threshold: 0.1 });\n\ndocument.querySelectorAll('.animate-in').forEach(el => observer.observe(el));\n```\n\n---\n\n## 导航与进度\n\n**HTML 结构：**\n```html\n<nav class=\"nav\">\n  <div class=\"progress-bar\" role=\"progressbar\" aria-valuenow=\"0\"></div>\n  <div class=\"nav-inner\">\n    <span class=\"nav-title\">课程标题</span>\n    <div class=\"nav-dots\">\n      <button class=\"nav-dot\" data-target=\"module-1\" data-tooltip=\"模块 1 名称\"\n              role=\"tab\" aria-label=\"模块 1\"></button>\n      <!-- 每个模块一个 -->\n    </div>\n  </div>\n</nav>\n```\n\n**进度条（尽可能只用 CSS，JS 回退）：**\n```javascript\nfunction updateProgressBar() {\n  const scrollTop = window.scrollY;\n  const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;\n  const progress = (scrollTop / scrollHeight) * 100;\n  progressBar.style.width = progress + '%';\n}\nwindow.addEventListener('scroll', () => {\n  requestAnimationFrame(updateProgressBar);\n}, { passive: true });\n```\n\n**导航点状态：**\n- 默认：`border: 2px solid var(--color-text-muted)`，空心\n- 当前：`border-color: var(--color-accent)`，实心中心，微妙发光阴影\n- 已访问：`background: var(--color-accent)`，实心填充\n\n**键盘导航：**\n```javascript\ndocument.addEventListener('keydown', (e) => {\n  if (['INPUT', 'TEXTAREA'].includes(e.target.tagName)) return;\n  if (e.key === 'ArrowDown' || e.key === 'ArrowRight') { nextModule(); e.preventDefault(); }\n  if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { prevModule(); e.preventDefault(); }\n});\n```\n\n---\n\n## 模块结构\n\n**每个模块的 HTML 模板：**\n```html\n<section class=\"module\" id=\"module-N\" style=\"background: var(--color-bg or --color-bg-warm)\">\n  <div class=\"module-content\">\n    <header class=\"module-header animate-in\">\n      <span class=\"module-number\">0N</span>\n      <h1 class=\"module-title\">模块标题</h1>\n      <p class=\"module-subtitle\">此模块教授内容的一句话描述</p>\n    </header>\n\n    <div class=\"module-body\">\n      <section class=\"screen animate-in\">\n        <h2 class=\"screen-heading\">屏幕标题</h2>\n        <p>内容...</p>\n        <!-- 交互元素、代码翻译等 -->\n      </section>\n\n      <section class=\"screen animate-in\">\n        <!-- 下一个屏幕 -->\n      </section>\n    </div>\n  </div>\n</section>\n```\n\n---\n\n## 响应式断点\n\n```css\n/* 平板 */\n@media (max-width: 768px) {\n  :root {\n    --text-4xl: 1.875rem;\n    --text-5xl: 2.25rem;\n    --text-6xl: 3rem;\n  }\n  .translation-block { grid-template-columns: 1fr; } /* 堆叠代码/英语 */\n  .pattern-cards { grid-template-columns: 1fr 1fr; }\n}\n\n/* 手机 */\n@media (max-width: 480px) {\n  :root {\n    --text-4xl: 1.5rem;\n    --text-5xl: 1.875rem;\n    --text-6xl: 2.25rem;\n  }\n  .module { padding: var(--space-8) var(--space-4); }\n  .pattern-cards { grid-template-columns: 1fr; }\n  .flow-steps { flex-direction: column; }\n  .flow-arrow { transform: rotate(90deg); }\n}\n```\n\n---\n\n## 滚动条与背景\n\n```css\n/* 自定义滚动条 */\n::-webkit-scrollbar { width: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb {\n  background: var(--color-border);\n  border-radius: var(--radius-full);\n}\n\n/* 微妙的氛围背景 */\nbody {\n  background: var(--color-bg);\n  background-image: radial-gradient(\n    ellipse at 20% 50%,\n    rgba(217, 79, 48, 0.03) 0%,\n    transparent 50%\n  );\n}\n\n/* 页面滚动设置 */\nhtml {\n  scroll-snap-type: y proximity;\n  scroll-behavior: smooth;\n}\n```\n\n---\n\n## 代码块全局设置\n\n课程中的所有代码块 —— 无论是在翻译块内、独立片段还是测验挑战中 —— 必须换行文本，绝不显示水平滚动条。这是教学工具，不是 IDE。\n\n```css\npre, code {\n  white-space: pre-wrap;       /* 换行长行 */\n  word-break: break-word;      /* 绝对需要时在单词中间断开 */\n  overflow-x: hidden;          /* 绝不出现水平滚动条 */\n}\n/* 隐藏代码容器上的滚动条 */\n.translation-code::-webkit-scrollbar,\npre::-webkit-scrollbar {\n  display: none;\n}\n```\n\n代码片段必须是来自真实代码库的**精确副本** — 绝不修改、修剪或简化。相反，从代码中选择自然简短（5-10 行）的部分来很好地说明概念。如果需要更长的块，全部显示 —— 换行 CSS 会处理可读性。\n\n---\n\n## 语法高亮（Catppuccin 风格）\n\n用于深色 `--color-bg-code` 背景上的代码块：\n\n```css\n.code-keyword  { color: #CBA6F7; }  /* 紫色 — if、else、return、function */\n.code-string   { color: #A6E3A1; }  /* 绿色 — \"字符串\" */\n.code-function { color: #89B4FA; }  /* 蓝色 — 函数名 */\n.code-comment  { color: #6C7086; }  /* 柔和灰色 — // 注释 */\n.code-number   { color: #FAB387; }  /* 桃色 — 数字 */\n.code-property { color: #F9E2AF; }  /* 黄色 — 对象键 */\n.code-operator { color: #94E2D5; }  /* 青色 — =、=>、+ 等 */\n.code-tag      { color: #F38BA8; }  /* 粉色 — HTML 标签 */\n.code-attr     { color: #F9E2AF; }  /* 黄色 — HTML 属性 */\n.code-value    { color: #A6E3A1; }  /* 绿色 — 属性值 */\n```\n\nFile v1.2.0:references/gotchas.md\n\n# 常见陷阱 —— 常见失败点\n\n> **何时阅读此文件：** 在阶段 3（编写模块 HTML）和阶段 4（审查）期间。在认为课程完成之前，检查以下每一项。\n\n根据你生成的课程版本（产品版或开发版），关注对应的检查项：\n\n---\n\n## 产品版特有问题\n\n### 提示框不足\n\n最常见的失败是提示框太少。非技术学习者不知道 REPL、JSON、标志、入口点、PATH、pip、命名空间、函数、类、模块、PR、E2E 等术语，甚至 Blender/GIMP 等软件名称。**经验法则：** 如果一个术语不会在与非技术朋友的日常对话中出现，就给它加提示框。大量倾向于太多。但是：不要给用户已经很熟悉的专业术语加提示框（例如，AI/ML 领域的人对 AI/ML 概念）。\n\n### 重复使用隐喻\n\n对所有内容使用\"餐厅\"或\"厨房\"。每个模块需要自己的隐喻，对该特定概念来说是必然的。如果你发现自己两次使用同一个隐喻，停下来找一个自然适合该概念的隐喻。\n\n### 测验数量不足\n\n**产品版要求每个模块至少一个测验。** 如果模块缺少测验，必须添加。\n\n---\n\n## 开发版特有问题\n\n### 过度解释基础概念\n\n专业程序员了解什么是 API、什么是回调、什么是数据库。不需要解释这些基础概念。\n\n**错误示例：** \"API（应用程序编程接口）是一种允许不同软件系统相互通信的方式...\"\n\n**正确示例：** \"项目使用 REST API 与支付服务通信，API 基址配置在 `config/api.ts`...\"\n\n### 缺少设计意图说明\n\n展示代码但不解释\"为什么要这样设计\"。专业程序员能看懂代码逻辑，他们想知道的是设计决策背后的原因。每个代码块都应该包含设计意图注释：权衡、替代方案、扩展点。\n\n### 架构图缺失或过于简化\n\n没有架构图，或者架构图只是简单的方框图。专业程序员需要看到：\n- 清晰的模块边界\n- 依赖方向\n- 数据流路径\n- 关键接口\n\n### 测验太简单\n\n**注意：测验在开发版中是可选功能，仅在用户明确要求时添加。**\n\n如果用户要求测验，测验问题不应该只测试表面理解，比如\"哪个文件处理 X？\"。专业程序员需要更有挑战性的问题：\n- 设计决策题（\"为什么要用策略模式而不是简单的 if-else？\"）\n- 场景题（\"如果要添加批量导出功能，你会如何实现？\"）\n- 问题诊断题（\"如果出现 Y 症状，可能是什么原因？\"）\n\n### 信息密度太低\n\n大量重复性描述，每句话信息量少。专业程序员阅读速度快，希望快速获取信息。每个段落都应该有明确的信息增量。\n\n### 代码示例不完整或被修改\n\n代码片段被过度简化，丢失了上下文信息。或者代码被\"清理\"过，不再是项目中的真实代码。\n\n**原则：** 使用真实代码，但精选最核心的 10-15 行，保留必要的上下文（导入、类型注解、注释）。\n\n### 代码过多，图表过少\n\n这是最常见的问题。课程贴了大量代码块，但缺少架构图、数据流动画和时序图。\n\n**检查标准：**\n- 每个模块是否至少有一个可视化元素（架构图、数据流动画、时序图、流程图）？\n- 单个代码块是否超过了 15 行？如果是，考虑拆分或用图表替代。\n- 是否有连续两个代码块之间没有可视化元素？\n- 课程整体的可视化元素占比是否达到 60%？\n- 是否有可以用数据流动画或步骤卡片替代代码的地方？\n\n**修复方法：** 回顾每个代码块，问自己：\"这段代码要传递的核心信息能否用图表或动画更好地表达？\" 如果能，用可视化替代。仅保留那些不可替代的核心代码片段。\n\n### 上下文耗尽导致后续模块质量下降\n\n分析大型代码库时，前面的模块占用了大量上下文，导致后面的模块信息不足、质量下降。\n\n**预防方法：**\n- 在阶段 2.5 为每个模块编写简报，后续编写时从简报读取而非重新分析代码库\n- 每完成 2 个模块后检查上下文使用情况\n- 必要时主动压缩上下文：将已提取的信息写入简报文件，释放空间\n\n### 缺少技术栈说明\n\n没有说明项目使用的技术栈以及选型理由。专业程序员关心：\n- 为什么选择 React 而不是 Vue？\n- 为什么用 PostgreSQL 而不是 MySQL？\n- 为什么选择 gRPC 而不是 REST？\n\n### 缺少扩展点和自定义机制说明\n\n专业学习者想知道\"如何添加新功能\"。课程应该明确指出：\n- 插件系统在哪里\n- 如何注册新的处理器\n- 配置扩展机制\n\n### 缺少实际应用指导\n\n课程讲清楚了\"是什么\"，但没有讲清楚\"怎么用\"。每个模块应该回答：\n- \"我在哪里添加新功能？\"\n- \"我如何定位特定类型的 bug？\"\n- \"我如何理解这个错误信息？\"\n\n---\n\n## 通用问题（两个版本都需注意）\n\n### 提示框被裁剪\n\n翻译块使用 `overflow: hidden` 来处理代码换行。如果提示框在术语元素内部使用 `position: absolute`，它们会被容器裁剪。\n\n**修复方法：** 提示框必须使用 `position: fixed` 并附加到 `document.body`。从 `getBoundingClientRect()` 计算位置。这已由 `main.js` 处理，但这是每次构建都会出现的 #1 bug。\n\n### 文字墙\n\n课程看起来像教科书而不是信息图。当你连续写超过 2-3 个句子而没有视觉中断时，就会发生这种情况。\n\n**产品版：** 每个屏幕必须至少 50% 是视觉内容（图表、代码块、问卷、动画）。\n**开发版：** 注重信息密度，可以文字占比更高，但必须有足够的图表和动画。\n\n将任何 3+ 项的列表转换为卡片，任何序列转换为步骤卡片或流程图，任何代码解释转换为代码↔英语翻译块。\n\n### 代码修改\n\n从代码库修剪、简化或\"清理\"代码片段。\n\n**产品版：** 学习者应该能够打开真实文件并看到完全相同的代码。不要通过编辑代码来使其更短，而是从代码库中*选择*自然简短（5-10 行）的片段来说明要点。\n**开发版：** 使用真实代码，仅保留必要的上下文（导入、类型注解），但保持核心逻辑完整。\n\n### 测试记忆的测验问题\n\n问\"API 代表什么？\"或\"哪个文件处理 X？\" —— 这些测试回忆，而不是理解。每个测验问题应该呈现学习者没见过的新场景，并要求他们*应用*所学内容。\n\n### 滚动捕捉强制模式\n\n使用 `scroll-snap-type: y mandatory` 会将用户困在长模块中。**始终使用 `proximity`。**\n\n### 模块质量下降\n\n尝试一次性编写所有模块会导致后面的模块变得单薄和仓促。一次构建一个模块，在继续之前验证每个模块。对于复杂代码库，使用带有模块简报的并行路径。\n\n### 缺少交互元素\n\n只有文本和代码块的模块，没有交互性。每个模块都需要交互元素。\n\n**产品版每个模块至少：** 测验、数据流动画、群聊、架构图、拖放之一\n**开发版每个模块至少：** 架构图、数据流动画、时序图之一\n\n这些不是装饰 —— 学习者是实际处理信息的方式。\n\nFile v1.2.0:references/interactive-elements.md\n\n# 交互元素参考\n\n课程中使用的每种交互元素类型的实现模式。选择最适合每个模块教学目标的元素。\n\n> **架构说明：** 这些元素的所有 CSS 和 JavaScript 都在 `references/styles.css` 和 `references/main.js` 中，它们被逐字复制到每个课程目录中。编写模块 HTML 文件时，只使用下面的 HTML 模式 — 不要为这些元素内联 `<style>` 或 `<script>` 标签。`main.js` 中的引擎在页面加载时通过扫描这里描述的相关类名和 `data-*` 属性自动初始化。\n\n## 目录\n1. [代码 ↔ 英语翻译块](#code--英语翻译块)\n2. [多选题测验](#多选题测验)\n3. [拖放匹配](#拖放匹配)\n4. [群聊动画](#群聊动画)\n5. [消息流 / 数据流动画](#消息流--数据流动画)\n6. [交互式架构图](#交互式架构图)\n7. [层次切换演示](#层次切换演示)\n8. [\"找 Bug\"挑战](#找-bug-挑战)\n9. [场景测验](#场景测验)\n10. [提示框](#提示框)\n11. [模式/功能卡片](#模式功能卡片)\n12. [流程图](#流程图)\n13. [权限/配置徽章](#权限配置徽章)\n14. [词汇表提示](#词汇表提示)\n15. [可视化文件树](#可视化文件树)\n16. [图标-标签行](#图标-标签行)\n17. [编号步骤卡片](#编号步骤卡片)\n\n---\n\n## 代码 ↔ 英语翻译块\n\n最重要的教学元素。左侧显示项目中的真实代码，右侧逐行显示通俗英语翻译。\n\n**HTML：**\n```html\n<div class=\"translation-block animate-in\">\n  <div class=\"translation-code\">\n    <span class=\"translation-label\">CODE</span>\n    <pre><code>\n<span class=\"code-line\"><span class=\"code-keyword\">const</span> response = <span class=\"code-keyword\">await</span> <span class=\"code-function\">fetch</span>(url, {</span>\n<span class=\"code-line\">  <span class=\"code-property\">method</span>: <span class=\"code-string\">'POST'</span>,</span>\n<span class=\"code-line\">  <span class=\"code-property\">headers</span>: { <span class=\"code-string\">'Authorization'</span>: apiKey }</span>\n<span class=\"code-line\">});</span>\n    </code></pre>\n  </div>\n  <div class=\"translation-english\">\n    <span class=\"translation-label\">PLAIN ENGLISH</span>\n    <div class=\"translation-lines\">\n      <p class=\"tl\">向 URL 发送请求并等待响应...</p>\n      <p class=\"tl\">我们在发送数据（POST），不只是请求数据（GET）...</p>\n      <p class=\"tl\">包含我们的 API 密钥，让服务器知道我们是谁...</p>\n      <p class=\"tl\">请求设置结束。</p>\n    </div>\n  </div>\n</div>\n```\n\n**CSS：**\n```css\n.translation-block {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0;\n  border-radius: var(--radius-md);\n  overflow: hidden;\n  box-shadow: var(--shadow-md);\n  margin: var(--space-8) 0;\n}\n.translation-code {\n  background: var(--color-bg-code);\n  color: #CDD6F4;\n  padding: var(--space-6);\n  font-family: var(--font-mono);\n  font-size: var(--text-sm);\n  line-height: 1.7;\n  position: relative;\n  overflow-x: hidden;  /* 绝不出现水平滚动条 */\n}\n.translation-code pre,\n.translation-code code {\n  white-space: pre-wrap;       /* 换行长行而不是滚动 */\n  word-break: break-word;      /* 必要时在单词中间断开 */\n  overflow-x: hidden;\n}\n.translation-english {\n  background: var(--color-surface-warm);\n  padding: var(--space-6);\n  font-size: var(--text-sm);\n  line-height: 1.7;\n  border-left: 3px solid var(--color-accent);\n}\n.translation-label {\n  position: absolute;\n  top: var(--space-2);\n  right: var(--space-3);\n  font-size: var(--text-xs);\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  opacity: 0.5;\n}\n.translation-english .translation-label {\n  color: var(--color-text-muted);\n}\n/* 响应式：移动端垂直堆叠 */\n@media (max-width: 768px) {\n  .translation-block { grid-template-columns: 1fr; }\n  .translation-english { border-left: none; border-top: 3px solid var(--color-accent); }\n}\n```\n\n**规则：**\n- 每行英语应对应 1-2 行代码\n- 使用对话式语言，不要技术术语\n- 强调\"为什么\"而不仅仅是\"什么\" —— 例如，\"包含我们的 API 密钥，让服务器知道我们是谁\"而不是\"设置 Authorization 头\"\n\n---\n\n## 多选题测验\n\n用于即时反馈测试理解。每个问题有选项、一个正确答案和每个问题的解释。\n\n**连接方式：** `main.js` 暴露 `window.selectOption(btn)`、`window.checkQuiz(containerId)` 和 `window.resetQuiz(containerId)`。通过 `onclick` 调用它们。每个问题的解释放在 `.quiz-question-block` 上的 `data-explanation-right` 和 `data-explanation-wrong` 中。\n\n**HTML：**\n```html\n<div class=\"quiz-container\" id=\"quiz-module3\">\n  <div class=\"quiz-question-block\"\n       data-correct=\"option-b\"\n       data-explanation-right=\"没错 — 因为 X 在此架构中负责 Y。\"\n       data-explanation-wrong=\"不完全对。想想 Y 在代码库中的位置...\">\n    <h3 class=\"quiz-question\">问题文本在这里？</h3>\n    <div class=\"quiz-options\">\n      <button class=\"quiz-option\" data-value=\"option-a\" onclick=\"selectOption(this)\">\n        <div class=\"quiz-option-radio\"></div>\n        <span>答案 A</span>\n      </button>\n      <button class=\"quiz-option\" data-value=\"option-b\" onclick=\"selectOption(this)\">\n        <div class=\"quiz-option-radio\"></div>\n        <span>答案 B（正确）</span>\n      </button>\n      <button class=\"quiz-option\" data-value=\"option-c\" onclick=\"selectOption(this)\">\n        <div class=\"quiz-option-radio\"></div>\n        <span>答案 C</span>\n      </button>\n    </div>\n    <div class=\"quiz-feedback\"></div>\n  </div>\n\n  <button class=\"quiz-check-btn\" onclick=\"checkQuiz('quiz-module3')\">检查答案</button>\n  <button class=\"quiz-reset-btn\" onclick=\"resetQuiz('quiz-module3')\">重试</button>\n</div>\n```\n\n**测验状态的 CSS：**\n```css\n.quiz-option {\n  display: flex; align-items: center; gap: var(--space-3);\n  padding: var(--space-3) var(--space-4);\n  border: 2px solid var(--color-border);\n  border-radius: var(--radius-sm);\n  background: var(--color-surface);\n  cursor: pointer; width: 100%;\n  transition: border-color var(--duration-fast), background var(--duration-fast);\n}\n.quiz-option:hover { border-color: var(--color-accent-muted); }\n.quiz-option.selected { border-color: var(--color-accent); background: var(--color-accent-light); }\n.quiz-option.correct { border-color: var(--color-success); background: var(--color-success-light); }\n.quiz-option.incorrect { border-color: var(--color-error); background: var(--color-error-light); }\n.quiz-option-radio {\n  width: 18px; height: 18px; border-radius: 50%;\n  border: 2px solid var(--color-border);\n  transition: all var(--duration-fast);\n}\n.quiz-option.selected .quiz-option-radio {\n  border-color: var(--color-accent);\n  background: var(--color-accent);\n  box-shadow: inset 0 0 0 3px white;\n}\n.quiz-feedback {\n  max-height: 0; overflow: hidden; opacity: 0;\n  transition: max-height var(--duration-normal), opacity var(--duration-normal);\n}\n.quiz-feedback.show { max-height: 200px; opacity: 1; padding: var(--space-3); margin-top: var(--space-2); border-radius: var(--radius-sm); }\n.quiz-feedback.success { background: var(--color-success-light); color: var(--color-success); }\n.quiz-feedback.error { background: var(--color-error-light); color: var(--color-error); }\n```\n\n---\n\n## 拖放匹配\n\n用于将概念与描述匹配。支持鼠标（HTML5 拖放 API）和触摸。\n\n**HTML：**\n```html\n<div class=\"dnd-container\">\n  <div class=\"dnd-chips\">\n    <div class=\"dnd-chip\" draggable=\"true\" data-answer=\"actor-a\">角色 A</div>\n    <div class=\"dnd-chip\" draggable=\"true\" data-answer=\"actor-b\">角色 B</div>\n    <div class=\"dnd-chip\" draggable=\"true\" data-answer=\"actor-c\">角色 C</div>\n  </div>\n  <div class=\"dnd-zones\">\n    <div class=\"dnd-zone\" data-correct=\"actor-a\">\n      <p class=\"dnd-zone-label\">角色 A 的描述</p>\n      <div class=\"dnd-zone-target\">拖到这里</div>\n    </div>\n    <!-- 更多区域 -->\n  </div>\n  <button onclick=\"checkDnD()\">检查匹配</button>\n  <button onclick=\"resetDnD()\">重置</button>\n</div>\n```\n\n**JS（鼠标 + 触摸）：**\n```javascript\n// 鼠标：HTML5 拖放 API\nchips.forEach(chip => {\n  chip.addEventListener('dragstart', (e) => {\n    e.dataTransfer.setData('text/plain', chip.dataset.answer);\n    chip.classList.add('dragging');\n  });\n  chip.addEventListener('dragend', () => chip.classList.remove('dragging'));\n});\n\nzones.forEach(zone => {\n  const target = zone.querySelector('.dnd-zone-target');\n  target.addEventListener('dragover', (e) => { e.preventDefault(); target.classList.add('drag-over'); });\n  target.addEventListener('dragleave', () => target.classList.remove('drag-over'));\n  target.addEventListener('drop', (e) => {\n    e.preventDefault();\n    target.classList.remove('drag-over');\n    const answer = e.dataTransfer.getData('text/plain');\n    const chip = document.querySelector(`[data-answer=\"${answer}\"]`);\n    target.textContent = chip.textContent;\n    target.dataset.placed = answer;\n    chip.classList.add('placed');\n  });\n});\n\n// 触摸：自定义实现（HTML5 拖放在移动端不工作）\nchips.forEach(chip => {\n  chip.addEventListener('touchstart', (e) => {\n    e.preventDefault();\n    const touch = e.touches[0];\n    const clone = chip.cloneNode(true);\n    clone.classList.add('touch-ghost');\n    clone.style.cssText = `position:fixed; z-index:1000; pointer-events:none;\n      left:${touch.clientX - 40}px; top:${touch.clientY - 20}px;`;\n    document.body.appendChild(clone);\n    chip._ghost = clone;\n    chip._answer = chip.dataset.answer;\n  }, { passive: false });\n\n  chip.addEventListener('touchmove', (e) => {\n    e.preventDefault();\n    const touch = e.touches[0];\n    if (chip._ghost) {\n      chip._ghost.style.left = (touch.clientX - 40) + 'px';\n      chip._ghost.style.top = (touch.clientY - 20) + 'px';\n    }\n    // 高亮手指下的区域\n    const el = document.elementFromPoint(touch.clientX, touch.clientY);\n    zones.forEach(z => z.querySelector('.dnd-zone-target').classList.remove('drag-over'));\n    if (el && el.closest('.dnd-zone-target')) {\n      el.closest('.dnd-zone-target').classList.add('drag-over');\n    }\n  }, { passive: false });\n\n  chip.addEventListener('touchend', (e) => {\n    if (chip._ghost) { chip._ghost.remove(); chip._ghost = null; }\n    const touch = e.changedTouches[0];\n    const el = document.elementFromPoint(touch.clientX, touch.clientY);\n    if (el && el.closest('.dnd-zone-target')) {\n      const target = el.closest('.dnd-zone-target');\n      target.textContent = chip.textContent;\n      target.dataset.placed = chip._answer;\n      chip.classList.add('placed');\n    }\n  });\n});\n```\n\n---\n\n## 群聊动画\n\niMessage/微信风格的聊天，显示组件之间\"对话\"。消息逐一出现，带有输入指示器。\n\n**连接方式：** `main.js` 在页面加载时自动初始化每个 `.chat-window`。给每个聊天窗口一个唯一的 `id`。控制按钮需要这些类：`.chat-next-btn`、`.chat-all-btn`、`.chat-reset-btn`。输入指示器头像元素应该有 `id=\"{chatWindowId}-typing-avatar\"` 或者简单是 `.chat-typing` 内的第一个 `.chat-avatar`。\n\n**HTML：**\n```html\n<div class=\"chat-window\" id=\"chat-module2\">\n  <div class=\"chat-messages\">\n    <div class=\"chat-message\" data-msg=\"0\" data-sender=\"actor-a\" style=\"display:none\">\n      <div class=\"chat-avatar\" style=\"background: var(--color-actor-1)\">A</div>\n      <div class=\"chat-bubble\">\n        <span class=\"chat-sender\" style=\"color: var(--color-actor-1)\">角色 A</span>\n        <p>嘿 Background，我需要这个项目的数据。</p>\n      </div>\n    </div>\n    <!-- 更多消息... -->\n  </div>\n\n  <div class=\"chat-typing\" id=\"chat-typing\" style=\"display:none\">\n    <div class=\"chat-avatar\" id=\"typing-avatar\">?</div>\n    <div class=\"chat-typing-dots\">\n      <span class=\"typing-dot\"></span>\n      <span class=\"typing-dot\"></span>\n      <span class=\"typing-dot\"></span>\n    </div>\n  </div>\n\n  <div class=\"chat-controls\">\n    <button class=\"btn chat-next-btn\">下一条消息</button>\n    <button class=\"btn chat-all-btn\">全部播放</button>\n    <button class=\"btn chat-reset-btn\">重播</button>\n    <span class=\"chat-progress\"></span>\n  </div>\n</div>\n```\n\n**输入点的 CSS：**\n```css\n.typing-dot {\n  width: 8px; height: 8px; border-radius: 50%;\n  background: var(--color-text-muted);\n  animation: typingBounce 1.4s infinite;\n}\n.typing-dot:nth-child(2) { animation-delay: 0.2s; }\n.typing-dot:nth-child(3) { animation-delay: 0.4s; }\n@keyframes typingBounce {\n  0%, 60%, 100% { transform: translateY(0); }\n  30% { transform: translateY(-6px); }\n}\n```\n\n---\n\n## 消息流 / 数据流动画\n\n组件之间数据移动的分步可视化。用户点击\"下一步\"前进。\n\n**连接方式：** `main.js` 在页面加载时自动初始化每个 `.flow-animation`。在 `data-steps` 中传递步骤为 JSON。每个步骤对象：`{ highlight: \"flow-actor-id\", label: \"描述\", packet: true, from: \"actor-id-suffix\", to: \"actor-id-suffix\" }`。角色元素 ID 必须是 `flow-actor-1`、`flow-actor-2` 等。控制按钮需要类 `.flow-next-btn` 和 `.flow-reset-btn`。\n\n> **⚠️ 步骤标签中的单引号会破坏解析。** `data-steps` 属性由单引号定界（`data-steps='[...]'`），所以标签内的任何单引号（例如 `\"the user's request\"`）会提前终止属性并导致 `JSON.parse` 静默失败 —— 整个动画将停止工作。要么避免在标签中使用撇号，用 `&apos;` 替换，或使用双引号定界并转义内部引号重写属性（`data-steps=\"[{\\\"label\\\":\\\"...\\\"}]\"`）。\n\n**HTML：**\n```html\n<div class=\"flow-animation\" data-steps='[\n  {\"highlight\":\"flow-actor-1\",\"label\":\"用户点击按钮\"},\n  {\"highlight\":\"flow-actor-1\",\"label\":\"前端发送请求\",\"packet\":true,\"from\":\"actor-1\",\"to\":\"actor-2\"},\n  {\"highlight\":\"flow-actor-2\",\"label\":\"后端调用数据库\",\"packet\":true,\"from\":\"actor-2\",\"to\":\"actor-3\"}\n]'>\n  <div class=\"flow-actors\">\n    <div class=\"flow-actor\" id=\"flow-actor-1\">\n      <div class=\"flow-actor-icon\">A</div>\n      <span>角色 1</span>\n    </div>\n    <div class=\"flow-actor\" id=\"flow-actor-2\">\n      <div class=\"flow-actor-icon\">B</div>\n      <span>角色 2</span>\n    </div>\n    <div class=\"flow-actor\" id=\"flow-actor-3\">\n      <div class=\"flow-actor-icon\">C</div>\n      <span>角色 3</span>\n    </div>\n  </div>\n\n  <div class=\"flow-packet\" id=\"flow-packet\"></div>\n\n  <div class=\"flow-step-label\" id=\"flow-label\">点击\"下一步\"开始</div>\n\n  <div class=\"flow-controls\">\n    <button class=\"btn flow-next-btn\">下一步</button>\n    <button class=\"btn flow-reset-btn\">重新开始</button>\n    <span class=\"flow-progress\"></span>\n  </div>\n</div>\n```\n\n**活动角色发光的 CSS：**\n```css\n.flow-actor.active {\n  box-shadow: 0 0 0 3px var(--color-accent), 0 0 20px rgba(217, 79, 48, 0.2);\n  transform: scale(1.05);\n  transition: all var(--duration-normal) var(--ease-out);\n}\n```\n\n---\n\n## 交互式架构图\n\n全系统图，悬停/点击组件显示描述提示框。\n\n**HTML：**\n```html\n<div class=\"arch-diagram\">\n  <div class=\"arch-zone arch-zone-browser\">\n    <h4 class=\"arch-zone-label\">浏览器</h4>\n    <div class=\"arch-component\" data-desc=\"将 UI 注入网页，读取 DOM，捕获用户操作\"\n         onclick=\"showArchDesc(this)\">\n      <div class=\"arch-icon\">📄</div>\n      <span>组件 A</span>\n    </div>\n    <!-- 更多组件 -->\n  </div>\n  <div class=\"arch-zone arch-zone-external\">\n    <h4 class=\"arch-zone-label\">外部服务</h4>\n    <!-- API 卡片 -->\n  </div>\n  <div class=\"arch-description\" id=\"arch-desc\">点击任何组件了解它的作用</div>\n</div>\n```\n\n---\n\n## 层次切换演示\n\n显示不同层（例如，HTML/CSS/JS，或数据/逻辑/UI）如何相互构建。三个标签在视图之间切换。\n\n**HTML：**\n```html\n<div class=\"layer-demo\">\n  <div class=\"layer-tabs\">\n    <button class=\"layer-tab active\" onclick=\"showLayer('html')\">HTML</button>\n    <button class=\"layer-tab\" onclick=\"showLayer('css')\">+ CSS</button>\n    <button class=\"layer-tab\" onclick=\"showLayer('js')\">+ JS</button>\n  </div>\n  <div class=\"layer-viewport\">\n    <div class=\"layer\" id=\"layer-html\" style=\"display:block\">\n      <!-- 原始无样式版本 -->\n    </div>\n    <div class=\"layer\" id=\"layer-css\" style=\"display:none\">\n      <!-- 带样式版本 -->\n    </div>\n    <div class=\"layer\" id=\"layer-js\" style=\"display:none\">\n      <!-- 交互版本 -->\n    </div>\n  </div>\n  <p class=\"layer-description\" id=\"layer-desc\">这是原始 HTML...</p>\n</div>\n```\n\n---\n\n## \"找 Bug\"挑战\n\n显示带有故意 bug 的代码。用户点击有 bug 的行。显示解释问题。\n\n**HTML：**\n```html\n<div class=\"bug-challenge\">\n  <h3>找出这段代码中的 bug：</h3>\n  <div class=\"bug-code\">\n    <div class=\"bug-line\" data-line=\"1\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">1</span>\n      <code>chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {</code>\n    </div>\n    <div class=\"bug-line\" data-line=\"2\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">2</span>\n      <code>  if (msg.action === 'fetchData') {</code>\n    </div>\n    <div class=\"bug-line bug-target\" data-line=\"3\" onclick=\"checkBugLine(this, true)\">\n      <span class=\"line-num\">3</span>\n      <code>    fetch(url).then(r => r.json()).then(data => sendResponse(data));</code>\n    </div>\n    <div class=\"bug-line\" data-line=\"4\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">4</span>\n      <code>  }</code>\n    </div>\n    <div class=\"bug-line\" data-line=\"5\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">5</span>\n      <code>});</code>\n    </div>\n  </div>\n  <div class=\"bug-feedback\" id=\"bug-feedback\"></div>\n</div>\n```\n\n**JS：**\n```javascript\nwindow.checkBugLine = function(el, isCorrect) {\n  const feedback = el.closest('.bug-challenge').querySelector('.bug-feedback');\n  if (isCorrect) {\n    el.classList.add('correct');\n    feedback.innerHTML = '<strong>找到了！</strong> 监听器使用了异步操作（fetch）但没有返回 true。Chrome 在响应发送之前关闭消息通道。修复：在末尾添加 <code>return true;</code>。';\n    feedback.className = 'bug-feedback show success';\n  } else {\n    el.classList.add('incorrect');\n    feedback.innerHTML = '不是这行 — 找找异步时序可能引起问题的地方...';\n    feedback.className = 'bug-feedback show error';\n    setTimeout(() => { el.classList.remove('incorrect'); feedback.className = 'bug-feedback'; }, 2000);\n  }\n};\n```\n\n---\n\n## 场景测验\n\n\"资深工程师会怎么做？\" —— 情境问题带解释。\n\n与多选题相同的 HTML/CSS/JS 模式，但场景描述更长，解释更详细。将每个问题包装在场景上下文块中：\n\n```html\n<div class=\"scenario-block\">\n  <div class=\"scenario-context\">\n    <span class=\"scenario-label\">场景</span>\n    <p>你的应用处理 3 小时的播客转录。API 有 16,000 token 限制。你怎么办？</p>\n  </div>\n  <!-- quiz-options 在这里 -->\n</div>\n```\n\n---\n\n## 提示框\n\n\"顿悟！\"时刻 —— 通用计算机科学见解。每个模块最多 2 个。\n\n```html\n<div class=\"callout callout-accent\">\n  <div class=\"callout-icon\">💡</div>\n  <div class=\"callout-content\">\n    <strong class=\"callout-title\">关键见解</strong>\n    <p>这个模式 —— 将职责分成专注的角色 —— 是软件工程中最重要的想法之一。工程师称之为\"关注点分离\"。</p>\n  </div>\n</div>\n```\n\n**变体：**\n- `callout-accent`：朱红色左边框，浅强调背景（用于 CS 见解）\n- `callout-info`：青色左边框，浅信息背景（用于\"值得了解\"）\n- `callout-warning`：红色左边框，浅错误背景（用于常见错误）\n\n---\n\n## 模式/功能卡片\n\n突出工程模式、技术栈组件或关键概念的卡片网格。\n\n```html\n<div class=\"pattern-cards\">\n  <div class=\"pattern-card\" style=\"border-top: 3px solid var(--color-actor-1)\">\n    <div class=\"pattern-icon\" style=\"background: var(--color-actor-1)\">🔄</div>\n    <h4 class=\"pattern-title\">缓存</h4>\n    <p class=\"pattern-desc\">存储结果以避免重复工作 —— 就像保存剩菜而不是每次都做新饭。</p>\n  </div>\n  <!-- 更多卡片 -->\n</div>\n```\n\n```css\n.pattern-cards {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n  gap: var(--space-4);\n}\n.pattern-card {\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  padding: var(--space-6);\n  box-shadow: var(--shadow-sm);\n  transition: transform var(--duration-normal) var(--ease-out), box-shadow var(--duration-normal);\n}\n.pattern-card:hover {\n  transform: translateY(-4px);\n  box-shadow: var(--shadow-md);\n}\n```\n\n---\n\n## 流程图\n\n**水平流程（桌面）：**\n```html\n<div class=\"flow-steps\">\n  <div class=\"flow-step\">\n    <div class=\"flow-step-num\">1</div>\n    <p>用户点击按钮</p>\n  </div>\n  <div class=\"flow-arrow\">→</div>\n  <div class=\"flow-step\">\n    <div class=\"flow-step-num\">2</div>\n    <p>组件 A 检测到点击</p>\n  </div>\n  <div class=\"flow-arrow\">→</div>\n  <!-- 更多步骤 -->\n</div>\n```\n\n箭头在移动端通过 CSS 变换旋转为 `↓`。\n\n---\n\n## 权限/配置徽章\n\n用于注释配置文件、权限或设置：\n\n```html\n<div class=\"badge-list\">\n  <div class=\"badge-item\">\n    <code class=\"badge-code\">storage</code>\n    <span class=\"badge-desc\">在会话之间保存数据（像浏览器书签）</span>\n  </div>\n  <div class=\"badge-item\">\n    <code class=\"badge-code\">activeTab</code>\n    <span class=\"badge-desc\">访问当前打开的标签页（仅当用户点击时）</span>\n  </div>\n</div>\n```\n\n```css\n.badge-item {\n  display: flex; align-items: center; gap: var(--space-4);\n  padding: var(--space-3) var(--space-4);\n  border: 1px solid var(--color-border-light);\n  border-radius: var(--radius-sm);\n  transition: border-color var(--duration-fast);\n}\n.badge-item:hover { border-color: var(--color-accent-muted); }\n.badge-code {\n  font-family: var(--font-mono);\n  font-size: var(--text-sm);\n  background: var(--color-bg-code);\n  color: #CBA6F7;\n  padding: var(--space-1) var(--space-3);\n  border-radius: var(--radius-sm);\n  white-space: nowrap;\n}\n```\n\n---\n\n## 词汇表提示\n\n非技术学习者最重要的无障碍功能。课程文本中的任何技术术语都应该包装在提示中，在悬停（桌面）或点击（移动）时显示通俗英语定义。学习者永远不需要离开页面或 Google 任何东西。\n\n**HTML — 内联标记术语：**\n```html\n<p>扩展使用\n  <span class=\"term\" data-definition=\"service worker 是独立于网页运行的后台脚本 —— 就像一个始终开启的幕后助手，即使你没有在看页面。\">service worker</span>\n  来处理 API 调用。\n</p>\n```\n\n**CSS：**\n```css\n.term {\n  border-bottom: 1.5px dashed var(--color-accent-muted);\n  cursor: pointer;    /* 不是 cursor: help — pointer 感觉可点击且诱人 */\n  position: relative;\n}\n.term:hover, .term.active {\n  border-bottom-color: var(--color-accent);\n  color: var(--color-accent);\n}\n\n/* 提示框气泡 — 使用 position: fixed 并通过 JS 附加到 document.body\n   所以它永远不会被祖先的 overflow: hidden 容器裁剪\n   （像翻译块）。参见下面 JS 部分的定位逻辑。 */\n.term-tooltip {\n  position: fixed;        /* 关键：fixed，不是 absolute — 防止裁剪 */\n  background: var(--color-bg-code);\n  color: #CDD6F4;\n  padding: var(--space-3) var(--space-4);\n  border-radius: var(--radius-sm);\n  font-size: var(--text-sm);\n  font-family: var(--font-body);\n  line-height: var(--leading-normal);\n  width: max(200px, min(320px, 80vw));\n  box-shadow: var(--shadow-lg);\n  pointer-events: none;\n  opacity: 0;\n  transition: opacity var(--duration-fast);\n  z-index: 10000;        /* 在所有东西之上，包括导航 */\n}\n/* 向下的箭头 */\n.term-tooltip::after {\n  content: '';\n  position: absolute;\n  top: 100%;\n  left: 50%;\n  transform: translateX(-50%);\n  border: 6px solid transparent;\n  border-top-color: var(--color-bg-code);\n}\n.term-tooltip.visible {\n  opacity: 1;\n}\n\n/* 如果提示框超出屏幕顶部，翻转到下方 */\n.term-tooltip.flip {\n  bottom: auto;\n  top: calc(100% + 8px);\n}\n.term-tooltip.flip::after {\n  top: auto;\n  bottom: 100%;\n  border-top-color: transparent;\n  border-bottom-color: var(--color-bg-code);\n}\n```\n\n**JS — 附加到 body 的 position: fixed 提示框（永远不会被 overflow 裁剪）：**\n```javascript\n// 提示框容器 — 附加到 body 所以永远不会被裁剪\nlet activeTooltip = null;\n\nfunction positionTooltip(term, tip) {\n  const rect = term.getBoundingClientRect();\n  const tipWidth = 300; // 大约\n  let left = rect.left + rect.width / 2 - tipWidth / 2;\n  // 限制在视口内\n  left = Math.max(8, Math.min(left, window.innerWidth - tipWidth - 8));\n\n  // 先尝试上方\n  let top = rect.top - 8;\n  tip.style.left = left + 'px';\n\n  // 默认定位在上方，如果没有空间则翻转到下方\n  document.body.appendChild(tip);\n  const tipHeight = tip.offsetHeight;\n  if (rect.top - tipHeight - 8 < 0) {\n    // 翻转到下方\n    tip.style.top = (rect.bottom + 8) + 'px';\n    tip.classList.add('flip');\n  } else {\n    tip.style.top = (rect.top - tipHeight - 8) + 'px';\n    tip.classList.remove('flip');\n  }\n}\n\ndocument.querySelectorAll('.term').forEach(term => {\n  const tip = document.createElement('span');\n  tip.className = 'term-tooltip';\n  tip.textContent = term.dataset.definition;\n\n  // 桌面端悬停\n  term.addEventListener('mouseenter', () => {\n    if (activeTooltip && activeTooltip !== tip) {\n      activeTooltip.classList.remove('visible');\n      activeTooltip.remove();\n    }\n    positionTooltip(term, tip);\n    requestAnimationFrame(() => tip.classList.add('visible'));\n    activeTooltip = tip;\n  });\n\n  term.addEventListener('mouseleave', () => {\n    tip.classList.remove('visible');\n    setTimeout(() => { if (!tip.classList.contains('visible')) tip.remove(); }, 150);\n    activeTooltip = null;\n  });\n\n  // 移动端点击\n  term.addEventListener('click', (e) => {\n    e.stopPropagation();\n    if (activeTooltip && activeTooltip !== tip) {\n      activeTooltip.classList.remove('visible');\n      activeTooltip.remove();\n    }\n    if (tip.classList.contains('visible')) {\n      tip.classList.remove('visible');\n      tip.remove();\n      activeTooltip = null;\n    } else {\n      positionTooltip(term, tip);\n      requestAnimationFrame(() => tip.classList.add('visible'));\n      activeTooltip = tip;\n    }\n  });\n});\n\n// 点击其他地方关闭提示框\ndocument.addEventListener('click', () => {\n  if (activeTooltip) {\n    activeTooltip.classList.remove('visible');\n    activeTooltip.remove();\n    activeTooltip = null;\n  }\n});\n```\n\n**规则：**\n- 在每个模块首次使用时标记每个技术术语（API、DOM、回调、异步、端点、中间件等）\n- 定义最多 1-2 句话，使用日常语言\n- 在定义中使用隐喻当它有帮助时 —— 例如，\"**回调**就像在餐厅留下你的电话号码，这样当你的桌子准备好时他们可以打电话给你\"\n- 不要在同一屏幕内标记同一个术语两次 — 只在每个模块首次出现时\n- 虚线下划线应该足够微妙不分散注意力，但也足够明显让好奇的学习者发现它\n\n---\n\n## 可视化文件树\n\n使用此代替列出\"这个文件夹做 X，那个文件夹做 Y\"的段落。更容易扫描。\n\n```html\n<div class=\"file-tree\">\n  <div class=\"ft-folder open\">\n    <span class=\"ft-name\">app/</span>\n    <span class=\"ft-desc\">页面和 API 路由</span>\n    <div class=\"ft-children\">\n      <div class=\"ft-folder\">\n        <span class=\"ft-name\">api/</span>\n        <span class=\"ft-desc\">前端调用的后端端点</span>\n      </div>\n      <div class=\"ft-file\">\n        <span class=\"ft-name\">layout.tsx</span>\n        <span class=\"ft-desc\">包装每个页面的外壳</span>\n      </div>\n    </div>\n  </div>\n  <div class=\"ft-folder\">\n    <span class=\"ft-name\">components/</span>\n    <span class=\"ft-desc\">可复用的 UI 构建块</span>\n  </div>\n  <div class=\"ft-folder\">\n    <span class=\"ft-name\">lib/</span>\n    <span class=\"ft-desc\">共享逻辑和工具</span>\n  </div>\n</div>\n```\n\n```css\n.file-tree { font-family: var(--font-mono); font-size: var(--text-sm); }\n.ft-folder, .ft-file {\n  padding: var(--space-2) var(--space-3);\n  border-left: 2px solid var(--color-border-light);\n  margin-left: var(--space-4);\n}\n.ft-folder > .ft-name { color: var(--color-accent); font-weight: 600; }\n.ft-folder > .ft-name::before { content: '📁 '; }\n.ft-file > .ft-name::before { content: '📄 '; }\n.ft-desc {\n  color: var(--color-text-secondary);\n  font-family: var(--font-body);\n  margin-left: var(--space-2);\n  font-size: var(--text-xs);\n}\n.ft-children { margin-left: var(--space-4); }\n```\n\n---\n\n## 图标-标签行\n\n用于可视化列出组件、功能或概念。替换项目符号段落。\n\n```html\n<div class=\"icon-rows\">\n  <div class=\"icon-row\">\n    <div class=\"icon-circle\" style=\"background: var(--color-actor-1)\">🖥️</div>\n    <div>\n      <strong>前端 (Next.js)</strong>\n      <p>用户看到和交互的内容</p>\n    </div>\n  </div>\n  <div class=\"icon-row\">\n    <div class=\"icon-circle\" style=\"background: var(--color-actor-2)\">⚡</div>\n    <div>\n      <strong>API 路由</strong>\n      <p>在服务器上运行的后端逻辑</p>\n    </div>\n  </div>\n  <div class=\"icon-row\">\n    <div class=\"icon-circle\" style=\"background: var(--color-actor-3)\">🗄️</div>\n    <div>\n      <strong>数据库 (Supabase)</strong>\n      <p>所有数据永久存储的地方</p>\n    </div>\n  </div>\n</div>\n```\n\n```css\n.icon-rows { display: flex; flex-direction: column; gap: var(--space-4); }\n.icon-row {\n  display: flex; align-items: center; gap: var(--space-4);\n  padding: var(--space-4);\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  box-shadow: var(--shadow-sm);\n}\n.icon-row p { margin: 0; color: var(--color-text-secondary); font-size: var(--text-sm); }\n.icon-circle {\n  width: 48px; height: 48px; border-radius: 50%;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 1.25rem; flex-shrink: 0;\n}\n```\n\n---\n\n## 编号步骤卡片\n\n用于本来是编号段落列表的序列。视觉化、可扫描，每步独立存在。\n\n```html\n<div class=\"step-cards\">\n  <div class=\"step-card\">\n    <div class=\"step-num\">1</div>\n    <div class=\"step-body\">\n      <strong>用户粘贴 YouTube URL</strong>\n      <p>前端捕获 URL 并提取视频 ID</p>\n    </div>\n  </div>\n  <div class=\"step-card\">\n    <div class=\"step-num\">2</div>\n    <div class=\"step-body\">\n      <strong>API 获取转录</strong>\n      <p>服务器端路由调用外部服务获取视频文本</p>\n    </div>\n  </div>\n  <div class=\"step-card\">\n    <div class=\"step-num\">3</div>\n    <div class=\"step-body\">\n      <strong>AI 分析内容</strong>\n      <p>转录发送给提取关键时刻的 AI 模型</p>\n    </div>\n  </div>\n</div>\n```\n\n```css\n.step-cards { display: flex; flex-direction: column; gap: var(--space-3); }\n.step-card {\n  display: flex; align-items: flex-start; gap: var(--space-4);\n  padding: var(--space-4) var(--space-5);\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  border-left: 3px solid var(--color-accent);\n  box-shadow: var(--shadow-sm);\n}\n.step-num {\n  width: 32px; height: 32px; border-radius: 50%;\n  background: var(--color-accent);\n  color: white; font-weight: 700;\n  display: flex; align-items: center; justify-content: center;\n  font-family: var(--font-display);\n  flex-shrink: 0;\n}\n.step-body p { margin: var(--space-1) 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); }\n```\n\nFile v1.2.0:references/module-brief-template.md\n\n# 模块简报模板\n\n> **何时阅读此文件：** 在阶段 2.5（规划检查点）期间，用于复杂代码库。每个模块填写一份简报，保存到 `course-name/briefs/0N-slug.md`。每份简报给并行代理编写一个模块所需的一切，无需阅读代码库或 SKILL.md。\n\n**选择适合你课程版本的章节：**\n- 产品版：关注\"教学弧线\"、\"代码↔英语翻译\"、\"交互元素\"\n- 开发版：关注\"教学目标\"、\"设计意图说明\"、\"架构图/数据流图\"\n\n---\n\n## 模块 N：[标题]\n\n### 教学弧线（产品版）\n\n- **隐喻：** [一个新鲜的、具体的隐喻 — 绝不要\"餐厅\"。参见 `references/content-philosophy.md` > 隐喻优先]\n- **开篇钩子：** [一句连接到学习者从使用应用中已知内容的话]\n- **关键见解：** [学习者应该带走理解的一件事]\n- **\"为什么要关心？\"：** [这如何帮助他们驾驭 AI / 调试 / 做决策]\n\n### 教学目标（开发版）\n\n- **核心心智模型：** [学习者在完成模块后应该建立什么样的心智模型？]\n- **关键设计决策：** [这个模块要解释的设计决策是什么？]\n- **实用技能：** [学习者能够做什么？例如，\"能够定位并修改支付流程\"]\n- **深度层次：** [架构层 / 实现层 / 进阶层]\n\n---\n\n### 代码片段（预提取）\n\n**产品版：** 包含模块将在代码↔英语翻译块中使用的实际代码。从代码库复制粘贴，带文件路径和行号。写作代理将逐字使用这些 — 它不会重新阅读代码库。\n\n**开发版：** 包含模块将在代码分析块中使用的实际代码。保持代码完整，包括必要的上下文（导入、类型注解、相关注释）。不要过度简化。\n\n文件：src/example/file.ts（第 12-24 行）\n```typescript\n[在此粘贴实际代码]\n```\n\n文件：src/another/file.ts（第 45-52 行）\n**设计笔记（开发版，预写入）：**\n- 这段代码的设计意图：...\n- 使用了什么模式：...\n- 潜在陷阱：...\n```typescript\n[在此粘贴实际代码]\n```\n\n---\n\n### 交互元素\n\n检查此模块需要哪些元素。包含足够的细节供写作代理构建它们。\n\n**产品版交互元素：**\n- [ ] **代码↔英语翻译** — 使用上面哪些片段\n- [ ] **测验** — [数量] 个问题，风格：[场景 / 调试 / 架构 / 追踪]。每个问题角度的简要描述。\n- [ ] **群聊动画** — 角色：[列表]。消息流摘要：[谁对谁说什么，按什么顺序]\n- [ ] **数据流动画** — 角色：[列表]。步骤：[高亮和数据包移动的序列]\n- [ ] **拖放** — 项目：[列表]，目标：[列表]\n- [ ] **其他** — [架构图、层次切换、模式卡片等]\n\n**开发版交互元素：**\n*注意：测验默认不包含，仅在用户明确要求时添加。*\n- [ ] **代码分析块** — 使用上面哪些片段，重点解释什么设计决策\n- [ ] **架构图** — [类型：组件图 / 层次图 / 部署图]。主要组件：[列表]。关键标注：[信息]\n- [ ] **数据流图** — [角色和步骤，数据包移动序列]\n- [ ] **时序图/状态图** — [按需：展示交互或状态转换]\n- [ ] **深度测验** — （可选，仅用户要求时）[数量] 个问题，类型：[设计决策 / 场景应用 / 问题诊断]\n\n---\n\n### 架构图/数据流图（开发版）\n\n如果模块需要图表，预定义：\n\n- **图表类型：** [组件图 / 时序图 / 数据流图 / 状态图]\n- **主要组件：** [列出图中需要展示的组件]\n- **数据流/关系：** [描述数据如何流动或组件如何交互]\n- **关键标注：** [需要在图中标注的信息]\n\n---\n\n### 设计意图说明（开发版）\n\n专业程序员需要理解\"为什么\"，而不仅仅是\"是什么\"。预提取设计背景：\n\n- **为什么这样设计？** [设计决策的背景和理由]\n- **有哪些权衡？** [选择 A 而不是 B 的原因]\n- **替代方案是什么？** [讨论过但未采用的方案]\n- **扩展点在哪里？** [如何扩展这个模块/功能]\n\n---\n\n### 要阅读的参考文件\n\n只列出写作代理需要的部分 — 不是整个文件。\n\n- `references/interactive-elements.md` → [章节名称]\n- `references/design-system.md` → [仅当需要特定 token 时]\n- `references/gotchas.md` → [始终包含]\n\n根据版本选择：\n- **产品版**：`references/content-philosophy.md` → [始终包含]\n- **开发版**：`references/content-philosophy-pro.md` → [始终包含]\n\n---\n\n### 连接\n\n- **前一个模块：** [标题 — 涵盖了什么，此模块如何在此基础上深入/构建]\n- **后一个模块：** [标题 — 将涵盖什么，此模块如何为后续做铺垫]\n- **语调/风格说明：** [产品版：强调色名称、角色命名约定等 / 开发版：技术深度递进位置]\n\nFile v1.2.0:DESIGN.md\n\n# 设计系统：Codebase to Course（教育课程风格）\n\n> 温暖、易读、面向非技术用户的交互式课程设计系统\n\n## 1. 视觉主题与氛围\n\n**设计风格定位**：教育出版物风格，温暖亲切，强调可读性与交互性\n\n**整体视觉印象**：\n- 浅色温暖的纸张质感背景，像翻阅一本精心设计的教材\n- 深色代码块形成强烈对比，突出代码内容\n- 朱红色作为主要强调色，活泼但不刺眼\n\n**关键视觉特征**：\n- 模块化滚动吸附（scroll-snap）—— 像翻书一样浏览\n- 温暖色调的阴影（绝不使用纯黑阴影）\n- 大号模块编号作为视觉锚点\n- 渐进式动画揭示内容\n- Catppuccin 风格的语法高亮\n\n**设计哲学**：\n> 这不是 IDE，是教学工具。代码块换行显示，绝不出现水平滚动条。\n\n---\n\n## 2. 色彩调色板与角色\n\n### 背景表面\n\n| 角色 | 颜色值 | 用途 |\n|------|--------|------|\n| `--color-bg` | `#FAF7F2` | 主背景，温暖的米白色，像旧纸张 |\n| `--color-bg-warm` | `#F5F0E8` | 交替模块背景，创造视觉节奏 |\n| `--color-bg-code` | `#1E1E2E` | 代码块背景，深靛蓝炭黑 |\n| `--color-surface` | `#FFFFFF` | 卡片表面，纯白 |\n| `--color-surface-warm` | `#FDF9F3` | 温暖的卡片表面 |\n\n### 文字颜色\n\n| 角色 | 颜色值 | 用途 |\n|------|--------|------|\n| `--color-text` | `#2C2A28` | 主文字，深炭黑，对眼睛友好 |\n| `--color-text-secondary` | `#6B6560` | 次要文字，温暖的灰色 |\n| `--color-text-muted` | `#9E9790` | 柔和灰色，用于时间戳、标签 |\n\n### 品牌与强调色\n\n| 角色 | 颜色值 | 用途 |\n|------|--------|------|\n| `--color-accent` | `#D94F30` | 主强调色，朱红色 |\n| `--color-accent-hover` | `#C4432A` | 悬停状态 |\n| `--color-accent-light` | `#FDEEE9` | 浅色背景 |\n| `--color-accent-muted` | `#E8836C` | 柔和强调 |\n\n**可选强调色方案**：\n| 名称 | 主色 | 悬停 | 浅色 | 柔和 |\n|------|------|------|------|------|\n| 珊瑚 | `#E06B56` | `#C85A47` | `#FDECEA` | `#E89585` |\n| 青色 | `#2A7B9B` | `#1F6280` | `#E4F2F7` | `#5A9DB8` |\n| 琥珀 | `#D4A843` | `#BF9530` | `#FDF5E0` | `#E0C070` |\n| 森林 | `#2D8B55` | `#226B41` | `#E8F5EE` | `#5AAD7A` |\n\n### 状态色\n\n| 角色 | 颜色值 | 浅色背景 |\n|------|--------|----------|\n| 成功 | `#2D8B55` | `#E8F5EE` |\n| 错误 | `#C93B3B` | `#FDE8E8` |\n| 信息 | `#2A7B9B` | `#E4F2F7` |\n\n### 角色颜色（用于聊天气泡、图表）\n\n| 角色 | 颜色值 | 名称 |\n|------|--------|------|\n| Actor 1 | `#D94F30` | 朱红色 |\n| Actor 2 | `#2A7B9B` | 青色 |\n| Actor 3 | `#7B6DAA` | 柔和梅红色 |\n| Actor 4 | `#D4A843` | 金色 |\n| Actor 5 | `#2D8B55` | 森林色 |\n\n### 边框与分隔线\n\n| 角色 | 颜色值 |\n|------|--------|\n| `--color-border` | `#E5DFD6` |\n| `--color-border-light` | `#EEEBE5` |\n\n### 语法高亮（Catppuccin 风格）\n\n| 类型 | 颜色值 | 用途 |\n|------|--------|------|\n| `.code-keyword` | `#CBA6F7` | if, else, return, function |\n| `.code-string` | `#A6E3A1` | 字符串 |\n| `.code-function` | `#89B4FA` | 函数名 |\n| `.code-comment` | `#6C7086` | 注释 |\n| `.code-number` | `#FAB387` | 数字 |\n| `.code-property` | `#F9E2AF` | 对象键 |\n| `.code-operator` | `#94E2D5` | =, =>, + |\n| `.code-tag` | `#F38BA8` | HTML 标签 |\n| `.code-attr` | `#F9E2AF` | HTML 属性 |\n| `.code-value` | `#A6E3A1` | 属性值 |\n\n---\n\n## 3. 字体规则\n\n### 字体族\n\n```css\n--font-display: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n--font-body:    'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n--font-mono:    'JetBrains Mono', 'Fira Code', 'Source Code Pro', 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;\n```\n\n> **注意**：使用系统字体回退，无需外部字体链接，确保中国大陆用户无需翻墙即可正常显示。\n\n### 层级表格\n\n| 角色 | 字体 | 大小 | 字重 | 行高 | 用途 |\n|------|------|------|------|------|------|\n| 模块编号 | Display | `3.75rem` (60px) | 800 | 1 | 大号背景装饰 |\n| 模块标题 | Display | `2.25rem` (36px) | 700 | 1.15 | 主标题 |\n| 模块副标题 | Body | `1.125rem` (18px) | 400 | 1.3 | 描述文字 |\n| 屏幕标题 | Display | `1.5rem` (24px) | 600 | 1.3 | 子标题 |\n| 正文 | Body | `1rem` (16px) | 400 | 1.6 | 段落文字 |\n| 引导段落 | Body | `1.125rem` (18px) | 400 | 1.6 | 重要段落 |\n| 代码 | Mono | `0.875rem` (14px) | 400 | 1.7 | 代码块 |\n| 标签/徽章 | Mono | `0.75rem` (12px) | 400 | - | 大写 + 0.05em 字间距 |\n\n### 类型比例（1.25 比例）\n\n```css\n--text-xs:   0.75rem;    /* 12px */\n--text-sm:   0.875rem;   /* 14px */\n--text-base: 1rem;       /* 16px */\n--text-lg:   1.125rem;   /* 18px */\n--text-xl:   1.25rem;    /* 20px */\n--text-2xl:  1.5rem;     /* 24px */\n--text-3xl:  1.875rem;   /* 30px */\n--text-4xl:  2.25rem;    /* 36px */\n--text-5xl:  3rem;       /* 48px */\n--text-6xl:  3.75rem;    /* 60px */\n```\n\n---\n\n## 4. 组件样式\n\n### 按钮\n\n**主要按钮**\n```css\n.btn-primary {\n  background: var(--color-accent);\n  color: white;\n  border: none;\n  padding: var(--space-2) var(--space-5);\n  border-radius: var(--radius-sm);\n  font-weight: 600;\n}\n.btn-primary:hover {\n  background: var(--color-accent-hover);\n  transform: translateY(-1px);\n}\n```\n\n**次要按钮**\n```css\n.btn {\n  background: var(--color-surface);\n  color: var(--color-text-secondary);\n  border: 1px solid var(--color-border);\n  padding: var(--space-2) var(--space-5);\n  border-radius: var(--radius-sm);\n}\n.btn:hover {\n  border-color: var(--color-accent-muted);\n  color: var(--color-accent);\n}\n```\n\n### 卡片与容器\n\n**基础卡片**\n```css\n.pattern-card {\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  padding: var(--space-6);\n  box-shadow: var(--shadow-sm);\n  border-top: 3px solid var(--color-accent);\n}\n.pattern-card:hover {\n  transform: translateY(-4px);\n  box-shadow: var(--shadow-md);\n}\n```\n\n**测验容器**\n```css\n.quiz-container {\n  background: var(--color-surface);\n  border-radius: var(--radius-lg);\n  padding: var(--space-8);\n  box-shadow: var(--shadow-md);\n}\n```\n\n**代码翻译块**\n```css\n.translation-block {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  border-radius: var(--radius-md);\n  overflow: hidden;\n  box-shadow: var(--shadow-md);\n}\n.translation-code {\n  background: var(--color-bg-code);\n  color: #CDD6F4;\n  padding: var(--space-6);\n}\n.translation-english {\n  background: var(--color-surface-warm);\n  padding: var(--space-6);\n  border-left: 3px solid var(--color-accent);\n}\n```\n\n### 徽章与标签\n\n**代码徽章**\n```css\n.badge-code {\n  font-family: var(--font-mono);\n  font-size: var(--text-sm);\n  background: var(--color-bg-code);\n  color: #CBA6F7;\n  padding: var(--space-1) var(--space-3);\n  border-radius: var(--radius-sm);\n}\n```\n\n**拖拽芯片**\n```css\n.dnd-chip {\n  background: var(--color-accent);\n  color: white;\n  border-radius: var(--radius-full);\n  padding: var(--space-2) var(--space-4);\n  font-weight: 600;\n  cursor: grab;\n}\n```\n\n### 导航\n\n**顶部导航**\n```css\n.nav {\n  position: fixed;\n  top: 0;\n  height: 50px;\n  background: rgba(250,247,242,0.92);\n  backdrop-filter: blur(8px);\n  border-bottom: 1px solid var(--color-border-light);\n}\n```\n\n**导航点**\n```css\n.nav-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  border: 2px solid var(--color-text-muted);\n  background: transparent;\n}\n.nav-dot.active {\n  border-color: var(--color-accent);\n  background: var(--color-accent);\n  box-shadow: 0 0 0 3px var(--color-accent-light);\n}\n.nav-dot.visited {\n  border-color: var(--color-accent-muted);\n  background: var(--color-accent-muted);\n}\n```\n\n### 提示框（Callout）\n\n```css\n.callout {\n  display: flex;\n  gap: var(--space-4);\n  padding: var(--space-5);\n  border-radius: var(--radius-md);\n  border-left: 4px solid;\n}\n.callout-accent  { background: var(--color-accent-light);  border-color: var(--color-accent); }\n.callout-info    { background: var(--color-info-light);    border-color: var(--color-info); }\n.callout-warning { background: var(--color-error-light);   border-color: var(--color-error); }\n```\n\n---\n\n## 5. 布局原则\n\n### 间距系统\n\n```css\n--space-1:  0.25rem;   /* 4px */\n--space-2:  0.5rem;    /* 8px */\n--space-3:  0.75rem;   /* 12px */\n--space-4:  1rem;      /* 16px */\n--space-5:  1.25rem;   /* 20px */\n--space-6:  1.5rem;    /* 24px */\n--space-8:  2rem;      /* 32px */\n--space-10: 2.5rem;    /* 40px */\n--space-12: 3rem;      /* 48px */\n--space-16: 4rem;      /* 64px */\n--space-20: 5rem;      /* 80px */\n--space-24: 6rem;      /* 96px */\n```\n\n### 网格与容器\n\n```css\n--content-width:      800px;   /* 标准阅读宽度 */\n--content-width-wide: 1000px;  /* 用于并排布局 */\n--nav-height:         50px;\n```\n\n### 模块布局\n\n```css\n.module {\n  min-height: 100dvh;\n  scroll-snap-align: start;\n  padding: var(--space-16) var(--space-6);\n  padding-top: calc(var(--nav-height) + var(--space-12));\n}\n.module-content {\n  max-width: var(--content-width);\n  margin: 0 auto;\n}\n```\n\n### 圆角刻度\n\n| 名称 | 值 | 用途 |\n|------|------|------|\n| `--radius-sm` | `8px` | 按钮、标签、输入框 |\n| `--radius-md` | `12px` | 卡片、容器 |\n| `--radius-lg` | `16px` | 大型容器、模态框 |\n| `--radius-full` | `9999px` | 胶囊形状、头像 |\n\n---\n\n## 6. 深度与层级\n\n### 阴影系统\n\n| 级别 | 值 | 用途 |\n|------|-----|------|\n| `--shadow-sm` | `0 1px 2px rgba(44,42,40,0.05)` | 微妙提升 |\n| `--shadow-md` | `0 4px 12px rgba(44,42,40,0.08)` | 卡片、容器 |\n| `--shadow-lg` | `0 8px 24px rgba(44,42,40,0.10)` | 浮动元素 |\n| `--shadow-xl` | `0 16px 48px rgba(44,42,40,0.12)` | 模态框 |\n\n**阴影哲学**：使用温暖色调的 RGBA（44, 42, 40），绝不使用纯黑色阴影。\n\n### 层级处理\n\n| 级别 | 处理方式 | 用途 |\n|------|----------|------|\n| Flat | 无阴影 | 页面背景 |\n| Subtle | shadow-sm | 标签、小卡片 |\n| Elevated | shadow-md | 交互卡片、测验 |\n| Floating | shadow-lg/xl | 提示框、模态 |\n\n---\n\n## 7. 宜与忌\n\n### 宜\n\n- 偶数模块使用 `--color-bg`，奇数模块使用 `--color-bg-warm`（交替背景创造视觉节奏）\n- 代码块始终使用 `--color-bg-code` 配浅色文本\n- 使用滚动吸附（scroll-snap）让用户逐模块浏览\n- 使用 `animate-in` 类实现滚动触发的淡入动画\n- 使用温暖色调的阴影\n- 代码换行显示，绝不出现水平滚动条\n- 从真实代码库中选择简短片段（5-10 行）\n\n### 忌\n\n- 绝不使用纯黑色阴影\n- 绝不使用紫色渐变作为主色调\n- 绝不修改、修剪或简化代码片段\n- 绝不在代码块中显示水平滚动条\n- 避免模块之间没有视觉区分\n- 避免使用过于鲜艳的颜色\n\n---\n\n## 8. 响应式行为\n\n### 断点表格\n\n| 名称 | 宽度 | 关键变化 |\n|------|------|----------|\n| Tablet | ≤768px | 标题尺寸缩小，代码/英语堆叠显示 |\n| Mobile | ≤480px | 进一步缩小标题，单列卡片，垂直流程图 |\n\n### 断点样式\n\n**平板 (≤768px)**\n```css\n:root {\n  --text-4xl: 1.875rem;\n  --text-5xl: 2.25rem;\n  --text-6xl: 3rem;\n}\n.translation-block { grid-template-columns: 1fr; }\n.pattern-cards { grid-template-columns: 1fr 1fr; }\n```\n\n**手机 (≤480px)**\n```css\n:root {\n  --text-4xl: 1.5rem;\n  --text-5xl: 1.875rem;\n  --text-6xl: 2.25rem;\n}\n.module { padding: var(--space-8) var(--space-4); }\n.pattern-cards { grid-template-columns: 1fr; }\n.flow-steps { flex-direction: column; }\n.flow-arrow { transform: rotate(90deg); }\n```\n\n---\n\n## 9. Agent 提示指南\n\n### 快速颜色参考\n\n```\n主背景:     #FAF7F2\n主文字:     #2C2A28\n强调色:     #D94F30\n代码背景:   #1E1E2E\n边框:       #E5DFD6\n```\n\n### 动画与过渡\n\n```css\n--ease-out:    cubic-bezier(0.16, 1, 0.3, 1);\n--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n--duration-fast:   150ms;\n--duration-normal: 300ms;\n--duration-slow:   500ms;\n--stagger-delay:   120ms;\n```\n\n### 滚动动画\n\n```css\n.animate-in {\n  opacity: 0;\n  transform: translateY(20px);\n  transition:\n    opacity var(--duration-slow) var(--ease-out),\n    transform var(--duration-slow) var(--ease-out);\n}\n.animate-in.visible {\n  opacity: 1;\n  transform: translateY(0);\n}\n```\n\n### 模块 HTML 模板\n\n```html\n<section class=\"module\" id=\"module-N\" style=\"background: var(--color-bg)\">\n  <div class=\"module-content\">\n    <header class=\"module-header animate-in\">\n      <span class=\"module-number\">0N</span>\n      <h1 class=\"module-title\">模块标题</h1>\n      <p class=\"module-subtitle\">一句话描述</p>\n    </header>\n    <div class=\"module-body\">\n      <!-- 内容 -->\n    </div>\n  </div>\n</section>\n```\n\n### 滚动吸附设置\n\n```css\nhtml {\n  scroll-snap-type: y proximity;\n  scroll-behavior: smooth;\n}\n```\n\n### 自定义滚动条\n\n```css\n::-webkit-scrollbar { width: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb {\n  background: var(--color-border);\n  border-radius: var(--radius-full);\n}\n```\n\nFile v1.2.0:skill-card.md\n\n## Description:\n\nConverts a codebase into an offline Chinese interactive HTML course, with a product-manager mode for business flows and a developer mode for architecture, data flow, and technical explanations.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[zhouchang1988](https://clawhub.ai/user/zhouchang1988)\n\n### License/Terms of Use:\n\nMIT\n\n## Use Case:\n\nDevelopers, product managers, and technical educators use this skill in Claude Code to turn local or GitHub code repositories into Chinese, offline-ready interactive browser courses for business or technical onboarding.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Generated interactive HTML may run injected browser code when untrusted repository content is incorporated into unsafe browser-rendered content.\n\nMitigation: Use trusted repositories, inspect generated module HTML before opening index.html, and add strict escaping or sanitization before using the skill with untrusted input.\n\nRisk: The skill reads the target repository and writes a generated course directory.\n\nMitigation: Run it in a controlled workspace and review generated files before sharing, opening, or deploying them.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/zhouchang1988/skills/codebase-to-course-cn)\n- [README](README.md)\n- [Design system overview](DESIGN.md)\n- [Product-manager content philosophy](references/content-philosophy.md)\n- [Developer content philosophy](references/content-philosophy-pro.md)\n- [Design system reference](references/design-system.md)\n- [Interactive elements reference](references/interactive-elements.md)\n- [Common gotchas](references/gotchas.md)\n- [Module brief template](references/module-brief-template.md)\n- [Inspiration project link from README](https://github.com/zarazhangrui/codebase-to-course)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance, files]\n\n**Output Format:** [Markdown guidance plus generated HTML, CSS, JavaScript, shell script, and assembled browser course files.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Creates a course directory named from user-provided keywords and version mode; generated courses are intended to run offline.]\n\n## Skill Version(s):\n\n1.2.0 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.2.0:LICENSE\n\nMIT License\n\nCopyright (c) 2026 zhouchang1988\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\nArchive v1.1.4: 16 files, 57521 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3479b), references/_base.html (2850b), references/_footer.html (61b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (5970b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35313b), SKILL.md (12556b), _meta.json (140b)\n\nFile v1.1.4:SKILL.md\n\n---\nname: codebase-to-course-cn\ndescription: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。\n---\n\n# 代码库转课程\n\n将任意代码库转换为精美的交互式课程。输出是一个**目录**，包含预构建的 `styles.css`、`main.js`、每个模块的 HTML 文件以及组装好的 `index.html` — 可直接在浏览器中打开，无需任何设置。\n\n**支持两个版本：**\n- **产品版（默认）**：面向产品经理，聚焦业务逻辑、用户流程和系统架构，不含代码细节\n- **开发版**：面向程序员，包含架构图、数据流动画、技术方案描述\n\n**重要：中国大陆可访问性要求**\n\n此技能面向中国大陆用户，**必须确保生成的课程在中国大陆网络环境下可以完全离线运行**：\n- **禁止使用 Google Fonts** 及任何 Google CDN 资源\n- **禁止使用外部 CDN** 加载字体、样式或脚本\n- **禁止依赖任何需要翻墙才能访问的资源**\n- 所有字体必须使用系统字体或内嵌字体\n- 所有资源必须本地化，确保零网络依赖\n\n## 首次运行欢迎语\n\n当技能首次触发且用户尚未指定代码库时，介绍自己并说明你的功能：\n\n> **我可以将任意代码库转换为交互式课程，讲解其工作原理。**\n>\n> 只需指向一个项目：\n> - **本地文件夹** — 例如，\"将 ./my-project 转换为课程\"\n> - **GitHub 链接** — 例如，\"从 https://github.com/user/repo 制作课程\"\n> - **当前项目** — 如果你已经在代码库中，只需说\"将此转换为课程\"\n>\n> 我可以生成两个版本的课程：\n> - **产品版（默认）**：面向产品经理，讲清业务逻辑和系统架构\n> - **开发版**：面向程序员，聚焦技术架构和实现细节\n\n**询问用户需要哪个版本：**\n\n> 你希望生成哪个版本？\n> 1. **产品版** — 面向产品经理，聚焦业务逻辑、用户流程和整体架构（默认）\n> 2. **开发版** — 面向专业程序员，聚焦架构图、数据流和技术方案\n>\n> 也可以带上版本继续，例如\"将此转换为开发版课程\"。\n\n**关于测验：**\n- 产品版 **不包含测验**\n- 开发版 **默认不需要测验**，仅当用户明确要求\"增加测验\"时才添加\n\n如果用户提供 GitHub 链接，在开始分析之前先克隆仓库（`git clone <url> /tmp/<repo-name>`）。如果他们说\"此代码库\"或类似表述，使用当前工作目录。\n\n---\n\n## 产品版 vs 开发版 对比\n\n| 特性 | 产品版（默认） | 开发版 |\n|---|---|---|\n| **目标受众** | 产品经理、业务人员 | 专业程序员 |\n| **教学方式** | 业务流程图、用户旅程、系统架构概览 | 架构图、数据流动画、技术方案 |\n| **代码展示** | **不展示代码** | 精选10-15行核心代码 + 设计意图 |\n| **测验** | **无** | **默认不需要**（用户要求时才添加） |\n| **视觉元素** | 流程图、业务架构图、用户旅程图、功能模块图 | 架构图、时序图、数据流图 |\n| **语言风格** | 业务语言、产品术语、结构化清晰 | 简洁、信息密度高、技术术语 |\n\n---\n\n## 产品版详细指南\n\n目标学习者是**产品经理和业务人员** —— 需要理解系统做什么、业务逻辑怎么流转、各模块如何协作，但不需要知道代码细节。\n\n**关键原则：**\n- 不展示任何代码\n- 用业务语言解释系统行为，避免技术实现细节\n- 聚焦\"系统做了什么\"而非\"代码怎么写\"\n- 用流程图和架构图可视化业务逻辑\n- 关注用户旅程、数据流转、模块职责\n- 解释清楚各功能模块的输入/输出/边界\n\n**强制交互元素（每个模块必须包含）：**\n- **业务流程图** — 至少一个（展示核心业务逻辑流转）\n- **系统架构概览图** — 整个课程至少2个（模块关系、职责划分）\n- **用户旅程图** — 整个课程至少一个（端到端用户操作路径）\n- **功能模块卡片** — 展示各模块职责、输入输出\n- **数据流转动画** — 整个课程至少一个（业务数据如何在系统中流动）\n\n**禁止包含：**\n- 代码片段（任何形式）\n- 代码翻译块\n- 测验题\n- 技术实现细节（如算法、数据结构、设计模式名称）\n- 词汇表提示（产品经理不需要学习编程术语）\n\n---\n\n## 开发版详细指南\n\n目标学习者是**专业程序员** —— 需要快速理解代码库架构、准备技术分享或代码评审。\n\n**核心原则：图表驱动，代码精简。**\n\n**关键原则：**\n- 信息密度优先，减少\"废话\"\n- 直接使用技术术语，无需解释基础概念\n- **图表和动画优先于代码贴片**\n- 代码仅作为补充（每片段10-15行）\n- 60%+ 图表/动画，25% 简洁文字，15% 精选代码\n\n**测验为可选功能：** 默认不包含。仅当用户明确要求\"增加测验\"或\"需要测验题\"时才添加。\n\n**强制交互元素：**\n- **架构图** — 每个课程至少2个（交互式架构图，点击组件显示描述）\n- **数据流动画** — 每个课程至少2个（请求/响应流转）\n- **时序图/状态图** — 每个课程至少1个\n- **代码分析块** — 整个课程2-3个（不是每个模块都需要）\n\n**内容比例：**\n- 60%+ 图表、动画、交互式可视化\n- 25% 文字描述（技术方案、设计决策）\n- 15% 代码片段（每片段10-15行）\n\n---\n\n## 流程\n\n### 阶段 0：选择版本\n\n在开始前，通过询问或用户明确指令确定课程版本：\n\n> **生成哪个版本？**\n> - **产品版**（默认）：业务逻辑课程，适合产品经理，无代码无测验\n> - **开发版**：技术架构课程，适合程序员，默认无测验\n\n如果用户说\"开发版\"、\"技术版\"、\"架构课程\"或类似词语，使用**开发版**。\n如果用户说\"产品版\"、\"给PM看的\"、\"业务逻辑\"或没有指定，使用**产品版**（默认）。\n\n**记录版本选择**，后续所有内容创作都遵循对应版本的规则。\n\n### 阶段 1：代码库分析\n\n**产品版分析重点：**\n- 系统整体做了什么（产品定位、核心价值）\n- 主要功能模块及其职责（用业务语言描述）\n- 核心用户旅程（用户怎么用这个系统）\n- 业务数据流转（数据从哪来、到哪去、经过什么处理）\n- 模块间的协作关系（谁调用谁、谁依赖谁）\n- 系统边界（与外部系统的交互点）\n- 关键业务规则和约束\n\n**开发版分析重点：**\n- 架构风格（分层？微服务？单体？事件驱动？）\n- 核心抽象（领域模型、关键接口、主要数据结构）\n- 模块边界（职责划分、依赖关系、通信机制）\n- 数据流路径（从请求到响应的完整链路）\n- 设计决策痕迹（从代码和注释推断\"为什么\"）\n- 技术选型理由（为什么用 X 而不是 Y？）\n- 扩展机制（插件系统？钩子？配置？）\n- 测试策略、部署拓扑\n\n**自己弄清楚应用做什么**，通过阅读 README、主要入口点和核心代码。不要让用户解释产品。\n\n### 阶段 2：课程设计\n\n将课程结构化为 **4-6 个模块**。\n\n**产品版模块结构示例：**\n| 模块 | 目的 |\n|---|---|\n| 1 | 产品全景（系统做什么、解决什么问题、核心价值） |\n| 2 | 功能模块地图（各模块职责与边界） |\n| 3 | 核心用户旅程（端到端操作流程） |\n| 4 | 数据流转（业务数据如何在系统中流动） |\n| 5 | 模块协作（各部分如何配合完成业务） |\n| 6 | 系统边界与扩展（外部集成、未来可扩展方向） |\n\n**开发版模块结构示例：**\n| 模块 | 目的 |\n|---|---|\n| 1 | 架构全景（系统边界、主要组件） |\n| 2 | 核心抽象与领域模型 |\n| 3 | 数据流与请求生命周期 |\n| 4 | 设计模式与实现技巧 |\n| 5 | 扩展点与自定义 |\n| 6 | 技术债务与演进方向 |\n\n**每个模块应包含：**\n- 3-6 个屏幕（模块内流动的子节）\n- **产品版**：至少一个业务流程图或架构概览图、功能模块卡片\n- **开发版**：至少一个架构图或数据流图、至多一个关键代码分析块\n\n**测验要求：**\n- **产品版**：不包含测验\n- **开发版**：默认无测验，仅当用户明确要求\"增加测验\"时才添加\n\n**不要提交课程计划供审批 —— 直接构建它。**\n\n**设计课程计划后，决定使用哪种构建路径：**\n- **简单代码库**（单一用途 CLI、小型库、清晰入口点、5 个或更少模块）→ 直接进入阶段 3 顺序路径\n- **复杂代码库**（全栈应用、多个服务、单体仓库或 6+ 个模块）→ 先进入阶段 2.5，然后阶段 3 并行路径\n\n### 阶段 2.5：模块简报（仅限复杂代码库）\n\n阅读对应版本的参考文件：\n- **产品版**：`references/content-philosophy.md`\n- **开发版**：`references/module-brief-template.md` + `references/content-philosophy-pro.md`\n\n**对于每个模块，将简报写入 `course-name/briefs/0N-slug.md`，包含：**\n- 教学目标（学习者应获得什么）\n- 预提取的代码片段\n- 交互元素清单\n- 相关设计决策文档\n\n### 阶段 3：构建课程\n\n课程输出是一个**目录**。\n\n**输出结构：**\n```\ncourse-name/\n  styles.css       ← 从 references/styles.css 逐字复制\n  main.js          ← 从 references/main.js 逐字复制\n  _base.html       ← 定制的外壳（标题、强调色、导航点）\n  _footer.html     ← 从 references/_footer.html 逐字复制\n  build.sh         ← 从 references/build.sh 逐字复制\n  briefs/          ← 模块简报（仅限复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 由 build.sh 组装\n```\n\n**步骤 1：设置** — 读取并复制四个基础文件\n\n**步骤 2：定制 `_base.html`** — 替换标题、强调色、导航点\n\n**GitHub 仓库链接处理：**\n- 如果课程来源于 GitHub 仓库（用户提供了 GitHub URL），保留 `_base.html` 中的 `.nav-repo-link` 元素，将 `REPO_URL` 替换为实际的 GitHub 仓库地址\n- 如果课程来源于本地目录或当前项目（非 GitHub），**删除整个 `.nav-repo-link` 元素**\n\n**步骤 3：编写模块** — 根据版本选择参考文件\n\n选择对应的 content-philosophy 文件：\n- **产品版**：阅读 `references/content-philosophy.md`\n- **开发版**：阅读 `references/content-philosophy-pro.md`\n\n同时阅读：\n- `references/gotchas.md` — 常见失败点\n- `references/interactive-elements.md` — 交互元素实现模式\n- `references/design-system.md` — 视觉约定\n\n对于每个模块，编写 `course-name/modules/0N-slug.html`，只包含 `<section class=\"module\" id=\"module-N\">` 块及其内容。\n\n**步骤 4：组装** — 从课程目录运行 `build.sh`：\n```bash\ncd course-name && bash build.sh\n```\n\n### 阶段 4：审查和打开\n\n运行 `build.sh` 后，在浏览器中打开 `index.html`。引导用户浏览构建的内容，并征求反馈。\n\n---\n\n## 设计身份\n\n视觉设计应该像一个**精美的开发者笔记本** —— 温暖、诱人且独特。\n\n- **产品版**：温暖调色板（米白背景、温暖灰色）、大胆强调色（朱红、珊瑚、青色）、充足留白、信息层次清晰\n- **开发版**：简洁调色板（浅灰背景、深色文字）、专业强调色（蓝、绿、橙）、信息密度高\n\n两者使用相同的 CSS/JS 基础，但通过内容组织和视觉元素来实现不同的体验。\n\n---\n\n## 参考文件\n\n`references/` 目录包含详细规范。**只在到达相关阶段时阅读它们**。\n\n**共享参考文件：**\n- `references/design-system.md` — CSS 自定义属性、调色板、排版\n- `references/interactive-elements.md` — 交互元素实现模式\n- `references/gotchas.md` — 常见失败点\n- `references/module-brief-template.md` — 模块简报模板\n\n**版本特定参考文件：**\n- `references/content-philosophy.md` — 产品版内容规则\n- `references/content-philosophy-pro.md` — 开发版内容规则\n\n## 输出语言\n\n本技能的输出内容默认使用中文。除非用户明确要求其他语言，否则：\n- 文档和说明使用中文\n- 技术术语保持原文或使用中英对照\n\nFile v1.1.4:README.md\n\n# codebase-to-course-cn\n\n> 将任意代码库转换为精美的交互式课程，直接在浏览器中打开，无需任何设置。\n\n**灵感来自 [codebase-to-course](https://github.com/zarazhangrui/codebase-to-course)** — 在原项目思路基础上进行了中文本地化适配和功能增强。\n\n## 它是什么\n\n一个 Claude Code 技能（Skill），能够分析任意代码库并生成交互式 HTML 课程。输出是一个完整目录，包含 `styles.css`、`main.js`、模块 HTML 文件和组装好的 `index.html`，双击即可在浏览器中浏览。\n\n## 两个版本\n\n| | 产品版（默认） | 开发版 |\n|---|---|---|\n| **面向谁** | 产品经理、业务人员 | 专业程序员 |\n| **怎么教** | 业务流程图、用户旅程、系统架构概览 | 架构图、数据流动画、技术方案 |\n| **代码展示** | 不展示代码 | 精选 10-15 行核心代码 |\n| **测验** | 无 | 默认不含，按需添加 |\n| **视觉风格** | 温暖亲切，像翻阅教材 | 简洁专业，信息密度高 |\n\n## 交互元素\n\n- **代码↔中文翻译块** — 左侧代码，右侧通俗解释（开发版）\n- **群聊动画** — 组件之间的对话模拟（产品版）\n- **业务流程图** — 核心业务逻辑流转可视化（产品版）\n- **数据流/消息流动画** — 请求从 A 到 B 的可视化\n- **架构图** — 点击组件查看描述（开发版）\n- **时序图/状态图** — 生命周期与状态流转（开发版）\n- **嵌入式测验** — 多选、场景、拖放、找 bug（开发版，按需添加）\n- **词汇表提示** — 技术术语首次出现时自动标注（产品版）\n\n## 使用方式\n\n在 Claude Code 中触发：\n\n```\n将 ./my-project 转换为课程\n```\n\n或指定版本：\n\n```\n将此转换为开发版课程\n```\n\n或从 GitHub 仓库生成：\n\n```\n从 https://github.com/user/repo 制作课程\n```\n\n## 中国大陆适配\n\n本技能面向中国大陆用户，**所有生成的课程均可完全离线运行**：\n\n- 禁止使用 Google Fonts 及任何 Google CDN 资源\n- 禁止使用外部 CDN 加载字体、样式或脚本\n- 字体使用系统字体回退（PingFang SC、Microsoft YaHei）\n- 零网络依赖，断网也能正常浏览\n\n## 生成的课程结构\n\n```\ncourse-name/\n  styles.css       ← 预构建样式\n  main.js          ← 交互逻辑\n  _base.html       ← 页面外壳\n  _footer.html     ← 页脚\n  build.sh         ← 组装脚本\n  briefs/          ← 模块简报（复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 组装后的完整课程\n```\n\n## 技术细节\n\n- **视觉设计**：教育出版物风格，温暖米白背景（`#FAF7F2`），朱红强调色（`#D94F30`），Catppuccin 语法高亮\n- **布局**：滚动吸附（scroll-snap），逐模块翻页浏览\n- **动画**：滚动触发的淡入效果，渐进式内容揭示\n- **响应式**：支持桌面、平板、手机三种断点\n\n## 与原项目的关系\n\n本项目是 [codebase-to-course](https://github.com/zarazhangrui/codebase-to-course) 的中文本地化衍生版本，主要变更：\n\n- 课程内容默认输出中文\n- 移除所有外部 CDN 依赖，确保中国大陆可访问\n- 字体栈替换为中文系统字体回退\n- 新增产品版和开发版双模式（产品版聚焦业务逻辑，开发版聚焦架构图、数据流动画、技术方案）\n- 交互元素实现模式本地化适配\n\n## 许可证\n\n与原项目保持一致。\n\nFile v1.1.4:_meta.json\n\n{\n  \"ownerId\": \"kn7a799d479xj5aftb4bqm6ea984cv4m\",\n  \"slug\": \"codebase-to-course-cn\",\n  \"version\": \"1.1.4\",\n  \"publishedAt\": 1779165126966\n}\n\nFile v1.1.4:references/content-philosophy-pro.md\n\n# 内容哲学 — 专业程序员版\n\n> **何时阅读此文件：** 在阶段 3（编写模块 HTML）期间。这些原则指导每个内容决策。\n\n这些原则是将优秀技术课程与普通教程区分开来的关键。\n\n### 信息密度优先\n\n专业程序员阅读速度快，理解能力强。课程应该信息密度高，减少\"废话\"。\n\n**文字原则：**\n- 每个段落有明确信息增量。如果没有新信息，删掉它。\n- 直接使用技术术语，无需解释基础概念。\n- **图表和动画优先于代码贴片。** 能用架构图、数据流动画、时序图表达的，就用可视化。\n- **代码仅作为补充。** 仅在图表无法替代时才展示代码，且每片段控制在 10-15 行。\n- 60%+ 的屏幕应该是图表、动画或交互元素，25% 是简洁的文字描述，15% 是精选代码。\n\n**将描述转换为可视化（优先级排序）：**\n- \"组件 A 调用组件 B\" → **数据流动画**（首选）或**时序图**\n- \"数据从 X 流向 Y\" → **数据流动画**\n- \"系统架构\" → **交互式架构图**（点击组件显示描述）\n- \"请求处理流程\" → **流程图** + **步骤卡片**\n- \"状态变化\" → **状态图动画**\n- \"这个函数的核心逻辑\" → **精简代码片段（10-15 行）+ 设计意图注释**（仅在可视化不足以表达时使用）\n\n**避免的做法：**\n- 不要贴超过 15 行的代码块 — 如果需要展示更多代码，拆分为多个小片段，每个片段聚焦一个概念\n- 不要连续放置两个代码块而中间没有可视化元素\n- 不要用代码来展示可以用图表表达的关系和流程\n\n### 代码分析块 — 精选且聚焦\n\n面向专业程序员的代码分析，重点不是\"这段代码做什么\"（他们能看懂代码），而是\"为什么要这样设计\"。**一个课程中只需要 2-3 个代码分析块**，每个聚焦最核心的设计决策。\n\n**代码选取原则：**\n- 每片段 **不超过 10-15 行** — 选取最能体现设计意图的核心代码\n- 优先选取：接口定义、核心数据结构、关键算法的核心逻辑\n- 不选取：配置代码、样板代码、工具函数、导入语句\n\n**结构：**\n- 左侧：精选的核心代码（保持格式，允许水平滚动）\n- 右侧：设计意图注释（不是逐行翻译）\n\n**示例：**\n\n```html\n<div class=\"translation-block animate-in\">\n  <div class=\"translation-code\">\n    <span class=\"translation-label\">CODE</span>\n    <pre><code>\n<span class=\"code-line\"><span class=\"code-keyword\">export class</span> <span class=\"code-function\">EventBus</span> {</span>\n<span class=\"code-line\">  <span class=\"code-keyword\">private</span> listeners = <span class=\"code-keyword\">new</span> <span class=\"code-function\">Map</span>&lt;string, Set&lt;Function&gt;&gt;();</span>\n<span class=\"code-line\"></span>\n<span class=\"code-line\">  <span class=\"code-function\">subscribe</span>(event: string, fn: Function) {</span>\n<span class=\"code-line\">    <span class=\"code-keyword\">if</span> (!<span class=\"code-keyword\">this</span>.listeners.has(event)) {</span>\n<span class=\"code-line\">      <span class=\"code-keyword\">this</span>.listeners.set(event, <span class=\"code-keyword\">new</span> Set());</span>\n<span class=\"code-line\">    }</span>\n<span class=\"code-line\">    <span class=\"code-keyword\">this</span>.listeners.get(event)!.add(fn);</span>\n<span class=\"code-line\">  }</span>\n<span class=\"code-line\">}</span>\n    </code></pre>\n  </div>\n  <div class=\"translation-english\">\n    <span class=\"translation-label\">DESIGN INTENT</span>\n    <div class=\"translation-lines\">\n      <p class=\"tl\"><strong>为什么用 Map + Set？</strong></p>\n      <p class=\"tl\">Map 提供 O(1) 事件查找，Set 自动去重，防止同一监听器被多次注册。这是比数组更高效的选择。</p>\n      <p class=\"tl\"><strong>扩展点：</strong>可替换为 WeakMap 避免内存泄漏，或添加优先级支持。</p>\n    </div>\n  </div>\n</div>\n```\n\n**关键：**\n- 代码保持原始格式，**允许水平滚动**（与 cn 版本不同）\n- 右侧解释\"为什么\"，不是\"什么\"\n- 指出设计权衡、扩展点、潜在陷阱\n\n### 架构图 — 课程的核心视觉元素\n\n架构图是课程中**最重要的元素**。每个课程必须包含至少 2 个架构图。使用交互式架构图（点击组件显示描述）。\n\n**架构图类型：**\n- **组件图** — 展示模块边界和依赖关系\n- **层次图** — 展示分层架构（表现层、业务层、数据层）\n- **部署图** — 展示服务拓扑和数据流\n\n**设计原则：**\n- 清晰的边界和箭头方向\n- 使用颜色区分不同类型的组件\n- 标注关键数据流路径\n- 包含图例\n- **每个组件可点击**，显示职责描述和关键接口\n\n### 数据流动画 — 追踪请求生命周期\n\n数据流动画是仅次于架构图的第二重要元素。每个课程至少 2 个。展示请求从入口到响应的完整旅程。\n\n**内容：**\n- 入口点（路由、控制器）\n- 中间件链\n- 业务逻辑层\n- 数据访问层\n- 外部服务调用\n- 响应返回路径\n\n**动画化：** 使用 `data-steps` 属性创建分步动画，让学习者逐步追踪数据流。\n\n### 技术方案描述 — 简洁文字替代大段代码\n\n对于技术方案和实现细节，优先使用**简洁的文字描述 + 可视化**，而不是贴大段代码。\n\n**描述模式：**\n- **方案概述** — 用 2-3 句话说明技术方案的核心思路\n- **关键决策** — 用提示框（callout）标注为什么选择这种方案\n- **实现要点** — 用编号步骤卡片（step-cards）列出关键实现步骤\n- **核心代码** — 仅在上述方式无法充分表达时，展示 10-15 行最核心的代码\n\n**示例（好的方式）：**\n> 认证系统采用 JWT + Refresh Token 双 token 方案。Access Token 有效期 15 分钟，存储在内存中；Refresh Token 有效期 7 天，存储在 HttpOnly Cookie 中。Token 验证通过中间件统一处理，失败后自动尝试刷新。\n>\n> [数据流动画：用户请求 → 中间件验证 → Token 过期 → 刷新流程 → 重新请求]\n\n**示例（差的方式）：**\n> [贴 50 行 auth middleware 代码]\n> [贴 30 行 token refresh 代码]\n> [贴 20 行 cookie 设置代码]\n\n### 测验（可选功能）\n\n**重要：测验默认不包含。** 仅当用户明确要求\"增加测验\"或\"需要测验题\"时才添加。\n\n测验应该测试学习者能否**应用**知识解决新问题，而不是回忆事实。\n\n**测验类型（按价值排序）：**\n\n1. **设计决策题** — \"如果要添加 X 功能，你会选择哪种方式？为什么？\"\n   ```\n   题目：团队决定添加一个批量导出功能，预期数据量可能达到 100 万条。\n   你会选择哪种实现方式？\n\n   A. 同步导出，直接在内存中处理后返回文件\n   B. 异步任务队列，处理完成后发送邮件通知\n   C. 流式处理，边读边写，不占用太多内存\n\n   正确答案：C 或 B（取决于具体需求）\n   解释：同步导出会超时；异步适合大规模但需要用户等待通知；\n   流式处理内存效率最高，但需要实现复杂的流控逻辑...\n   ```\n\n2. **架构修改题** — \"如果要将单体拆分为微服务，你会如何划分边界？\"\n\n3. **问题诊断题** — \"用户报告 X 功能变慢，根据你学的架构，可能是什么原因？\"\n\n4. **代码定位题** — \"如果要修改 Y 行为，你需要修改哪些文件？\"\n\n**不测验的内容：**\n- 文件名记忆\n- 语法细节\n- 可以通过 Ctrl+F 找到的事实\n\n### 设计笔记提示框\n\n使用提示框标注重要的设计决策和权衡：\n\n```html\n<div class=\"callout callout-info\">\n  <div class=\"callout-title\">设计笔记</div>\n  <p>这里使用策略模式而不是 if-else，是为了支持未来添加新的支付方式而不修改核心逻辑。这是开闭原则的应用。</p>\n</div>\n```\n\n**类型：**\n- **设计决策** — \"为什么这样设计\"\n- **权衡说明** — \"选择了 A 而不是 B，因为...\"\n- **陷阱警告** — \"注意：这里容易出错\"\n- **扩展点** — \"如果要添加 X，可以在这里扩展\"\n\n### 技术术语处理\n\n专业程序员了解基础术语，但可能不熟悉：\n- 项目特定的术语和缩写\n- 特定框架/库的概念\n- 公司内部约定\n\n**处理方式：**\n- 项目特定术语：首次出现时用词汇表提示\n- 通用技术术语：直接使用，不解释\n- 框架特定概念：简要说明与项目的关联\n\n### 模块间的技术深度递进\n\n模块之间的深度应该递进，可视化复杂度也应递进：\n\n1. **模块 1-2**：架构层面 — 交互式架构图、组件关系图、层次图，用文字描述整体设计思路\n2. **模块 3-4**：实现层面 — 数据流动画、时序图、精选核心代码片段（2-3 个），用步骤卡片描述关键流程\n3. **模块 5-6**：进阶层面 — 状态图、场景对比图，用提示框讨论扩展策略和技术债务\n\n不要在早期模块深入代码细节，也不要在后期模块重复架构概念。\n\n### 实用性导向\n\n每个模块都应该回答一个实用问题：\n- \"我在哪里添加新功能？\"\n- \"我如何定位 bug？\"\n- \"我如何理解这个错误？\"\n- \"我如何扩展这个系统？\"\n\n如果模块不能帮助学习者解决实际问题，重新设计它。\n\nFile v1.1.4:references/content-philosophy.md\n\n# 内容哲学 — 产品经理版\n\n> **何时阅读此文件：** 在阶段 2.5（编写模块简报）和阶段 3（编写模块 HTML）期间。这些原则指导每个内容决策 —— 展示什么、如何解释以及用什么方式呈现。\n\n这些原则是让产品经理真正看懂系统的关键。\n\n### 核心原则：业务语言，零代码\n\n产品经理需要理解\"系统做了什么\"和\"业务逻辑怎么流转\"，不需要知道\"代码怎么写\"。\n\n**绝对禁止：**\n- 任何代码片段（不论长短）\n- 代码翻译块\n- 技术实现细节（算法名称、数据结构、设计模式术语）\n- 编程术语的词汇表提示\n- 测验题\n\n**语言规范：**\n- 用业务语言描述系统行为：\"用户提交订单后，系统自动校验库存\"而非\"调用 checkInventory() 方法\"\n- 用\"模块\"、\"服务\"、\"功能\"等产品经理熟悉的词汇\n- 避免\"类\"、\"函数\"、\"接口\"、\"中间件\"等编程术语\n- 可以提及技术组件的名字（如\"数据库\"、\"缓存\"、\"消息队列\"），但只解释它在业务中的角色\n\n### 展示，不要讲述 —— 极致视觉化\n\n产品经理习惯看流程图和架构图。课程应该更像产品文档而不是技术文档。\n\n**文字限制：**\n- 每个文字块最多 **3-4 个句子**。如果你在写第五句，停下来把它转换为视觉元素。\n- 每个屏幕必须 **至少 60% 是视觉内容**（流程图、架构图、卡片、模块关系图）。\n\n**将文字转换为视觉元素：**\n- 步骤描述 → **流程图**（带清晰的开始/结束/分支）\n- \"模块 A 和模块 B 协作\" → **系统架构图**（带箭头和标注）\n- \"这个功能包含 X、Y、Z\" → **功能模块卡片**（图标 + 一句话描述）\n- \"用户先做 A，再做 B\" → **用户旅程图**（带步骤编号）\n- \"数据从 X 流到 Y\" → **数据流转动画**\n- 列表描述 → **带图标的卡片网格**\n- 条件逻辑 → **决策树/分支流程图**\n\n**视觉呼吸空间：**\n- 在元素之间使用充足的间距\n- 在全宽视觉元素和窄文字块之间交替，创造节奏\n- 每个模块应该至少有一个\"英雄视觉\" —— 一个主导屏幕的图表或交互元素\n\n### 业务流程图 — 课程的核心\n\n业务流程图是产品版课程中**最重要的元素**。每个模块至少一个。\n\n**流程图类型：**\n- **用户操作流程** — 用户从开始到完成某个目标的完整路径\n- **业务逻辑流程** — 系统内部业务规则的执行顺序和分支\n- **审批/状态流程** — 数据或实体的状态变迁\n- **异常处理流程** — 出错时系统的应对策略\n\n**设计原则：**\n- 用业务语言标注每个节点（\"校验用户身份\"而非\"auth middleware\"）\n- 分支条件用业务条件表述（\"库存充足？\"而非\"inventory > 0\"）\n- 标注关键的输入和输出\n- 使用颜色区分正常路径和异常路径\n\n### 系统架构概览 — 让PM看到全景\n\n用简化的架构图展示系统的\"大图\"。不需要技术细节，只需要模块边界和职责。\n\n**展示内容：**\n- 各功能模块的名称和一句话职责\n- 模块之间的调用/依赖关系（用箭头和简短标注）\n- 外部系统的集成点\n- 数据存储的位置和用途\n\n**不展示：**\n- 具体的技术实现（框架名、库名）\n- 代码层面的依赖关系\n- 部署细节\n\n### 用户旅程图 — 端到端的产品体验\n\n从用户的视角描述使用系统的完整流程。\n\n**结构：**\n- 明确的起点（用户意图）和终点（目标达成）\n- 每一步标注：用户做了什么 → 系统做了什么 → 用户看到什么\n- 标注关键的决策点和分支路径\n- 标注可能出错的地方和系统的应对\n\n### 功能模块卡片 — 职责一目了然\n\n每个模块用一张卡片展示其核心信息。\n\n**卡片包含：**\n- 模块名称（业务名称，不是代码文件名）\n- 一句话职责描述\n- 核心功能列表（3-5 条）\n- 与其他模块的关系（谁给它数据、它给谁数据）\n\n### 数据流转 — 讲清楚\"数据去哪了\"\n\n产品经理最关心的问题之一：数据从哪来、怎么处理、到哪去。\n\n**展示方式：**\n- 用动画展示数据在系统中的流动路径\n- 每个节点标注\"对数据做了什么\"（而非\"用什么技术做的\"）\n- 标注数据格式的变化（如\"用户填的表单\" → \"存储的订单记录\"）\n- 标注数据持久化的位置\n\n### 每个屏幕一个概念\n\n不要堆砌信息。模块内的每个屏幕讲清楚一件事。\n\n**好的屏幕主题：**\n- \"订单创建的完整流程\"\n- \"支付模块和库存模块如何配合\"\n- \"系统如何处理退款请求\"\n\n**差的屏幕主题：**\n- \"后端架构和所有API\" — 太大了，拆分\n- \"技术栈介绍\" — 产品经理不需要这个\n\n### 业务规则可视化\n\n系统中的业务规则是产品经理最需要理解的部分。\n\n**展示方式：**\n- 用决策树展示条件逻辑（\"如果 VIP 用户 → 免运费；如果新用户 → 首单折扣\"）\n- 用状态图展示实体的生命周期（订单：待支付 → 已支付 → 已发货 → 已完成）\n- 用表格对比不同场景的处理方式\n\n### 模块间内容递进\n\n模块之间应该从宏观到微观递进：\n\n1. **模块 1-2**：全景层 — 系统做什么、有哪些大模块、核心价值\n2. **模块 3-4**：流程层 — 核心业务流程、用户旅程、数据流转\n3. **模块 5-6**：细节层 — 模块协作细节、边界情况、扩展方向\n\n### 实用性导向\n\n每个模块都应该回答产品经理关心的问题：\n- \"这个系统的核心业务逻辑是什么？\"\n- \"用户操作后系统内部发生了什么？\"\n- \"各模块之间是怎么配合的？\"\n- \"数据是怎么流转和存储的？\"\n- \"如果要加新功能，会影响哪些模块？\"\n- \"系统的边界在哪里？和哪些外部系统有交互？\"\n\n如果模块不能帮助产品经理理解业务逻辑或做出产品决策，重新设计它。\n\nFile v1.1.4:references/design-system.md\n\n# 设计系统参考\n\n课程的完整 CSS 设计 token。将整个 `:root` 块复制到课程 HTML 中，并调整强调色以适应项目的个性。\n\n## 目录\n1. [调色板](#调色板)\n2. [排版](#排版)\n3. [间距与布局](#间距与布局)\n4. [阴影与深度](#阴影与深度)\n5. [动画与过渡](#动画与过渡)\n6. [导航与进度](#导航与进度)\n7. [模块结构](#模块结构)\n8. [响应式断点](#响应式断点)\n9. [滚动条与背景](#滚动条与背景)\n\n---\n\n## 调色板\n\n```css\n:root {\n  /* --- 背景 --- */\n  --color-bg:             #FAF7F2;       /* 温暖的米白色，像旧纸张 */\n  --color-bg-warm:        #F5F0E8;       /* 稍微更温暖，用于交替模块 */\n  --color-bg-code:        #1E1E2E;       /* 深靛蓝炭黑，用于代码块 */\n  --color-text:           #2C2A28;       /* 深炭黑，对眼睛友好 */\n  --color-text-secondary: #6B6560;       /* 温暖的灰色，用于次要文本 */\n  --color-text-muted:     #9E9790;       /* 柔和的，用于时间戳、标签 */\n  --color-border:         #E5DFD6;       /* 微妙的温暖边框 */\n  --color-border-light:   #EEEBE5;       /* 更浅的边框 */\n  --color-surface:        #FFFFFF;       /* 卡片表面 */\n  --color-surface-warm:   #FDF9F3;       /* 温暖的卡片表面 */\n\n  /* --- 强调色（根据项目调整 — 选择一个大胆的颜色）---\n     默认：朱红色。替代方案：珊瑚色 (#E06B56)、青色 (#2A7B9B)、\n     琥珀色 (#D4A843)、森林色 (#2D8B55)。避免紫色渐变。 */\n  --color-accent:         #D94F30;\n  --color-accent-hover:   #C4432A;\n  --color-accent-light:   #FDEEE9;\n  --color-accent-muted:   #E8836C;\n\n  /* --- 语义色 --- */\n  --color-success:        #2D8B55;\n  --color-success-light:  #E8F5EE;\n  --color-error:          #C93B3B;\n  --color-error-light:    #FDE8E8;\n  --color-info:           #2A7B9B;\n  --color-info-light:     #E4F2F7;\n\n  /* --- 角色颜色（分配给主要组件）---\n     代码库中的每个主要\"角色\"获得一个独特的颜色\n     用于聊天气泡、图表和高亮 */\n  --color-actor-1:        #D94F30;       /* 朱红色 */\n  --color-actor-2:        #2A7B9B;       /* 青色 */\n  --color-actor-3:        #7B6DAA;       /* 柔和的梅红色 */\n  --color-actor-4:        #D4A843;       /* 金色 */\n  --color-actor-5:        #2D8B55;       /* 森林色 */\n}\n```\n\n**规则：**\n- 偶数模块使用 `--color-bg`，奇数模块使用 `--color-bg-warm`（交替背景创造视觉节奏）\n- 角色颜色应该在视觉上彼此区分并与强调色区分\n- 代码块始终使用 `--color-bg-code` 配浅色文本\n\n---\n\n## 排版\n\n```css\n:root {\n  /* --- 字体 ---\n     使用系统字体回退，确保中国大陆用户无需翻墙即可正常显示。\n     展示/正文字体：中文优先系统字体\n     等宽字体：开发者友好的等宽系统字体回退链 */\n  --font-display:  'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n  --font-body:     'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n  --font-mono:     'JetBrains Mono', 'Fira Code', 'Source Code Pro', 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;\n\n  /* --- 类型比例（1.25 比例）--- */\n  --text-xs:   0.75rem;    /* 12px — 标签、徽章 */\n  --text-sm:   0.875rem;   /* 14px — 次要文本、代码 */\n  --text-base: 1rem;       /* 16px — 正文文本 */\n  --text-lg:   1.125rem;   /* 18px — 引导段落 */\n  --text-xl:   1.25rem;    /* 20px — 屏幕标题 */\n  --text-2xl:  1.5rem;     /* 24px — 子模块标题 */\n  --text-3xl:  1.875rem;   /* 30px — 模块副标题 */\n  --text-4xl:  2.25rem;    /* 36px — 模块标题 */\n  --text-5xl:  3rem;       /* 48px — 英雄文本 */\n  --text-6xl:  3.75rem;    /* 60px — 模块编号 */\n\n  /* --- 行高 --- */\n  --leading-tight:  1.15;  /* 标题 */\n  --leading-snug:   1.3;   /* 小标题 */\n  --leading-normal: 1.6;   /* 正文文本 */\n  --leading-loose:  1.8;   /* 轻松阅读 */\n}\n```\n\n**注意：使用系统字体，无需外部字体链接**\n本技能面向中国大陆用户，所有字体使用系统字体回退链，无需加载 Google Fonts 或任何外部 CDN。\n\n**规则：**\n- 模块编号：`--text-6xl`、font-display、weight 800、`--color-accent` 配 15% 不透明度\n- 模块标题：`--text-4xl`、font-display、weight 700\n- 屏幕标题：`--text-xl` 或 `--text-2xl`、font-display、weight 600\n- 正文文本：`--text-base` 或 `--text-lg`、font-body、`--leading-normal`\n- 代码：`--text-sm`、font-mono\n- 标签/徽章：`--text-xs`、font-mono、大写、letter-spacing 0.05em\n\n---\n\n## 间距与布局\n\n```css\n:root {\n  --space-1:  0.25rem;   /* 4px */\n  --space-2:  0.5rem;    /* 8px */\n  --space-3:  0.75rem;   /* 12px */\n  --space-4:  1rem;      /* 16px */\n  --space-5:  1.25rem;   /* 20px */\n  --space-6:  1.5rem;    /* 24px */\n  --space-8:  2rem;      /* 32px */\n  --space-10: 2.5rem;    /* 40px */\n  --space-12: 3rem;      /* 48px */\n  --space-16: 4rem;      /* 64px */\n  --space-20: 5rem;      /* 80px */\n  --space-24: 6rem;      /* 96px */\n\n  --content-width:     800px;   /* 标准阅读宽度 */\n  --content-width-wide: 1000px; /* 用于并排布局 */\n  --nav-height:        50px;\n  --radius-sm:  8px;\n  --radius-md:  12px;\n  --radius-lg:  16px;\n  --radius-full: 9999px;\n}\n```\n\n**模块布局：**\n```css\n.module {\n  min-height: 100dvh;       /* 回退：100vh */\n  scroll-snap-align: start;\n  padding: var(--space-16) var(--space-6);\n  padding-top: calc(var(--nav-height) + var(--space-12));\n}\n.module-content {\n  max-width: var(--content-width);\n  margin: 0 auto;\n}\n```\n\n---\n\n## 阴影与深度\n\n```css\n:root {\n  --shadow-sm:  0 1px 2px rgba(44, 42, 40, 0.05);\n  --shadow-md:  0 4px 12px rgba(44, 42, 40, 0.08);\n  --shadow-lg:  0 8px 24px rgba(44, 42, 40, 0.1);\n  --shadow-xl:  0 16px 48px rgba(44, 42, 40, 0.12);\n}\n```\n\n使用温暖色调的 RGBA（44, 42, 40）— 绝不使用纯黑色阴影。\n\n---\n\n## 动画与过渡\n\n```css\n:root {\n  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);\n  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  --duration-fast:   150ms;\n  --duration-normal: 300ms;\n  --duration-slow:   500ms;\n  --stagger-delay:   120ms;\n}\n```\n\n**滚动触发显示模式：**\n```css\n.animate-in {\n  opacity: 0;\n  transform: translateY(20px);\n  transition: opacity var(--duration-slow) var(--ease-out),\n              transform var(--duration-slow) var(--ease-out);\n}\n.animate-in.visible {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n/* 子元素交错显示 */\n.stagger-children > .animate-in {\n  transition-delay: calc(var(--stagger-index, 0) * var(--stagger-delay));\n}\n```\n\n**交错的 JS 设置：**\n```javascript\ndocument.querySelectorAll('.stagger-children').forEach(parent => {\n  Array.from(parent.children).forEach((child, i) => {\n    child.style.setProperty('--stagger-index', i);\n  });\n});\n```\n\n**Intersection Observer（触发显示）：**\n```javascript\nconst observer = new IntersectionObserver((entries) => {\n  entries.forEach(entry => {\n    if (entry.isIntersecting) {\n      entry.target.classList.add('visible');\n      observer.unobserve(entry.target); // 只动画一次\n    }\n  });\n}, { rootMargin: '0px 0px -10% 0px', threshold: 0.1 });\n\ndocument.querySelectorAll('.animate-in').forEach(el => observer.observe(el));\n```\n\n---\n\n## 导航与进度\n\n**HTML 结构：**\n```html\n<nav class=\"nav\">\n  <div class=\"progress-bar\" role=\"progressbar\" aria-valuenow=\"0\"></div>\n  <div class=\"nav-inner\">\n    <span class=\"nav-title\">课程标题</span>\n    <div class=\"nav-dots\">\n      <button class=\"nav-dot\" data-target=\"module-1\" data-tooltip=\"模块 1 名称\"\n              role=\"tab\" aria-label=\"模块 1\"></button>\n      <!-- 每个模块一个 -->\n    </div>\n  </div>\n</nav>\n```\n\n**进度条（尽可能只用 CSS，JS 回退）：**\n```javascript\nfunction updateProgressBar() {\n  const scrollTop = window.scrollY;\n  const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;\n  const progress = (scrollTop / scrollHeight) * 100;\n  progressBar.style.width = progress + '%';\n}\nwindow.addEventListener('scroll', () => {\n  requestAnimationFrame(updateProgressBar);\n}, { passive: true });\n```\n\n**导航点状态：**\n- 默认：`border: 2px solid var(--color-text-muted)`，空心\n- 当前：`border-color: var(--color-accent)`，实心中心，微妙发光阴影\n- 已访问：`background: var(--color-accent)`，实心填充\n\n**键盘导航：**\n```javascript\ndocument.addEventListener('keydown', (e) => {\n  if (['INPUT', 'TEXTAREA'].includes(e.target.tagName)) return;\n  if (e.key === 'ArrowDown' || e.key === 'ArrowRight') { nextModule(); e.preventDefault(); }\n  if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { prevModule(); e.preventDefault(); }\n});\n```\n\n---\n\n## 模块结构\n\n**每个模块的 HTML 模板：**\n```html\n<section class=\"module\" id=\"module-N\" style=\"background: var(--color-bg or --color-bg-warm)\">\n  <div class=\"module-content\">\n    <header class=\"module-header animate-in\">\n      <span class=\"module-number\">0N</span>\n      <h1 class=\"module-title\">模块标题</h1>\n      <p class=\"module-subtitle\">此模块教授内容的一句话描述</p>\n    </header>\n\n    <div class=\"module-body\">\n      <section class=\"screen animate-in\">\n        <h2 class=\"screen-heading\">屏幕标题</h2>\n        <p>内容...</p>\n        <!-- 交互元素、代码翻译等 -->\n      </section>\n\n      <section class=\"screen animate-in\">\n        <!-- 下一个屏幕 -->\n      </section>\n    </div>\n  </div>\n</section>\n```\n\n---\n\n## 响应式断点\n\n```css\n/* 平板 */\n@media (max-width: 768px) {\n  :root {\n    --text-4xl: 1.875rem;\n    --text-5xl: 2.25rem;\n    --text-6xl: 3rem;\n  }\n  .translation-block { grid-template-columns: 1fr; } /* 堆叠代码/英语 */\n  .pattern-cards { grid-template-columns: 1fr 1fr; }\n}\n\n/* 手机 */\n@media (max-width: 480px) {\n  :root {\n    --text-4xl: 1.5rem;\n    --text-5xl: 1.875rem;\n    --text-6xl: 2.25rem;\n  }\n  .module { padding: var(--space-8) var(--space-4); }\n  .pattern-cards { grid-template-columns: 1fr; }\n  .flow-steps { flex-direction: column; }\n  .flow-arrow { transform: rotate(90deg); }\n}\n```\n\n---\n\n## 滚动条与背景\n\n```css\n/* 自定义滚动条 */\n::-webkit-scrollbar { width: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb {\n  background: var(--color-border);\n  border-radius: var(--radius-full);\n}\n\n/* 微妙的氛围背景 */\nbody {\n  background: var(--color-bg);\n  background-image: radial-gradient(\n    ellipse at 20% 50%,\n    rgba(217, 79, 48, 0.03) 0%,\n    transparent 50%\n  );\n}\n\n/* 页面滚动设置 */\nhtml {\n  scroll-snap-type: y proximity;\n  scroll-behavior: smooth;\n}\n```\n\n---\n\n## 代码块全局设置\n\n课程中的所有代码块 —— 无论是在翻译块内、独立片段还是测验挑战中 —— 必须换行文本，绝不显示水平滚动条。这是教学工具，不是 IDE。\n\n```css\npre, code {\n  white-space: pre-wrap;       /* 换行长行 */\n  word-break: break-word;      /* 绝对需要时在单词中间断开 */\n  overflow-x: hidden;          /* 绝不出现水平滚动条 */\n}\n/* 隐藏代码容器上的滚动条 */\n.translation-code::-webkit-scrollbar,\npre::-webkit-scrollbar {\n  display: none;\n}\n```\n\n代码片段必须是来自真实代码库的**精确副本** — 绝不修改、修剪或简化。相反，从代码中选择自然简短（5-10 行）的部分来很好地说明概念。如果需要更长的块，全部显示 —— 换行 CSS 会处理可读性。\n\n---\n\n## 语法高亮（Catppuccin 风格）\n\n用于深色 `--color-bg-code` 背景上的代码块：\n\n```css\n.code-keyword  { color: #CBA6F7; }  /* 紫色 — if、else、return、function */\n.code-string   { color: #A6E3A1; }  /* 绿色 — \"字符串\" */\n.code-function { color: #89B4FA; }  /* 蓝色 — 函数名 */\n.code-comment  { color: #6C7086; }  /* 柔和灰色 — // 注释 */\n.code-number   { color: #FAB387; }  /* 桃色 — 数字 */\n.code-property { color: #F9E2AF; }  /* 黄色 — 对象键 */\n.code-operator { color: #94E2D5; }  /* 青色 — =、=>、+ 等 */\n.code-tag      { color: #F38BA8; }  /* 粉色 — HTML 标签 */\n.code-attr     { color: #F9E2AF; }  /* 黄色 — HTML 属性 */\n.code-value    { color: #A6E3A1; }  /* 绿色 — 属性值 */\n```\n\nFile v1.1.4:references/gotchas.md\n\n# 常见陷阱 —— 常见失败点\n\n> **何时阅读此文件：** 在阶段 3（编写模块 HTML）和阶段 4（审查）期间。在认为课程完成之前，检查以下每一项。\n\n根据你生成的课程版本（产品版或开发版），关注对应的检查项：\n\n---\n\n## 产品版特有问题\n\n### 提示框不足\n\n最常见的失败是提示框太少。非技术学习者不知道 REPL、JSON、标志、入口点、PATH、pip、命名空间、函数、类、模块、PR、E2E 等术语，甚至 Blender/GIMP 等软件名称。**经验法则：** 如果一个术语不会在与非技术朋友的日常对话中出现，就给它加提示框。大量倾向于太多。但是：不要给用户已经很熟悉的专业术语加提示框（例如，AI/ML 领域的人对 AI/ML 概念）。\n\n### 重复使用隐喻\n\n对所有内容使用\"餐厅\"或\"厨房\"。每个模块需要自己的隐喻，对该特定概念来说是必然的。如果你发现自己两次使用同一个隐喻，停下来找一个自然适合该概念的隐喻。\n\n### 测验数量不足\n\n**产品版要求每个模块至少一个测验。** 如果模块缺少测验，必须添加。\n\n---\n\n## 开发版特有问题\n\n### 过度解释基础概念\n\n专业程序员了解什么是 API、什么是回调、什么是数据库。不需要解释这些基础概念。\n\n**错误示例：** \"API（应用程序编程接口）是一种允许不同软件系统相互通信的方式...\"\n\n**正确示例：** \"项目使用 REST API 与支付服务通信，API 基址配置在 `config/api.ts`...\"\n\n### 缺少设计意图说明\n\n展示代码但不解释\"为什么要这样设计\"。专业程序员能看懂代码逻辑，他们想知道的是设计决策背后的原因。每个代码块都应该包含设计意图注释：权衡、替代方案、扩展点。\n\n### 架构图缺失或过于简化\n\n没有架构图，或者架构图只是简单的方框图。专业程序员需要看到：\n- 清晰的模块边界\n- 依赖方向\n- 数据流路径\n- 关键接口\n\n### 测验太简单\n\n**注意：测验在开发版中是可选功能，仅在用户明确要求时添加。**\n\n如果用户要求测验，测验问题不应该只测试表面理解，比如\"哪个文件处理 X？\"。专业程序员需要更有挑战性的问题：\n- 设计决策题（\"为什么要用策略模式而不是简单的 if-else？\"）\n- 场景题（\"如果要添加批量导出功能，你会如何实现？\"）\n- 问题诊断题（\"如果出现 Y 症状，可能是什么原因？\"）\n\n### 信息密度太低\n\n大量重复性描述，每句话信息量少。专业程序员阅读速度快，希望快速获取信息。每个段落都应该有明确的信息增量。\n\n### 代码示例不完整或被修改\n\n代码片段被过度简化，丢失了上下文信息。或者代码被\"清理\"过，不再是项目中的真实代码。\n\n**原则：** 使用真实代码，但精选最核心的 10-15 行，保留必要的上下文（导入、类型注解、注释）。\n\n### 代码过多，图表过少\n\n这是最常见的问题。课程贴了大量代码块，但缺少架构图、数据流动画和时序图。\n\n**检查标准：**\n- 每个模块是否至少有一个可视化元素（架构图、数据流动画、时序图、流程图）？\n- 单个代码块是否超过了 15 行？如果是，考虑拆分或用图表替代。\n- 是否有连续两个代码块之间没有可视化元素？\n- 课程整体的可视化元素占比是否达到 60%？\n- 是否有可以用数据流动画或步骤卡片替代代码的地方？\n\n**修复方法：** 回顾每个代码块，问自己：\"这段代码要传递的核心信息能否用图表或动画更好地表达？\" 如果能，用可视化替代。仅保留那些不可替代的核心代码片段。\n\n### 上下文耗尽导致后续模块质量下降\n\n分析大型代码库时，前面的模块占用了大量上下文，导致后面的模块信息不足、质量下降。\n\n**预防方法：**\n- 在阶段 2.5 为每个模块编写简报，后续编写时从简报读取而非重新分析代码库\n- 每完成 2 个模块后检查上下文使用情况\n- 必要时主动压缩上下文：将已提取的信息写入简报文件，释放空间\n\n### 缺少技术栈说明\n\n没有说明项目使用的技术栈以及选型理由。专业程序员关心：\n- 为什么选择 React 而不是 Vue？\n- 为什么用 PostgreSQL 而不是 MySQL？\n- 为什么选择 gRPC 而不是 REST？\n\n### 缺少扩展点和自定义机制说明\n\n专业学习者想知道\"如何添加新功能\"。课程应该明确指出：\n- 插件系统在哪里\n- 如何注册新的处理器\n- 配置扩展机制\n\n### 缺少实际应用指导\n\n课程讲清楚了\"是什么\"，但没有讲清楚\"怎么用\"。每个模块应该回答：\n- \"我在哪里添加新功能？\"\n- \"我如何定位特定类型的 bug？\"\n- \"我如何理解这个错误信息？\"\n\n---\n\n## 通用问题（两个版本都需注意）\n\n### 提示框被裁剪\n\n翻译块使用 `overflow: hidden` 来处理代码换行。如果提示框在术语元素内部使用 `position: absolute`，它们会被容器裁剪。\n\n**修复方法：** 提示框必须使用 `position: fixed` 并附加到 `document.body`。从 `getBoundingClientRect()` 计算位置。这已由 `main.js` 处理，但这是每次构建都会出现的 #1 bug。\n\n### 文字墙\n\n课程看起来像教科书而不是信息图。当你连续写超过 2-3 个句子而没有视觉中断时，就会发生这种情况。\n\n**产品版：** 每个屏幕必须至少 50% 是视觉内容（图表、代码块、问卷、动画）。\n**开发版：** 注重信息密度，可以文字占比更高，但必须有足够的图表和动画。\n\n将任何 3+ 项的列表转换为卡片，任何序列转换为步骤卡片或流程图，任何代码解释转换为代码↔英语翻译块。\n\n### 代码修改\n\n从代码库修剪、简化或\"清理\"代码片段。\n\n**产品版：** 学习者应该能够打开真实文件并看到完全相同的代码。不要通过编辑代码来使其更短，而是从代码库中*选择*自然简短（5-10 行）的片段来说明要点。\n**开发版：** 使用真实代码，仅保留必要的上下文（导入、类型注解），但保持核心逻辑完整。\n\n### 测试记忆的测验问题\n\n问\"API 代表什么？\"或\"哪个文件处理 X？\" —— 这些测试回忆，而不是理解。每个测验问题应该呈现学习者没见过的新场景，并要求他们*应用*所学内容。\n\n### 滚动捕捉强制模式\n\n使用 `scroll-snap-type: y mandatory` 会将用户困在长模块中。**始终使用 `proximity`。**\n\n### 模块质量下降\n\n尝试一次性编写所有模块会导致后面的模块变得单薄和仓促。一次构建一个模块，在继续之前验证每个模块。对于复杂代码库，使用带有模块简报的并行路径。\n\n### 缺少交互元素\n\n只有文本和代码块的模块，没有交互性。每个模块都需要交互元素。\n\n**产品版每个模块至少：** 测验、数据流动画、群聊、架构图、拖放之一\n**开发版每个模块至少：** 架构图、数据流动画、时序图之一\n\n这些不是装饰 —— 学习者是实际处理信息的方式。\n\nFile v1.1.4:references/interactive-elements.md\n\n# 交互元素参考\n\n课程中使用的每种交互元素类型的实现模式。选择最适合每个模块教学目标的元素。\n\n> **架构说明：** 这些元素的所有 CSS 和 JavaScript 都在 `references/styles.css` 和 `references/main.js` 中，它们被逐字复制到每个课程目录中。编写模块 HTML 文件时，只使用下面的 HTML 模式 — 不要为这些元素内联 `<style>` 或 `<script>` 标签。`main.js` 中的引擎在页面加载时通过扫描这里描述的相关类名和 `data-*` 属性自动初始化。\n\n## 目录\n1. [代码 ↔ 英语翻译块](#code--英语翻译块)\n2. [多选题测验](#多选题测验)\n3. [拖放匹配](#拖放匹配)\n4. [群聊动画](#群聊动画)\n5. [消息流 / 数据流动画](#消息流--数据流动画)\n6. [交互式架构图](#交互式架构图)\n7. [层次切换演示](#层次切换演示)\n8. [\"找 Bug\"挑战](#找-bug-挑战)\n9. [场景测验](#场景测验)\n10. [提示框](#提示框)\n11. [模式/功能卡片](#模式功能卡片)\n12. [流程图](#流程图)\n13. [权限/配置徽章](#权限配置徽章)\n14. [词汇表提示](#词汇表提示)\n15. [可视化文件树](#可视化文件树)\n16. [图标-标签行](#图标-标签行)\n17. [编号步骤卡片](#编号步骤卡片)\n\n---\n\n## 代码 ↔ 英语翻译块\n\n最重要的教学元素。左侧显示项目中的真实代码，右侧逐行显示通俗英语翻译。\n\n**HTML：**\n```html\n<div class=\"translation-block animate-in\">\n  <div class=\"translation-code\">\n    <span class=\"translation-label\">CODE</span>\n    <pre><code>\n<span class=\"code-line\"><span class=\"code-keyword\">const</span> response = <span class=\"code-keyword\">await</span> <span class=\"code-function\">fetch</span>(url, {</span>\n<span class=\"code-line\">  <span class=\"code-property\">method</span>: <span class=\"code-string\">'POST'</span>,</span>\n<span class=\"code-line\">  <span class=\"code-property\">headers</span>: { <span class=\"code-string\">'Authorization'</span>: apiKey }</span>\n<span class=\"code-line\">});</span>\n    </code></pre>\n  </div>\n  <div class=\"translation-english\">\n    <span class=\"translation-label\">PLAIN ENGLISH</span>\n    <div class=\"translation-lines\">\n      <p class=\"tl\">向 URL 发送请求并等待响应...</p>\n      <p class=\"tl\">我们在发送数据（POST），不只是请求数据（GET）...</p>\n      <p class=\"tl\">包含我们的 API 密钥，让服务器知道我们是谁...</p>\n      <p class=\"tl\">请求设置结束。</p>\n    </div>\n  </div>\n</div>\n```\n\n**CSS：**\n```css\n.translation-block {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 0;\n  border-radius: var(--radius-md);\n  overflow: hidden;\n  box-shadow: var(--shadow-md);\n  margin: var(--space-8) 0;\n}\n.translation-code {\n  background: var(--color-bg-code);\n  color: #CDD6F4;\n  padding: var(--space-6);\n  font-family: var(--font-mono);\n  font-size: var(--text-sm);\n  line-height: 1.7;\n  position: relative;\n  overflow-x: hidden;  /* 绝不出现水平滚动条 */\n}\n.translation-code pre,\n.translation-code code {\n  white-space: pre-wrap;       /* 换行长行而不是滚动 */\n  word-break: break-word;      /* 必要时在单词中间断开 */\n  overflow-x: hidden;\n}\n.translation-english {\n  background: var(--color-surface-warm);\n  padding: var(--space-6);\n  font-size: var(--text-sm);\n  line-height: 1.7;\n  border-left: 3px solid var(--color-accent);\n}\n.translation-label {\n  position: absolute;\n  top: var(--space-2);\n  right: var(--space-3);\n  font-size: var(--text-xs);\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  opacity: 0.5;\n}\n.translation-english .translation-label {\n  color: var(--color-text-muted);\n}\n/* 响应式：移动端垂直堆叠 */\n@media (max-width: 768px) {\n  .translation-block { grid-template-columns: 1fr; }\n  .translation-english { border-left: none; border-top: 3px solid var(--color-accent); }\n}\n```\n\n**规则：**\n- 每行英语应对应 1-2 行代码\n- 使用对话式语言，不要技术术语\n- 强调\"为什么\"而不仅仅是\"什么\" —— 例如，\"包含我们的 API 密钥，让服务器知道我们是谁\"而不是\"设置 Authorization 头\"\n\n---\n\n## 多选题测验\n\n用于即时反馈测试理解。每个问题有选项、一个正确答案和每个问题的解释。\n\n**连接方式：** `main.js` 暴露 `window.selectOption(btn)`、`window.checkQuiz(containerId)` 和 `window.resetQuiz(containerId)`。通过 `onclick` 调用它们。每个问题的解释放在 `.quiz-question-block` 上的 `data-explanation-right` 和 `data-explanation-wrong` 中。\n\n**HTML：**\n```html\n<div class=\"quiz-container\" id=\"quiz-module3\">\n  <div class=\"quiz-question-block\"\n       data-correct=\"option-b\"\n       data-explanation-right=\"没错 — 因为 X 在此架构中负责 Y。\"\n       data-explanation-wrong=\"不完全对。想想 Y 在代码库中的位置...\">\n    <h3 class=\"quiz-question\">问题文本在这里？</h3>\n    <div class=\"quiz-options\">\n      <button class=\"quiz-option\" data-value=\"option-a\" onclick=\"selectOption(this)\">\n        <div class=\"quiz-option-radio\"></div>\n        <span>答案 A</span>\n      </button>\n      <button class=\"quiz-option\" data-value=\"option-b\" onclick=\"selectOption(this)\">\n        <div class=\"quiz-option-radio\"></div>\n        <span>答案 B（正确）</span>\n      </button>\n      <button class=\"quiz-option\" data-value=\"option-c\" onclick=\"selectOption(this)\">\n        <div class=\"quiz-option-radio\"></div>\n        <span>答案 C</span>\n      </button>\n    </div>\n    <div class=\"quiz-feedback\"></div>\n  </div>\n\n  <button class=\"quiz-check-btn\" onclick=\"checkQuiz('quiz-module3')\">检查答案</button>\n  <button class=\"quiz-reset-btn\" onclick=\"resetQuiz('quiz-module3')\">重试</button>\n</div>\n```\n\n**测验状态的 CSS：**\n```css\n.quiz-option {\n  display: flex; align-items: center; gap: var(--space-3);\n  padding: var(--space-3) var(--space-4);\n  border: 2px solid var(--color-border);\n  border-radius: var(--radius-sm);\n  background: var(--color-surface);\n  cursor: pointer; width: 100%;\n  transition: border-color var(--duration-fast), background var(--duration-fast);\n}\n.quiz-option:hover { border-color: var(--color-accent-muted); }\n.quiz-option.selected { border-color: var(--color-accent); background: var(--color-accent-light); }\n.quiz-option.correct { border-color: var(--color-success); background: var(--color-success-light); }\n.quiz-option.incorrect { border-color: var(--color-error); background: var(--color-error-light); }\n.quiz-option-radio {\n  width: 18px; height: 18px; border-radius: 50%;\n  border: 2px solid var(--color-border);\n  transition: all var(--duration-fast);\n}\n.quiz-option.selected .quiz-option-radio {\n  border-color: var(--color-accent);\n  background: var(--color-accent);\n  box-shadow: inset 0 0 0 3px white;\n}\n.quiz-feedback {\n  max-height: 0; overflow: hidden; opacity: 0;\n  transition: max-height var(--duration-normal), opacity var(--duration-normal);\n}\n.quiz-feedback.show { max-height: 200px; opacity: 1; padding: var(--space-3); margin-top: var(--space-2); border-radius: var(--radius-sm); }\n.quiz-feedback.success { background: var(--color-success-light); color: var(--color-success); }\n.quiz-feedback.error { background: var(--color-error-light); color: var(--color-error); }\n```\n\n---\n\n## 拖放匹配\n\n用于将概念与描述匹配。支持鼠标（HTML5 拖放 API）和触摸。\n\n**HTML：**\n```html\n<div class=\"dnd-container\">\n  <div class=\"dnd-chips\">\n    <div class=\"dnd-chip\" draggable=\"true\" data-answer=\"actor-a\">角色 A</div>\n    <div class=\"dnd-chip\" draggable=\"true\" data-answer=\"actor-b\">角色 B</div>\n    <div class=\"dnd-chip\" draggable=\"true\" data-answer=\"actor-c\">角色 C</div>\n  </div>\n  <div class=\"dnd-zones\">\n    <div class=\"dnd-zone\" data-correct=\"actor-a\">\n      <p class=\"dnd-zone-label\">角色 A 的描述</p>\n      <div class=\"dnd-zone-target\">拖到这里</div>\n    </div>\n    <!-- 更多区域 -->\n  </div>\n  <button onclick=\"checkDnD()\">检查匹配</button>\n  <button onclick=\"resetDnD()\">重置</button>\n</div>\n```\n\n**JS（鼠标 + 触摸）：**\n```javascript\n// 鼠标：HTML5 拖放 API\nchips.forEach(chip => {\n  chip.addEventListener('dragstart', (e) => {\n    e.dataTransfer.setData('text/plain', chip.dataset.answer);\n    chip.classList.add('dragging');\n  });\n  chip.addEventListener('dragend', () => chip.classList.remove('dragging'));\n});\n\nzones.forEach(zone => {\n  const target = zone.querySelector('.dnd-zone-target');\n  target.addEventListener('dragover', (e) => { e.preventDefault(); target.classList.add('drag-over'); });\n  target.addEventListener('dragleave', () => target.classList.remove('drag-over'));\n  target.addEventListener('drop', (e) => {\n    e.preventDefault();\n    target.classList.remove('drag-over');\n    const answer = e.dataTransfer.getData('text/plain');\n    const chip = document.querySelector(`[data-answer=\"${answer}\"]`);\n    target.textContent = chip.textContent;\n    target.dataset.placed = answer;\n    chip.classList.add('placed');\n  });\n});\n\n// 触摸：自定义实现（HTML5 拖放在移动端不工作）\nchips.forEach(chip => {\n  chip.addEventListener('touchstart', (e) => {\n    e.preventDefault();\n    const touch = e.touches[0];\n    const clone = chip.cloneNode(true);\n    clone.classList.add('touch-ghost');\n    clone.style.cssText = `position:fixed; z-index:1000; pointer-events:none;\n      left:${touch.clientX - 40}px; top:${touch.clientY - 20}px;`;\n    document.body.appendChild(clone);\n    chip._ghost = clone;\n    chip._answer = chip.dataset.answer;\n  }, { passive: false });\n\n  chip.addEventListener('touchmove', (e) => {\n    e.preventDefault();\n    const touch = e.touches[0];\n    if (chip._ghost) {\n      chip._ghost.style.left = (touch.clientX - 40) + 'px';\n      chip._ghost.style.top = (touch.clientY - 20) + 'px';\n    }\n    // 高亮手指下的区域\n    const el = document.elementFromPoint(touch.clientX, touch.clientY);\n    zones.forEach(z => z.querySelector('.dnd-zone-target').classList.remove('drag-over'));\n    if (el && el.closest('.dnd-zone-target')) {\n      el.closest('.dnd-zone-target').classList.add('drag-over');\n    }\n  }, { passive: false });\n\n  chip.addEventListener('touchend', (e) => {\n    if (chip._ghost) { chip._ghost.remove(); chip._ghost = null; }\n    const touch = e.changedTouches[0];\n    const el = document.elementFromPoint(touch.clientX, touch.clientY);\n    if (el && el.closest('.dnd-zone-target')) {\n      const target = el.closest('.dnd-zone-target');\n      target.textContent = chip.textContent;\n      target.dataset.placed = chip._answer;\n      chip.classList.add('placed');\n    }\n  });\n});\n```\n\n---\n\n## 群聊动画\n\niMessage/微信风格的聊天，显示组件之间\"对话\"。消息逐一出现，带有输入指示器。\n\n**连接方式：** `main.js` 在页面加载时自动初始化每个 `.chat-window`。给每个聊天窗口一个唯一的 `id`。控制按钮需要这些类：`.chat-next-btn`、`.chat-all-btn`、`.chat-reset-btn`。输入指示器头像元素应该有 `id=\"{chatWindowId}-typing-avatar\"` 或者简单是 `.chat-typing` 内的第一个 `.chat-avatar`。\n\n**HTML：**\n```html\n<div class=\"chat-window\" id=\"chat-module2\">\n  <div class=\"chat-messages\">\n    <div class=\"chat-message\" data-msg=\"0\" data-sender=\"actor-a\" style=\"display:none\">\n      <div class=\"chat-avatar\" style=\"background: var(--color-actor-1)\">A</div>\n      <div class=\"chat-bubble\">\n        <span class=\"chat-sender\" style=\"color: var(--color-actor-1)\">角色 A</span>\n        <p>嘿 Background，我需要这个项目的数据。</p>\n      </div>\n    </div>\n    <!-- 更多消息... -->\n  </div>\n\n  <div class=\"chat-typing\" id=\"chat-typing\" style=\"display:none\">\n    <div class=\"chat-avatar\" id=\"typing-avatar\">?</div>\n    <div class=\"chat-typing-dots\">\n      <span class=\"typing-dot\"></span>\n      <span class=\"typing-dot\"></span>\n      <span class=\"typing-dot\"></span>\n    </div>\n  </div>\n\n  <div class=\"chat-controls\">\n    <button class=\"btn chat-next-btn\">下一条消息</button>\n    <button class=\"btn chat-all-btn\">全部播放</button>\n    <button class=\"btn chat-reset-btn\">重播</button>\n    <span class=\"chat-progress\"></span>\n  </div>\n</div>\n```\n\n**输入点的 CSS：**\n```css\n.typing-dot {\n  width: 8px; height: 8px; border-radius: 50%;\n  background: var(--color-text-muted);\n  animation: typingBounce 1.4s infinite;\n}\n.typing-dot:nth-child(2) { animation-delay: 0.2s; }\n.typing-dot:nth-child(3) { animation-delay: 0.4s; }\n@keyframes typingBounce {\n  0%, 60%, 100% { transform: translateY(0); }\n  30% { transform: translateY(-6px); }\n}\n```\n\n---\n\n## 消息流 / 数据流动画\n\n组件之间数据移动的分步可视化。用户点击\"下一步\"前进。\n\n**连接方式：** `main.js` 在页面加载时自动初始化每个 `.flow-animation`。在 `data-steps` 中传递步骤为 JSON。每个步骤对象：`{ highlight: \"flow-actor-id\", label: \"描述\", packet: true, from: \"actor-id-suffix\", to: \"actor-id-suffix\" }`。角色元素 ID 必须是 `flow-actor-1`、`flow-actor-2` 等。控制按钮需要类 `.flow-next-btn` 和 `.flow-reset-btn`。\n\n> **⚠️ 步骤标签中的单引号会破坏解析。** `data-steps` 属性由单引号定界（`data-steps='[...]'`），所以标签内的任何单引号（例如 `\"the user's request\"`）会提前终止属性并导致 `JSON.parse` 静默失败 —— 整个动画将停止工作。要么避免在标签中使用撇号，用 `&apos;` 替换，或使用双引号定界并转义内部引号重写属性（`data-steps=\"[{\\\"label\\\":\\\"...\\\"}]\"`）。\n\n**HTML：**\n```html\n<div class=\"flow-animation\" data-steps='[\n  {\"highlight\":\"flow-actor-1\",\"label\":\"用户点击按钮\"},\n  {\"highlight\":\"flow-actor-1\",\"label\":\"前端发送请求\",\"packet\":true,\"from\":\"actor-1\",\"to\":\"actor-2\"},\n  {\"highlight\":\"flow-actor-2\",\"label\":\"后端调用数据库\",\"packet\":true,\"from\":\"actor-2\",\"to\":\"actor-3\"}\n]'>\n  <div class=\"flow-actors\">\n    <div class=\"flow-actor\" id=\"flow-actor-1\">\n      <div class=\"flow-actor-icon\">A</div>\n      <span>角色 1</span>\n    </div>\n    <div class=\"flow-actor\" id=\"flow-actor-2\">\n      <div class=\"flow-actor-icon\">B</div>\n      <span>角色 2</span>\n    </div>\n    <div class=\"flow-actor\" id=\"flow-actor-3\">\n      <div class=\"flow-actor-icon\">C</div>\n      <span>角色 3</span>\n    </div>\n  </div>\n\n  <div class=\"flow-packet\" id=\"flow-packet\"></div>\n\n  <div class=\"flow-step-label\" id=\"flow-label\">点击\"下一步\"开始</div>\n\n  <div class=\"flow-controls\">\n    <button class=\"btn flow-next-btn\">下一步</button>\n    <button class=\"btn flow-reset-btn\">重新开始</button>\n    <span class=\"flow-progress\"></span>\n  </div>\n</div>\n```\n\n**活动角色发光的 CSS：**\n```css\n.flow-actor.active {\n  box-shadow: 0 0 0 3px var(--color-accent), 0 0 20px rgba(217, 79, 48, 0.2);\n  transform: scale(1.05);\n  transition: all var(--duration-normal) var(--ease-out);\n}\n```\n\n---\n\n## 交互式架构图\n\n全系统图，悬停/点击组件显示描述提示框。\n\n**HTML：**\n```html\n<div class=\"arch-diagram\">\n  <div class=\"arch-zone arch-zone-browser\">\n    <h4 class=\"arch-zone-label\">浏览器</h4>\n    <div class=\"arch-component\" data-desc=\"将 UI 注入网页，读取 DOM，捕获用户操作\"\n         onclick=\"showArchDesc(this)\">\n      <div class=\"arch-icon\">📄</div>\n      <span>组件 A</span>\n    </div>\n    <!-- 更多组件 -->\n  </div>\n  <div class=\"arch-zone arch-zone-external\">\n    <h4 class=\"arch-zone-label\">外部服务</h4>\n    <!-- API 卡片 -->\n  </div>\n  <div class=\"arch-description\" id=\"arch-desc\">点击任何组件了解它的作用</div>\n</div>\n```\n\n---\n\n## 层次切换演示\n\n显示不同层（例如，HTML/CSS/JS，或数据/逻辑/UI）如何相互构建。三个标签在视图之间切换。\n\n**HTML：**\n```html\n<div class=\"layer-demo\">\n  <div class=\"layer-tabs\">\n    <button class=\"layer-tab active\" onclick=\"showLayer('html')\">HTML</button>\n    <button class=\"layer-tab\" onclick=\"showLayer('css')\">+ CSS</button>\n    <button class=\"layer-tab\" onclick=\"showLayer('js')\">+ JS</button>\n  </div>\n  <div class=\"layer-viewport\">\n    <div class=\"layer\" id=\"layer-html\" style=\"display:block\">\n      <!-- 原始无样式版本 -->\n    </div>\n    <div class=\"layer\" id=\"layer-css\" style=\"display:none\">\n      <!-- 带样式版本 -->\n    </div>\n    <div class=\"layer\" id=\"layer-js\" style=\"display:none\">\n      <!-- 交互版本 -->\n    </div>\n  </div>\n  <p class=\"layer-description\" id=\"layer-desc\">这是原始 HTML...</p>\n</div>\n```\n\n---\n\n## \"找 Bug\"挑战\n\n显示带有故意 bug 的代码。用户点击有 bug 的行。显示解释问题。\n\n**HTML：**\n```html\n<div class=\"bug-challenge\">\n  <h3>找出这段代码中的 bug：</h3>\n  <div class=\"bug-code\">\n    <div class=\"bug-line\" data-line=\"1\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">1</span>\n      <code>chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {</code>\n    </div>\n    <div class=\"bug-line\" data-line=\"2\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">2</span>\n      <code>  if (msg.action === 'fetchData') {</code>\n    </div>\n    <div class=\"bug-line bug-target\" data-line=\"3\" onclick=\"checkBugLine(this, true)\">\n      <span class=\"line-num\">3</span>\n      <code>    fetch(url).then(r => r.json()).then(data => sendResponse(data));</code>\n    </div>\n    <div class=\"bug-line\" data-line=\"4\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">4</span>\n      <code>  }</code>\n    </div>\n    <div class=\"bug-line\" data-line=\"5\" onclick=\"checkBugLine(this, false)\">\n      <span class=\"line-num\">5</span>\n      <code>});</code>\n    </div>\n  </div>\n  <div class=\"bug-feedback\" id=\"bug-feedback\"></div>\n</div>\n```\n\n**JS：**\n```javascript\nwindow.checkBugLine = function(el, isCorrect) {\n  const feedback = el.closest('.bug-challenge').querySelector('.bug-feedback');\n  if (isCorrect) {\n    el.classList.add('correct');\n    feedback.innerHTML = '<strong>找到了！</strong> 监听器使用了异步操作（fetch）但没有返回 true。Chrome 在响应发送之前关闭消息通道。修复：在末尾添加 <code>return true;</code>。';\n    feedback.className = 'bug-feedback show success';\n  } else {\n    el.classList.add('incorrect');\n    feedback.innerHTML = '不是这行 — 找找异步时序可能引起问题的地方...';\n    feedback.className = 'bug-feedback show error';\n    setTimeout(() => { el.classList.remove('incorrect'); feedback.className = 'bug-feedback'; }, 2000);\n  }\n};\n```\n\n---\n\n## 场景测验\n\n\"资深工程师会怎么做？\" —— 情境问题带解释。\n\n与多选题相同的 HTML/CSS/JS 模式，但场景描述更长，解释更详细。将每个问题包装在场景上下文块中：\n\n```html\n<div class=\"scenario-block\">\n  <div class=\"scenario-context\">\n    <span class=\"scenario-label\">场景</span>\n    <p>你的应用处理 3 小时的播客转录。API 有 16,000 token 限制。你怎么办？</p>\n  </div>\n  <!-- quiz-options 在这里 -->\n</div>\n```\n\n---\n\n## 提示框\n\n\"顿悟！\"时刻 —— 通用计算机科学见解。每个模块最多 2 个。\n\n```html\n<div class=\"callout callout-accent\">\n  <div class=\"callout-icon\">💡</div>\n  <div class=\"callout-content\">\n    <strong class=\"callout-title\">关键见解</strong>\n    <p>这个模式 —— 将职责分成专注的角色 —— 是软件工程中最重要的想法之一。工程师称之为\"关注点分离\"。</p>\n  </div>\n</div>\n```\n\n**变体：**\n- `callout-accent`：朱红色左边框，浅强调背景（用于 CS 见解）\n- `callout-info`：青色左边框，浅信息背景（用于\"值得了解\"）\n- `callout-warning`：红色左边框，浅错误背景（用于常见错误）\n\n---\n\n## 模式/功能卡片\n\n突出工程模式、技术栈组件或关键概念的卡片网格。\n\n```html\n<div class=\"pattern-cards\">\n  <div class=\"pattern-card\" style=\"border-top: 3px solid var(--color-actor-1)\">\n    <div class=\"pattern-icon\" style=\"background: var(--color-actor-1)\">🔄</div>\n    <h4 class=\"pattern-title\">缓存</h4>\n    <p class=\"pattern-desc\">存储结果以避免重复工作 —— 就像保存剩菜而不是每次都做新饭。</p>\n  </div>\n  <!-- 更多卡片 -->\n</div>\n```\n\n```css\n.pattern-cards {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n  gap: var(--space-4);\n}\n.pattern-card {\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  padding: var(--space-6);\n  box-shadow: var(--shadow-sm);\n  transition: transform var(--duration-normal) var(--ease-out), box-shadow var(--duration-normal);\n}\n.pattern-card:hover {\n  transform: translateY(-4px);\n  box-shadow: var(--shadow-md);\n}\n```\n\n---\n\n## 流程图\n\n**水平流程（桌面）：**\n```html\n<div class=\"flow-steps\">\n  <div class=\"flow-step\">\n    <div class=\"flow-step-num\">1</div>\n    <p>用户点击按钮</p>\n  </div>\n  <div class=\"flow-arrow\">→</div>\n  <div class=\"flow-step\">\n    <div class=\"flow-step-num\">2</div>\n    <p>组件 A 检测到点击</p>\n  </div>\n  <div class=\"flow-arrow\">→</div>\n  <!-- 更多步骤 -->\n</div>\n```\n\n箭头在移动端通过 CSS 变换旋转为 `↓`。\n\n---\n\n## 权限/配置徽章\n\n用于注释配置文件、权限或设置：\n\n```html\n<div class=\"badge-list\">\n  <div class=\"badge-item\">\n    <code class=\"badge-code\">storage</code>\n    <span class=\"badge-desc\">在会话之间保存数据（像浏览器书签）</span>\n  </div>\n  <div class=\"badge-item\">\n    <code class=\"badge-code\">activeTab</code>\n    <span class=\"badge-desc\">访问当前打开的标签页（仅当用户点击时）</span>\n  </div>\n</div>\n```\n\n```css\n.badge-item {\n  display: flex; align-items: center; gap: var(--space-4);\n  padding: var(--space-3) var(--space-4);\n  border: 1px solid var(--color-border-light);\n  border-radius: var(--radius-sm);\n  transition: border-color var(--duration-fast);\n}\n.badge-item:hover { border-color: var(--color-accent-muted); }\n.badge-code {\n  font-family: var(--font-mono);\n  font-size: var(--text-sm);\n  background: var(--color-bg-code);\n  color: #CBA6F7;\n  padding: var(--space-1) var(--space-3);\n  border-radius: var(--radius-sm);\n  white-space: nowrap;\n}\n```\n\n---\n\n## 词汇表提示\n\n非技术学习者最重要的无障碍功能。课程文本中的任何技术术语都应该包装在提示中，在悬停（桌面）或点击（移动）时显示通俗英语定义。学习者永远不需要离开页面或 Google 任何东西。\n\n**HTML — 内联标记术语：**\n```html\n<p>扩展使用\n  <span class=\"term\" data-definition=\"service worker 是独立于网页运行的后台脚本 —— 就像一个始终开启的幕后助手，即使你没有在看页面。\">service worker</span>\n  来处理 API 调用。\n</p>\n```\n\n**CSS：**\n```css\n.term {\n  border-bottom: 1.5px dashed var(--color-accent-muted);\n  cursor: pointer;    /* 不是 cursor: help — pointer 感觉可点击且诱人 */\n  position: relative;\n}\n.term:hover, .term.active {\n  border-bottom-color: var(--color-accent);\n  color: var(--color-accent);\n}\n\n/* 提示框气泡 — 使用 position: fixed 并通过 JS 附加到 document.body\n   所以它永远不会被祖先的 overflow: hidden 容器裁剪\n   （像翻译块）。参见下面 JS 部分的定位逻辑。 */\n.term-tooltip {\n  position: fixed;        /* 关键：fixed，不是 absolute — 防止裁剪 */\n  background: var(--color-bg-code);\n  color: #CDD6F4;\n  padding: var(--space-3) var(--space-4);\n  border-radius: var(--radius-sm);\n  font-size: var(--text-sm);\n  font-family: var(--font-body);\n  line-height: var(--leading-normal);\n  width: max(200px, min(320px, 80vw));\n  box-shadow: var(--shadow-lg);\n  pointer-events: none;\n  opacity: 0;\n  transition: opacity var(--duration-fast);\n  z-index: 10000;        /* 在所有东西之上，包括导航 */\n}\n/* 向下的箭头 */\n.term-tooltip::after {\n  content: '';\n  position: absolute;\n  top: 100%;\n  left: 50%;\n  transform: translateX(-50%);\n  border: 6px solid transparent;\n  border-top-color: var(--color-bg-code);\n}\n.term-tooltip.visible {\n  opacity: 1;\n}\n\n/* 如果提示框超出屏幕顶部，翻转到下方 */\n.term-tooltip.flip {\n  bottom: auto;\n  top: calc(100% + 8px);\n}\n.term-tooltip.flip::after {\n  top: auto;\n  bottom: 100%;\n  border-top-color: transparent;\n  border-bottom-color: var(--color-bg-code);\n}\n```\n\n**JS — 附加到 body 的 position: fixed 提示框（永远不会被 overflow 裁剪）：**\n```javascript\n// 提示框容器 — 附加到 body 所以永远不会被裁剪\nlet activeTooltip = null;\n\nfunction positionTooltip(term, tip) {\n  const rect = term.getBoundingClientRect();\n  const tipWidth = 300; // 大约\n  let left = rect.left + rect.width / 2 - tipWidth / 2;\n  // 限制在视口内\n  left = Math.max(8, Math.min(left, window.innerWidth - tipWidth - 8));\n\n  // 先尝试上方\n  let top = rect.top - 8;\n  tip.style.left = left + 'px';\n\n  // 默认定位在上方，如果没有空间则翻转到下方\n  document.body.appendChild(tip);\n  const tipHeight = tip.offsetHeight;\n  if (rect.top - tipHeight - 8 < 0) {\n    // 翻转到下方\n    tip.style.top = (rect.bottom + 8) + 'px';\n    tip.classList.add('flip');\n  } else {\n    tip.style.top = (rect.top - tipHeight - 8) + 'px';\n    tip.classList.remove('flip');\n  }\n}\n\ndocument.querySelectorAll('.term').forEach(term => {\n  const tip = document.createElement('span');\n  tip.className = 'term-tooltip';\n  tip.textContent = term.dataset.definition;\n\n  // 桌面端悬停\n  term.addEventListener('mouseenter', () => {\n    if (activeTooltip && activeTooltip !== tip) {\n      activeTooltip.classList.remove('visible');\n      activeTooltip.remove();\n    }\n    positionTooltip(term, tip);\n    requestAnimationFrame(() => tip.classList.add('visible'));\n    activeTooltip = tip;\n  });\n\n  term.addEventListener('mouseleave', () => {\n    tip.classList.remove('visible');\n    setTimeout(() => { if (!tip.classList.contains('visible')) tip.remove(); }, 150);\n    activeTooltip = null;\n  });\n\n  // 移动端点击\n  term.addEventListener('click', (e) => {\n    e.stopPropagation();\n    if (activeTooltip && activeTooltip !== tip) {\n      activeTooltip.classList.remove('visible');\n      activeTooltip.remove();\n    }\n    if (tip.classList.contains('visible')) {\n      tip.classList.remove('visible');\n      tip.remove();\n      activeTooltip = null;\n    } else {\n      positionTooltip(term, tip);\n      requestAnimationFrame(() => tip.classList.add('visible'));\n      activeTooltip = tip;\n    }\n  });\n});\n\n// 点击其他地方关闭提示框\ndocument.addEventListener('click', () => {\n  if (activeTooltip) {\n    activeTooltip.classList.remove('visible');\n    activeTooltip.remove();\n    activeTooltip = null;\n  }\n});\n```\n\n**规则：**\n- 在每个模块首次使用时标记每个技术术语（API、DOM、回调、异步、端点、中间件等）\n- 定义最多 1-2 句话，使用日常语言\n- 在定义中使用隐喻当它有帮助时 —— 例如，\"**回调**就像在餐厅留下你的电话号码，这样当你的桌子准备好时他们可以打电话给你\"\n- 不要在同一屏幕内标记同一个术语两次 — 只在每个模块首次出现时\n- 虚线下划线应该足够微妙不分散注意力，但也足够明显让好奇的学习者发现它\n\n---\n\n## 可视化文件树\n\n使用此代替列出\"这个文件夹做 X，那个文件夹做 Y\"的段落。更容易扫描。\n\n```html\n<div class=\"file-tree\">\n  <div class=\"ft-folder open\">\n    <span class=\"ft-name\">app/</span>\n    <span class=\"ft-desc\">页面和 API 路由</span>\n    <div class=\"ft-children\">\n      <div class=\"ft-folder\">\n        <span class=\"ft-name\">api/</span>\n        <span class=\"ft-desc\">前端调用的后端端点</span>\n      </div>\n      <div class=\"ft-file\">\n        <span class=\"ft-name\">layout.tsx</span>\n        <span class=\"ft-desc\">包装每个页面的外壳</span>\n      </div>\n    </div>\n  </div>\n  <div class=\"ft-folder\">\n    <span class=\"ft-name\">components/</span>\n    <span class=\"ft-desc\">可复用的 UI 构建块</span>\n  </div>\n  <div class=\"ft-folder\">\n    <span class=\"ft-name\">lib/</span>\n    <span class=\"ft-desc\">共享逻辑和工具</span>\n  </div>\n</div>\n```\n\n```css\n.file-tree { font-family: var(--font-mono); font-size: var(--text-sm); }\n.ft-folder, .ft-file {\n  padding: var(--space-2) var(--space-3);\n  border-left: 2px solid var(--color-border-light);\n  margin-left: var(--space-4);\n}\n.ft-folder > .ft-name { color: var(--color-accent); font-weight: 600; }\n.ft-folder > .ft-name::before { content: '📁 '; }\n.ft-file > .ft-name::before { content: '📄 '; }\n.ft-desc {\n  color: var(--color-text-secondary);\n  font-family: var(--font-body);\n  margin-left: var(--space-2);\n  font-size: var(--text-xs);\n}\n.ft-children { margin-left: var(--space-4); }\n```\n\n---\n\n## 图标-标签行\n\n用于可视化列出组件、功能或概念。替换项目符号段落。\n\n```html\n<div class=\"icon-rows\">\n  <div class=\"icon-row\">\n    <div class=\"icon-circle\" style=\"background: var(--color-actor-1)\">🖥️</div>\n    <div>\n      <strong>前端 (Next.js)</strong>\n      <p>用户看到和交互的内容</p>\n    </div>\n  </div>\n  <div class=\"icon-row\">\n    <div class=\"icon-circle\" style=\"background: var(--color-actor-2)\">⚡</div>\n    <div>\n      <strong>API 路由</strong>\n      <p>在服务器上运行的后端逻辑</p>\n    </div>\n  </div>\n  <div class=\"icon-row\">\n    <div class=\"icon-circle\" style=\"background: var(--color-actor-3)\">🗄️</div>\n    <div>\n      <strong>数据库 (Supabase)</strong>\n      <p>所有数据永久存储的地方</p>\n    </div>\n  </div>\n</div>\n```\n\n```css\n.icon-rows { display: flex; flex-direction: column; gap: var(--space-4); }\n.icon-row {\n  display: flex; align-items: center; gap: var(--space-4);\n  padding: var(--space-4);\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  box-shadow: var(--shadow-sm);\n}\n.icon-row p { margin: 0; color: var(--color-text-secondary); font-size: var(--text-sm); }\n.icon-circle {\n  width: 48px; height: 48px; border-radius: 50%;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 1.25rem; flex-shrink: 0;\n}\n```\n\n---\n\n## 编号步骤卡片\n\n用于本来是编号段落列表的序列。视觉化、可扫描，每步独立存在。\n\n```html\n<div class=\"step-cards\">\n  <div class=\"step-card\">\n    <div class=\"step-num\">1</div>\n    <div class=\"step-body\">\n      <strong>用户粘贴 YouTube URL</strong>\n      <p>前端捕获 URL 并提取视频 ID</p>\n    </div>\n  </div>\n  <div class=\"step-card\">\n    <div class=\"step-num\">2</div>\n    <div class=\"step-body\">\n      <strong>API 获取转录</strong>\n      <p>服务器端路由调用外部服务获取视频文本</p>\n    </div>\n  </div>\n  <div class=\"step-card\">\n    <div class=\"step-num\">3</div>\n    <div class=\"step-body\">\n      <strong>AI 分析内容</strong>\n      <p>转录发送给提取关键时刻的 AI 模型</p>\n    </div>\n  </div>\n</div>\n```\n\n```css\n.step-cards { display: flex; flex-direction: column; gap: var(--space-3); }\n.step-card {\n  display: flex; align-items: flex-start; gap: var(--space-4);\n  padding: var(--space-4) var(--space-5);\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  border-left: 3px solid var(--color-accent);\n  box-shadow: var(--shadow-sm);\n}\n.step-num {\n  width: 32px; height: 32px; border-radius: 50%;\n  background: var(--color-accent);\n  color: white; font-weight: 700;\n  display: flex; align-items: center; justify-content: center;\n  font-family: var(--font-display);\n  flex-shrink: 0;\n}\n.step-body p { margin: var(--space-1) 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); }\n```\n\nFile v1.1.4:references/module-brief-template.md\n\n# 模块简报模板\n\n> **何时阅读此文件：** 在阶段 2.5（规划检查点）期间，用于复杂代码库。每个模块填写一份简报，保存到 `course-name/briefs/0N-slug.md`。每份简报给并行代理编写一个模块所需的一切，无需阅读代码库或 SKILL.md。\n\n**选择适合你课程版本的章节：**\n- 产品版：关注\"教学弧线\"、\"代码↔英语翻译\"、\"交互元素\"\n- 开发版：关注\"教学目标\"、\"设计意图说明\"、\"架构图/数据流图\"\n\n---\n\n## 模块 N：[标题]\n\n### 教学弧线（产品版）\n\n- **隐喻：** [一个新鲜的、具体的隐喻 — 绝不要\"餐厅\"。参见 `references/content-philosophy.md` > 隐喻优先]\n- **开篇钩子：** [一句连接到学习者从使用应用中已知内容的话]\n- **关键见解：** [学习者应该带走理解的一件事]\n- **\"为什么要关心？\"：** [这如何帮助他们驾驭 AI / 调试 / 做决策]\n\n### 教学目标（开发版）\n\n- **核心心智模型：** [学习者在完成模块后应该建立什么样的心智模型？]\n- **关键设计决策：** [这个模块要解释的设计决策是什么？]\n- **实用技能：** [学习者能够做什么？例如，\"能够定位并修改支付流程\"]\n- **深度层次：** [架构层 / 实现层 / 进阶层]\n\n---\n\n### 代码片段（预提取）\n\n**产品版：** 包含模块将在代码↔英语翻译块中使用的实际代码。从代码库复制粘贴，带文件路径和行号。写作代理将逐字使用这些 — 它不会重新阅读代码库。\n\n**开发版：** 包含模块将在代码分析块中使用的实际代码。保持代码完整，包括必要的上下文（导入、类型注解、相关注释）。不要过度简化。\n\n文件：src/example/file.ts（第 12-24 行）\n```typescript\n[在此粘贴实际代码]\n```\n\n文件：src/another/file.ts（第 45-52 行）\n**设计笔记（开发版，预写入）：**\n- 这段代码的设计意图：...\n- 使用了什么模式：...\n- 潜在陷阱：...\n```typescript\n[在此粘贴实际代码]\n```\n\n---\n\n### 交互元素\n\n检查此模块需要哪些元素。包含足够的细节供写作代理构建它们。\n\n**产品版交互元素：**\n- [ ] **代码↔英语翻译** — 使用上面哪些片段\n- [ ] **测验** — [数量] 个问题，风格：[场景 / 调试 / 架构 / 追踪]。每个问题角度的简要描述。\n- [ ] **群聊动画** — 角色：[列表]。消息流摘要：[谁对谁说什么，按什么顺序]\n- [ ] **数据流动画** — 角色：[列表]。步骤：[高亮和数据包移动的序列]\n- [ ] **拖放** — 项目：[列表]，目标：[列表]\n- [ ] **其他** — [架构图、层次切换、模式卡片等]\n\n**开发版交互元素：**\n*注意：测验默认不包含，仅在用户明确要求时添加。*\n- [ ] **代码分析块** — 使用上面哪些片段，重点解释什么设计决策\n- [ ] **架构图** — [类型：组件图 / 层次图 / 部署图]。主要组件：[列表]。关键标注：[信息]\n- [ ] **数据流图** — [角色和步骤，数据包移动序列]\n- [ ] **时序图/状态图** — [按需：展示交互或状态转换]\n- [ ] **深度测验** — （可选，仅用户要求时）[数量] 个问题，类型：[设计决策 / 场景应用 / 问题诊断]\n\n---\n\n### 架构图/数据流图（开发版）\n\n如果模块需要图表，预定义：\n\n- **图表类型：** [组件图 / 时序图 / 数据流图 / 状态图]\n- **主要组件：** [列出图中需要展示的组件]\n- **数据流/关系：** [描述数据如何流动或组件如何交互]\n- **关键标注：** [需要在图中标注的信息]\n\n---\n\n### 设计意图说明（开发版）\n\n专业程序员需要理解\"为什么\"，而不仅仅是\"是什么\"。预提取设计背景：\n\n- **为什么这样设计？** [设计决策的背景和理由]\n- **有哪些权衡？** [选择 A 而不是 B 的原因]\n- **替代方案是什么？** [讨论过但未采用的方案]\n- **扩展点在哪里？** [如何扩展这个模块/功能]\n\n---\n\n### 要阅读的参考文件\n\n只列出写作代理需要的部分 — 不是整个文件。\n\n- `references/interactive-elements.md` → [章节名称]\n- `references/design-system.md` → [仅当需要特定 token 时]\n- `references/gotchas.md` → [始终包含]\n\n根据版本选择：\n- **产品版**：`references/content-philosophy.md` → [始终包含]\n- **开发版**：`references/content-philosophy-pro.md` → [始终包含]\n\n---\n\n### 连接\n\n- **前一个模块：** [标题 — 涵盖了什么，此模块如何在此基础上深入/构建]\n- **后一个模块：** [标题 — 将涵盖什么，此模块如何为后续做铺垫]\n- **语调/风格说明：** [产品版：强调色名称、角色命名约定等 / 开发版：技术深度递进位置]\n\nFile v1.1.4:DESIGN.md\n\n# 设计系统：Codebase to Course（教育课程风格）\n\n> 温暖、易读、面向非技术用户的交互式课程设计系统\n\n## 1. 视觉主题与氛围\n\n**设计风格定位**：教育出版物风格，温暖亲切，强调可读性与交互性\n\n**整体视觉印象**：\n- 浅色温暖的纸张质感背景，像翻阅一本精心设计的教材\n- 深色代码块形成强烈对比，突出代码内容\n- 朱红色作为主要强调色，活泼但不刺眼\n\n**关键视觉特征**：\n- 模块化滚动吸附（scroll-snap）—— 像翻书一样浏览\n- 温暖色调的阴影（绝不使用纯黑阴影）\n- 大号模块编号作为视觉锚点\n- 渐进式动画揭示内容\n- Catppuccin 风格的语法高亮\n\n**设计哲学**：\n> 这不是 IDE，是教学工具。代码块换行显示，绝不出现水平滚动条。\n\n---\n\n## 2. 色彩调色板与角色\n\n### 背景表面\n\n| 角色 | 颜色值 | 用途 |\n|------|--------|------|\n| `--color-bg` | `#FAF7F2` | 主背景，温暖的米白色，像旧纸张 |\n| `--color-bg-warm` | `#F5F0E8` | 交替模块背景，创造视觉节奏 |\n| `--color-bg-code` | `#1E1E2E` | 代码块背景，深靛蓝炭黑 |\n| `--color-surface` | `#FFFFFF` | 卡片表面，纯白 |\n| `--color-surface-warm` | `#FDF9F3` | 温暖的卡片表面 |\n\n### 文字颜色\n\n| 角色 | 颜色值 | 用途 |\n|------|--------|------|\n| `--color-text` | `#2C2A28` | 主文字，深炭黑，对眼睛友好 |\n| `--color-text-secondary` | `#6B6560` | 次要文字，温暖的灰色 |\n| `--color-text-muted` | `#9E9790` | 柔和灰色，用于时间戳、标签 |\n\n### 品牌与强调色\n\n| 角色 | 颜色值 | 用途 |\n|------|--------|------|\n| `--color-accent` | `#D94F30` | 主强调色，朱红色 |\n| `--color-accent-hover` | `#C4432A` | 悬停状态 |\n| `--color-accent-light` | `#FDEEE9` | 浅色背景 |\n| `--color-accent-muted` | `#E8836C` | 柔和强调 |\n\n**可选强调色方案**：\n| 名称 | 主色 | 悬停 | 浅色 | 柔和 |\n|------|------|------|------|------|\n| 珊瑚 | `#E06B56` | `#C85A47` | `#FDECEA` | `#E89585` |\n| 青色 | `#2A7B9B` | `#1F6280` | `#E4F2F7` | `#5A9DB8` |\n| 琥珀 | `#D4A843` | `#BF9530` | `#FDF5E0` | `#E0C070` |\n| 森林 | `#2D8B55` | `#226B41` | `#E8F5EE` | `#5AAD7A` |\n\n### 状态色\n\n| 角色 | 颜色值 | 浅色背景 |\n|------|--------|----------|\n| 成功 | `#2D8B55` | `#E8F5EE` |\n| 错误 | `#C93B3B` | `#FDE8E8` |\n| 信息 | `#2A7B9B` | `#E4F2F7` |\n\n### 角色颜色（用于聊天气泡、图表）\n\n| 角色 | 颜色值 | 名称 |\n|------|--------|------|\n| Actor 1 | `#D94F30` | 朱红色 |\n| Actor 2 | `#2A7B9B` | 青色 |\n| Actor 3 | `#7B6DAA` | 柔和梅红色 |\n| Actor 4 | `#D4A843` | 金色 |\n| Actor 5 | `#2D8B55` | 森林色 |\n\n### 边框与分隔线\n\n| 角色 | 颜色值 |\n|------|--------|\n| `--color-border` | `#E5DFD6` |\n| `--color-border-light` | `#EEEBE5` |\n\n### 语法高亮（Catppuccin 风格）\n\n| 类型 | 颜色值 | 用途 |\n|------|--------|------|\n| `.code-keyword` | `#CBA6F7` | if, else, return, function |\n| `.code-string` | `#A6E3A1` | 字符串 |\n| `.code-function` | `#89B4FA` | 函数名 |\n| `.code-comment` | `#6C7086` | 注释 |\n| `.code-number` | `#FAB387` | 数字 |\n| `.code-property` | `#F9E2AF` | 对象键 |\n| `.code-operator` | `#94E2D5` | =, =>, + |\n| `.code-tag` | `#F38BA8` | HTML 标签 |\n| `.code-attr` | `#F9E2AF` | HTML 属性 |\n| `.code-value` | `#A6E3A1` | 属性值 |\n\n---\n\n## 3. 字体规则\n\n### 字体族\n\n```css\n--font-display: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n--font-body:    'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;\n--font-mono:    'JetBrains Mono', 'Fira Code', 'Source Code Pro', 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;\n```\n\n> **注意**：使用系统字体回退，无需外部字体链接，确保中国大陆用户无需翻墙即可正常显示。\n\n### 层级表格\n\n| 角色 | 字体 | 大小 | 字重 | 行高 | 用途 |\n|------|------|------|------|------|------|\n| 模块编号 | Display | `3.75rem` (60px) | 800 | 1 | 大号背景装饰 |\n| 模块标题 | Display | `2.25rem` (36px) | 700 | 1.15 | 主标题 |\n| 模块副标题 | Body | `1.125rem` (18px) | 400 | 1.3 | 描述文字 |\n| 屏幕标题 | Display | `1.5rem` (24px) | 600 | 1.3 | 子标题 |\n| 正文 | Body | `1rem` (16px) | 400 | 1.6 | 段落文字 |\n| 引导段落 | Body | `1.125rem` (18px) | 400 | 1.6 | 重要段落 |\n| 代码 | Mono | `0.875rem` (14px) | 400 | 1.7 | 代码块 |\n| 标签/徽章 | Mono | `0.75rem` (12px) | 400 | - | 大写 + 0.05em 字间距 |\n\n### 类型比例（1.25 比例）\n\n```css\n--text-xs:   0.75rem;    /* 12px */\n--text-sm:   0.875rem;   /* 14px */\n--text-base: 1rem;       /* 16px */\n--text-lg:   1.125rem;   /* 18px */\n--text-xl:   1.25rem;    /* 20px */\n--text-2xl:  1.5rem;     /* 24px */\n--text-3xl:  1.875rem;   /* 30px */\n--text-4xl:  2.25rem;    /* 36px */\n--text-5xl:  3rem;       /* 48px */\n--text-6xl:  3.75rem;    /* 60px */\n```\n\n---\n\n## 4. 组件样式\n\n### 按钮\n\n**主要按钮**\n```css\n.btn-primary {\n  background: var(--color-accent);\n  color: white;\n  border: none;\n  padding: var(--space-2) var(--space-5);\n  border-radius: var(--radius-sm);\n  font-weight: 600;\n}\n.btn-primary:hover {\n  background: var(--color-accent-hover);\n  transform: translateY(-1px);\n}\n```\n\n**次要按钮**\n```css\n.btn {\n  background: var(--color-surface);\n  color: var(--color-text-secondary);\n  border: 1px solid var(--color-border);\n  padding: var(--space-2) var(--space-5);\n  border-radius: var(--radius-sm);\n}\n.btn:hover {\n  border-color: var(--color-accent-muted);\n  color: var(--color-accent);\n}\n```\n\n### 卡片与容器\n\n**基础卡片**\n```css\n.pattern-card {\n  background: var(--color-surface);\n  border-radius: var(--radius-md);\n  padding: var(--space-6);\n  box-shadow: var(--shadow-sm);\n  border-top: 3px solid var(--color-accent);\n}\n.pattern-card:hover {\n  transform: translateY(-4px);\n  box-shadow: var(--shadow-md);\n}\n```\n\n**测验容器**\n```css\n.quiz-container {\n  background: var(--color-surface);\n  border-radius: var(--radius-lg);\n  padding: var(--space-8);\n  box-shadow: var(--shadow-md);\n}\n```\n\n**代码翻译块**\n```css\n.translation-block {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  border-radius: var(--radius-md);\n  overflow: hidden;\n  box-shadow: var(--shadow-md);\n}\n.translation-code {\n  background: var(--color-bg-code);\n  color: #CDD6F4;\n  padding: var(--space-6);\n}\n.translation-english {\n  background: var(--color-surface-warm);\n  padding: var(--space-6);\n  border-left: 3px solid var(--color-accent);\n}\n```\n\n### 徽章与标签\n\n**代码徽章**\n```css\n.badge-code {\n  font-family: var(--font-mono);\n  font-size: var(--text-sm);\n  background: var(--color-bg-code);\n  color: #CBA6F7;\n  padding: var(--space-1) var(--space-3);\n  border-radius: var(--radius-sm);\n}\n```\n\n**拖拽芯片**\n```css\n.dnd-chip {\n  background: var(--color-accent);\n  color: white;\n  border-radius: var(--radius-full);\n  padding: var(--space-2) var(--space-4);\n  font-weight: 600;\n  cursor: grab;\n}\n```\n\n### 导航\n\n**顶部导航**\n```css\n.nav {\n  position: fixed;\n  top: 0;\n  height: 50px;\n  background: rgba(250,247,242,0.92);\n  backdrop-filter: blur(8px);\n  border-bottom: 1px solid var(--color-border-light);\n}\n```\n\n**导航点**\n```css\n.nav-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  border: 2px solid var(--color-text-muted);\n  background: transparent;\n}\n.nav-dot.active {\n  border-color: var(--color-accent);\n  background: var(--color-accent);\n  box-shadow: 0 0 0 3px var(--color-accent-light);\n}\n.nav-dot.visited {\n  border-color: var(--color-accent-muted);\n  background: var(--color-accent-muted);\n}\n```\n\n### 提示框（Callout）\n\n```css\n.callout {\n  display: flex;\n  gap: var(--space-4);\n  padding: var(--space-5);\n  border-radius: var(--radius-md);\n  border-left: 4px solid;\n}\n.callout-accent  { background: var(--color-accent-light);  border-color: var(--color-accent); }\n.callout-info    { background: var(--color-info-light);    border-color: var(--color-info); }\n.callout-warning { background: var(--color-error-light);   border-color: var(--color-error); }\n```\n\n---\n\n## 5. 布局原则\n\n### 间距系统\n\n```css\n--space-1:  0.25rem;   /* 4px */\n--space-2:  0.5rem;    /* 8px */\n--space-3:  0.75rem;   /* 12px */\n--space-4:  1rem;      /* 16px */\n--space-5:  1.25rem;   /* 20px */\n--space-6:  1.5rem;    /* 24px */\n--space-8:  2rem;      /* 32px */\n--space-10: 2.5rem;    /* 40px */\n--space-12: 3rem;      /* 48px */\n--space-16: 4rem;      /* 64px */\n--space-20: 5rem;      /* 80px */\n--space-24: 6rem;      /* 96px */\n```\n\n### 网格与容器\n\n```css\n--content-width:      800px;   /* 标准阅读宽度 */\n--content-width-wide: 1000px;  /* 用于并排布局 */\n--nav-height:         50px;\n```\n\n### 模块布局\n\n```css\n.module {\n  min-height: 100dvh;\n  scroll-snap-align: start;\n  padding: var(--space-16) var(--space-6);\n  padding-top: calc(var(--nav-height) + var(--space-12));\n}\n.module-content {\n  max-width: var(--content-width);\n  margin: 0 auto;\n}\n```\n\n### 圆角刻度\n\n| 名称 | 值 | 用途 |\n|------|------|------|\n| `--radius-sm` | `8px` | 按钮、标签、输入框 |\n| `--radius-md` | `12px` | 卡片、容器 |\n| `--radius-lg` | `16px` | 大型容器、模态框 |\n| `--radius-full` | `9999px` | 胶囊形状、头像 |\n\n---\n\n## 6. 深度与层级\n\n### 阴影系统\n\n| 级别 | 值 | 用途 |\n|------|-----|------|\n| `--shadow-sm` | `0 1px 2px rgba(44,42,40,0.05)` | 微妙提升 |\n| `--shadow-md` | `0 4px 12px rgba(44,42,40,0.08)` | 卡片、容器 |\n| `--shadow-lg` | `0 8px 24px rgba(44,42,40,0.10)` | 浮动元素 |\n| `--shadow-xl` | `0 16px 48px rgba(44,42,40,0.12)` | 模态框 |\n\n**阴影哲学**：使用温暖色调的 RGBA（44, 42, 40），绝不使用纯黑色阴影。\n\n### 层级处理\n\n| 级别 | 处理方式 | 用途 |\n|------|----------|------|\n| Flat | 无阴影 | 页面背景 |\n| Subtle | shadow-sm | 标签、小卡片 |\n| Elevated | shadow-md | 交互卡片、测验 |\n| Floating | shadow-lg/xl | 提示框、模态 |\n\n---\n\n## 7. 宜与忌\n\n### 宜\n\n- 偶数模块使用 `--color-bg`，奇数模块使用 `--color-bg-warm`（交替背景创造视觉节奏）\n- 代码块始终使用 `--color-bg-code` 配浅色文本\n- 使用滚动吸附（scroll-snap）让用户逐模块浏览\n- 使用 `animate-in` 类实现滚动触发的淡入动画\n- 使用温暖色调的阴影\n- 代码换行显示，绝不出现水平滚动条\n- 从真实代码库中选择简短片段（5-10 行）\n\n### 忌\n\n- 绝不使用纯黑色阴影\n- 绝不使用紫色渐变作为主色调\n- 绝不修改、修剪或简化代码片段\n- 绝不在代码块中显示水平滚动条\n- 避免模块之间没有视觉区分\n- 避免使用过于鲜艳的颜色\n\n---\n\n## 8. 响应式行为\n\n### 断点表格\n\n| 名称 | 宽度 | 关键变化 |\n|------|------|----------|\n| Tablet | ≤768px | 标题尺寸缩小，代码/英语堆叠显示 |\n| Mobile | ≤480px | 进一步缩小标题，单列卡片，垂直流程图 |\n\n### 断点样式\n\n**平板 (≤768px)**\n```css\n:root {\n  --text-4xl: 1.875rem;\n  --text-5xl: 2.25rem;\n  --text-6xl: 3rem;\n}\n.translation-block { grid-template-columns: 1fr; }\n.pattern-cards { grid-template-columns: 1fr 1fr; }\n```\n\n**手机 (≤480px)**\n```css\n:root {\n  --text-4xl: 1.5rem;\n  --text-5xl: 1.875rem;\n  --text-6xl: 2.25rem;\n}\n.module { padding: var(--space-8) var(--space-4); }\n.pattern-cards { grid-template-columns: 1fr; }\n.flow-steps { flex-direction: column; }\n.flow-arrow { transform: rotate(90deg); }\n```\n\n---\n\n## 9. Agent 提示指南\n\n### 快速颜色参考\n\n```\n主背景:     #FAF7F2\n主文字:     #2C2A28\n强调色:     #D94F30\n代码背景:   #1E1E2E\n边框:       #E5DFD6\n```\n\n### 动画与过渡\n\n```css\n--ease-out:    cubic-bezier(0.16, 1, 0.3, 1);\n--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n--duration-fast:   150ms;\n--duration-normal: 300ms;\n--duration-slow:   500ms;\n--stagger-delay:   120ms;\n```\n\n### 滚动动画\n\n```css\n.animate-in {\n  opacity: 0;\n  transform: translateY(20px);\n  transition:\n    opacity var(--duration-slow) var(--ease-out),\n    transform var(--duration-slow) var(--ease-out);\n}\n.animate-in.visible {\n  opacity: 1;\n  transform: translateY(0);\n}\n```\n\n### 模块 HTML 模板\n\n```html\n<section class=\"module\" id=\"module-N\" style=\"background: var(--color-bg)\">\n  <div class=\"module-content\">\n    <header class=\"module-header animate-in\">\n      <span class=\"module-number\">0N</span>\n      <h1 class=\"module-title\">模块标题</h1>\n      <p class=\"module-subtitle\">一句话描述</p>\n    </header>\n    <div class=\"module-body\">\n      <!-- 内容 -->\n    </div>\n  </div>\n</section>\n```\n\n### 滚动吸附设置\n\n```css\nhtml {\n  scroll-snap-type: y proximity;\n  scroll-behavior: smooth;\n}\n```\n\n### 自定义滚动条\n\n```css\n::-webkit-scrollbar { width: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb {\n  background: var(--color-border);\n  border-radius: var(--radius-full);\n}\n```\n\nFile v1.1.4:LICENSE\n\nMIT License\n\nCopyright (c) 2026 zhouchang1988\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substa\n\nArchive v1.1.3: 16 files, 56806 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3479b), references/_base.html (1949b), references/_footer.html (61b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (5970b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35066b), SKILL.md (12233b), _meta.json (140b)\n\nArchive v1.1.2: 16 files, 56806 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3479b), references/_base.html (1949b), references/_footer.html (61b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (5970b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35066b), SKILL.md (12233b), _meta.json (140b)\n\nArchive v1.1.1: 16 files, 56805 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3479b), references/_base.html (1949b), references/_footer.html (61b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (5970b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35066b), SKILL.md (12235b), _meta.json (140b)\n\nArchive v1.1.0: 16 files, 56753 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3290b), references/_base.html (1949b), references/_footer.html (61b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (5970b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35066b), SKILL.md (12235b), _meta.json (140b)\n\nArchive v1.0.0: 16 files, 58167 bytes\n\nFiles: DESIGN.md (12981b), LICENSE (1070b), README.md (3290b), references/_base.html (1949b), references/_footer.html (27b), references/build.sh (210b), references/content-philosophy-pro.md (9362b), references/content-philosophy.md (8337b), references/design-system.md (12290b), references/gotchas.md (7252b), references/interactive-elements.md (31572b), references/main.js (19539b), references/module-brief-template.md (4815b), references/styles.css (35066b), SKILL.md (11747b), _meta.json (140b)","readmeExcerpt":"Skill: codebase-to-course-cn Owner: zhouchang1988 Summary: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。 Tags: latest:1.2.0 Version history: v1.2.0 | 2026-05-21T09:15:07.115Z | user Release v1.2.0 from GitHub v1.1.4 | 2026-05-19T04:32:06.966Z | user Release v1.1.4 from GitHub v1.1.3 | 2026-05-15T02:27:17.173Z | user Release v1.","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"{英文关键词}-course-pm/  （产品版）\n{英文关键词}-course-pro/ （开发版）\n  styles.css       ← 从 references/styles.css 逐字复制\n  main.js          ← 从 references/main.js 逐字复制\n  _base.html       ← 定制的外壳（标题使用中文关键词、强调色、导航点）\n  _footer.html     ← 从 references/_footer.html 逐字复制\n  build.sh         ← 从 references/build.sh 逐字复制\n  briefs/          ← 模块简报（仅限复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 由 build.sh 组装"},{"language":"bash","snippet":"cd {英文关键词}-course-{pm|pro} && bash build.sh"},{"language":"text","snippet":"将 ./my-project 转换为课程"},{"language":"text","snippet":"将此转换为开发版课程"},{"language":"text","snippet":"从 https://github.com/user/repo 制作课程"},{"language":"text","snippet":"course-name/\n  styles.css       ← 预构建样式\n  main.js          ← 交互逻辑\n  _base.html       ← 页面外壳\n  _footer.html     ← 页脚\n  build.sh         ← 组装脚本\n  briefs/          ← 模块简报（复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 组装后的完整课程"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: codebase-to-course-cn\ndescription: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。\n---\n\n# 代码库转课程\n\n将任意代码库转换为精美的交互式课程。输出是一个**目录**，包含预构建的 `styles.css`、`main.js`、每个模块的 HTML 文件以及组装好的 `index.html` — 可直接在浏览器中打开，无需任何设置。\n\n**支持两个版本：**\n- **产品版（默认）**：面向产品经理，聚焦业务逻辑、用户流程和系统架构，不含代码细节\n- **开发版**：面向程序员，包含架构图、数据流动画、技术方案描述\n\n**重要：中国大陆可访问性要求**\n\n此技能面向中国大陆用户，**必须确保生成的课程在中国大陆网络环境下可以完全离线运行**：\n- **禁止使用 Google Fonts** 及任何 Google CDN 资源\n- **禁止使用外部 CDN** 加载字体、样式或脚本\n- **禁止依赖任何需要翻墙才能访问的资源**\n- 所有字体必须使用系统字体或内嵌字体\n- 所有资源必须本地化，确保零网络依赖\n\n## 首次运行互动流程\n\n当技能触发时，按顺序向用户收集以下信息，**全部收集完毕后才开始执行**：\n\n### 第 1 步：选择版本\n\n> 你希望生成哪个版本？\n> 1. **产品版** — 面向产品经理，聚焦业务逻辑、用户流程和整体架构（默认）\n> 2. **开发版** — 面向专业程序员，聚焦架构图、数据流和技术方案\n>\n> 也可以带上版本继续，例如\"将此转换为开发版课程\"。\n\n### 第 2 步：指定关键词\n\n> 请提供一个**中文关键词**和一个**英文关键词**，用于课程命名：\n> - **中文关键词**：用于课程标题和导航显示（例如：\"任务调度\"、\"支付系统\"）\n> - **英文关键词**：用于输出目录名和文件命名（例如：`task-scheduler`、`payment`）\n\n### 输出目录规则\n\n根据用户选择的版本和英文关键词，确定课程的输出目录名：\n- **产品版**：`{英文关键词}-course-pm`（例如：`task-scheduler-course-pm`）\n- **开发版**：`{英文关键词}-course-pro`（例如：`task-scheduler-course-pro`）\n\n**关于测验：**\n- 产品版 **不包含测验**\n- 开发版 **默认不需要测验**，仅当用户明确要求\"增加测验\"时才添加\n\n**关于代码库来源：**\n\n如果用户提供 GitHub 链接，在开始分析之前先克隆仓库（`git clone <url> /tmp/<repo-name>`）。如果他们说\"此代码库\"或类似表述，使用当前工作目录。\n\n**快捷方式：** 用户也可以在一条消息中同时提供所有信息，例如：\n- \"开发版，中文关键词：任务调度，英文关键词：task-scheduler\"\n- \"产品版 / 支付系统 / payment\"\n此时跳过逐步询问，直接开始执行。\n\n---\n\n## 产品版 vs 开发版 对比\n\n| 特性 | 产品版（默认） | 开发版 |\n|---|---|---|\n| **目标受众** | 产品经理、业务人员 | 专业程序员 |\n| **教学方式** | 业务流程图、用户旅程、系统架构概览 | 架构图、数据流动画、技术方案 |\n| **代码展示** | **不展示代码** | 精选10-15行核心代码 + 设计意图 |\n| **测验** | **无** | **默认不需要**（用户要求时才添加） |\n| **视觉元素** | 流程图、业务架构图、用户旅程图、功能模块图 | 架构图、时序图、数据流图 |\n| **语言风格** | 业务语言、产品术语、结构化清晰 | 简洁、信息密度高、技术术语 |\n\n---\n\n## 产品版详细指南\n\n目标学习者是**产品经理和业务人员** —— 需要理解系统做什么、业务逻辑怎么流转、各模块如何协作，但不需要知道代码细节。\n\n**关键原则：**\n- 不展示任何代码\n- 用业务语言解释系统行为，避免技术实现细节\n- 聚焦\"系统做了什么\"而非\"代码怎么写\"\n- 用流程图和架构图可视化业务逻辑\n- 关注用户旅程、数据流转、模块职责\n- 解释清楚各功能模块的输入/输出/边界\n\n**强制交互元素（每个模块必须包含）：**\n- **业务流程图** — 至少一个（展示核心业务逻辑流转）\n- **系统架构概览图** — 整个课程至少2个（模块关系、职责划分）\n- **用户旅程图** — 整个课程至少一个（端到端用户操作路径）\n- **功能模块卡片** — 展示各模块职责、输入输出\n- **数据流转动画** — 整个课程至少一个（业务数据如何在系统中流动）\n\n**禁止包含：**\n- 代码片段（任何形式）\n- 代码翻译块\n- 测验题\n- 技术实现细节（如算法、数据结构、设计模式名称）\n- 词汇表提示（产品经理不需要学习编程术语）\n\n---\n\n## 开发版详细指南\n\n目标学习者是**专业程序员** —— 需要快速理解代码库架构、准备技术分享或代码评审。\n\n**核心原则：图表驱动，代码精简。**\n\n**关键原则：**\n- 信息密度优先，减少\"废话\"\n- 直接使用技术术语，无需解释基础概念\n- **图表和动画优先于代码贴片**\n- 代码仅作为补充（每片段10-15行）\n- 60%+ 图表/动画，25% 简洁文字，15% 精选代码\n\n**测验为可选功能：** 默认不包含。仅当用户明确要求\"增加测验\"或\"需要测验题\"时才添加。\n\n**强制交互元素：**\n- **架构图** — 每个课程至少2个（交互式架构图，点击组件显示描述）\n- **数据流动画** — 每个课程至少2个（请求/响应流转）\n- **时序图/状态图** — 每个课程至少1个\n- **代码分析块** — 整个课程2-3个（不是每个模块都需要）\n\n**内容比例：**\n- 60%+ 图表、动画、交互式可视化\n- 25% 文字描述（技术方案、设计决策）\n- 15% 代码片段（每片段10-15行）\n\n---\n\n## 流程\n\n### 阶段 0：收集参数\n\n在开始前，确认以下三个参数已收集完毕：\n\n| 参数 | 用途 | 示例 |\n|---|---|---|\n| **版本** | 决定内容风格和输出目录后缀 | 产品版 / 开发版 |\n| **中文关键词** | 课程标题和导航显示 | \"任务调度\" |\n| **英文关键词** | 输出目录名和文件命名 | `task-scheduler` |\n\n**输出目录名规则：**\n- 产品版：`{英文关键词}-course-pm`\n- 开发版：`{英文关键词}-course-pro`\n\n如果用户已在首次互动中提供了所有参数，直接记录并继续。如果缺少任何参数，先补充询问。\n\n**记"},{"path":"README.md","content":"# codebase-to-course-cn\n\n> 将任意代码库转换为精美的交互式课程，直接在浏览器中打开，无需任何设置。\n\n**灵感来自 [codebase-to-course](https://github.com/zarazhangrui/codebase-to-course)** — 在原项目思路基础上进行了中文本地化适配和功能增强。\n\n## 它是什么\n\n一个 Claude Code 技能（Skill），能够分析任意代码库并生成交互式 HTML 课程。输出是一个完整目录，包含 `styles.css`、`main.js`、模块 HTML 文件和组装好的 `index.html`，双击即可在浏览器中浏览。\n\n## 两个版本\n\n| | 产品版（默认） | 开发版 |\n|---|---|---|\n| **面向谁** | 产品经理、业务人员 | 专业程序员 |\n| **怎么教** | 业务流程图、用户旅程、系统架构概览 | 架构图、数据流动画、技术方案 |\n| **代码展示** | 不展示代码 | 精选 10-15 行核心代码 |\n| **测验** | 无 | 默认不含，按需添加 |\n| **视觉风格** | 温暖亲切，像翻阅教材 | 简洁专业，信息密度高 |\n\n## 交互元素\n\n- **代码↔中文翻译块** — 左侧代码，右侧通俗解释（开发版）\n- **群聊动画** — 组件之间的对话模拟（产品版）\n- **业务流程图** — 核心业务逻辑流转可视化（产品版）\n- **数据流/消息流动画** — 请求从 A 到 B 的可视化\n- **架构图** — 点击组件查看描述（开发版）\n- **时序图/状态图** — 生命周期与状态流转（开发版）\n- **嵌入式测验** — 多选、场景、拖放、找 bug（开发版，按需添加）\n- **词汇表提示** — 技术术语首次出现时自动标注（产品版）\n\n## 使用方式\n\n在 Claude Code 中触发：\n\n```\n将 ./my-project 转换为课程\n```\n\n或指定版本：\n\n```\n将此转换为开发版课程\n```\n\n或从 GitHub 仓库生成：\n\n```\n从 https://github.com/user/repo 制作课程\n```\n\n## 中国大陆适配\n\n本技能面向中国大陆用户，**所有生成的课程均可完全离线运行**：\n\n- 禁止使用 Google Fonts 及任何 Google CDN 资源\n- 禁止使用外部 CDN 加载字体、样式或脚本\n- 字体使用系统字体回退（PingFang SC、Microsoft YaHei）\n- 零网络依赖，断网也能正常浏览\n\n## 生成的课程结构\n\n```\ncourse-name/\n  styles.css       ← 预构建样式\n  main.js          ← 交互逻辑\n  _base.html       ← 页面外壳\n  _footer.html     ← 页脚\n  build.sh         ← 组装脚本\n  briefs/          ← 模块简报（复杂代码库）\n  modules/\n    01-slug.html\n    02-slug.html\n    ...\n  index.html       ← 组装后的完整课程\n```\n\n## 技术细节\n\n- **视觉设计**：教育出版物风格，温暖米白背景（`#FAF7F2`），朱红强调色（`#D94F30`），Catppuccin 语法高亮\n- **布局**：滚动吸附（scroll-snap），逐模块翻页浏览\n- **动画**：滚动触发的淡入效果，渐进式内容揭示\n- **响应式**：支持桌面、平板、手机三种断点\n\n## 与原项目的关系\n\n本项目是 [codebase-to-course](https://github.com/zarazhangrui/codebase-to-course) 的中文本地化衍生版本，主要变更：\n\n- 课程内容默认输出中文\n- 移除所有外部 CDN 依赖，确保中国大陆可访问\n- 字体栈替换为中文系统字体回退\n- 新增产品版和开发版双模式（产品版聚焦业务逻辑，开发版聚焦架构图、数据流动画、技术方案）\n- 交互元素实现模式本地化适配\n\n## 许可证\n\n与原项目保持一致。"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7a799d479xj5aftb4bqm6ea984cv4m\",\n  \"slug\": \"codebase-to-course-cn\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1779354907115\n}"},{"path":"references/content-philosophy-pro.md","content":"# 内容哲学 — 专业程序员版\n\n> **何时阅读此文件：** 在阶段 3（编写模块 HTML）期间。这些原则指导每个内容决策。\n\n这些原则是将优秀技术课程与普通教程区分开来的关键。\n\n### 信息密度优先\n\n专业程序员阅读速度快，理解能力强。课程应该信息密度高，减少\"废话\"。\n\n**文字原则：**\n- 每个段落有明确信息增量。如果没有新信息，删掉它。\n- 直接使用技术术语，无需解释基础概念。\n- **图表和动画优先于代码贴片。** 能用架构图、数据流动画、时序图表达的，就用可视化。\n- **代码仅作为补充。** 仅在图表无法替代时才展示代码，且每片段控制在 10-15 行。\n- 60%+ 的屏幕应该是图表、动画或交互元素，25% 是简洁的文字描述，15% 是精选代码。\n\n**将描述转换为可视化（优先级排序）：**\n- \"组件 A 调用组件 B\" → **数据流动画**（首选）或**时序图**\n- \"数据从 X 流向 Y\" → **数据流动画**\n- \"系统架构\" → **交互式架构图**（点击组件显示描述）\n- \"请求处理流程\" → **流程图** + **步骤卡片**\n- \"状态变化\" → **状态图动画**\n- \"这个函数的核心逻辑\" → **精简代码片段（10-15 行）+ 设计意图注释**（仅在可视化不足以表达时使用）\n\n**避免的做法：**\n- 不要贴超过 15 行的代码块 — 如果需要展示更多代码，拆分为多个小片段，每个片段聚焦一个概念\n- 不要连续放置两个代码块而中间没有可视化元素\n- 不要用代码来展示可以用图表表达的关系和流程\n\n### 代码分析块 — 精选且聚焦\n\n面向专业程序员的代码分析，重点不是\"这段代码做什么\"（他们能看懂代码），而是\"为什么要这样设计\"。**一个课程中只需要 2-3 个代码分析块**，每个聚焦最核心的设计决策。\n\n**代码选取原则：**\n- 每片段 **不超过 10-15 行** — 选取最能体现设计意图的核心代码\n- 优先选取：接口定义、核心数据结构、关键算法的核心逻辑\n- 不选取：配置代码、样板代码、工具函数、导入语句\n\n**结构：**\n- 左侧：精选的核心代码（保持格式，允许水平滚动）\n- 右侧：设计意图注释（不是逐行翻译）\n\n**示例：**\n\n```html\n<div class=\"translation-block animate-in\">\n  <div class=\"translation-code\">\n    <span class=\"translation-label\">CODE</span>\n    <pre><code>\n<span class=\"code-line\"><span class=\"code-keyword\">export class</span> <span class=\"code-function\">EventBus</span> {</span>\n<span class=\"code-line\">  <span class=\"code-keyword\">private</span> listeners = <span class=\"code-keyword\">new</span> <span class=\"code-function\">Map</span>&lt;string, Set&lt;Function&gt;&gt;();</span>\n<span class=\"code-line\"></span>\n<span class=\"code-line\">  <span class=\"code-function\">subscribe</span>(event: string, fn: Function) {</span>\n<span class=\"code-line\">    <span class=\"code-keyword\">if</span> (!<span class=\"code-keyword\">this</span>.listeners.has(event)) {</span>\n<span class=\"code-line\">      <span class=\"code-keyword\">this</span>.listeners.set(event, <span class=\"code-keyword\">new</span> Set());</span>\n<span class=\"code-line\">    }</span>\n<span class=\"code-line\">    <span class=\"code-keyword\">this</span>.listeners.get(event)!.add(fn);</span>\n<span class=\"code-line\">  }</span>\n<span class=\"code-line\">}</span>\n    </code></pre>\n  </div>\n  <div class=\"translation-english\">\n    <span class=\"translation-label\">DESIGN INTENT</span>\n    <div class=\"translation-lines\">\n      <p class=\"tl\"><strong>为什么用 Map + Set？</strong></p>\n      <p class=\"tl\">Map 提供 O(1) 事件查找，Set 自动去重，防止同一监听器被多次注册。这是比数组更高效的选择。</p>\n      <p class=\"tl\"><strong>扩展点：</strong>可替换为 WeakMap 避免内存泄漏，或添加优先级支持。</p>\n    </div>\n  </div>\n</div>\n```\n\n**关键：**\n- 代码保持原始格式，**允许水平滚动**（与 cn 版本不同）\n- 右侧解释\"为什么\"，不是\"什么\"\n- 指出设计权衡、扩展点、潜在陷阱\n\n### 架构图 — 课程的核心视觉元素\n\n架构图是课程中**最重要的元素**。每个课程必须包含至少 2 个架构图。使用交互式架构图（点击组件显示描述）。\n\n**架构图类型：**\n- **组件图** — 展示模块边界和依赖关系\n- **层次图** — 展示分层架构（表现层、业务层、数据层）\n- **部署图** — 展示服务拓扑和数据流\n\n**设计原则：**\n- 清晰的边界和箭头方向\n- 使用颜色区分不同类型的组件\n- 标注关键数据流路径\n- 包含图例\n- **每个组件可点击**，显示职责描述和关键接口\n\n### 数据流动画 — 追踪请求生命周期\n\n数据流动画是仅次于架构图的第二重要元素。每个课程至少 2 个。展示请求从入口到响应的完整旅程。\n\n**内容：**\n- 入口点（路由、控制器）\n- 中间件链\n- 业务逻辑层\n- 数据访问层\n- 外部服务调用\n- 响应返回路径\n\n**动画化：** 使用 `data-steps` 属性创建分步动画，让学习者逐步追踪数据流。\n\n### 技术方案描述 — 简洁文字替代"},{"path":"references/content-philosophy.md","content":"# 内容哲学 — 产品经理版\n\n> **何时阅读此文件：** 在阶段 2.5（编写模块简报）和阶段 3（编写模块 HTML）期间。这些原则指导每个内容决策 —— 展示什么、如何解释以及用什么方式呈现。\n\n这些原则是让产品经理真正看懂系统的关键。\n\n### 核心原则：业务语言，零代码\n\n产品经理需要理解\"系统做了什么\"和\"业务逻辑怎么流转\"，不需要知道\"代码怎么写\"。\n\n**绝对禁止：**\n- 任何代码片段（不论长短）\n- 代码翻译块\n- 技术实现细节（算法名称、数据结构、设计模式术语）\n- 编程术语的词汇表提示\n- 测验题\n\n**语言规范：**\n- 用业务语言描述系统行为：\"用户提交订单后，系统自动校验库存\"而非\"调用 checkInventory() 方法\"\n- 用\"模块\"、\"服务\"、\"功能\"等产品经理熟悉的词汇\n- 避免\"类\"、\"函数\"、\"接口\"、\"中间件\"等编程术语\n- 可以提及技术组件的名字（如\"数据库\"、\"缓存\"、\"消息队列\"），但只解释它在业务中的角色\n\n### 展示，不要讲述 —— 极致视觉化\n\n产品经理习惯看流程图和架构图。课程应该更像产品文档而不是技术文档。\n\n**文字限制：**\n- 每个文字块最多 **3-4 个句子**。如果你在写第五句，停下来把它转换为视觉元素。\n- 每个屏幕必须 **至少 60% 是视觉内容**（流程图、架构图、卡片、模块关系图）。\n\n**将文字转换为视觉元素：**\n- 步骤描述 → **流程图**（带清晰的开始/结束/分支）\n- \"模块 A 和模块 B 协作\" → **系统架构图**（带箭头和标注）\n- \"这个功能包含 X、Y、Z\" → **功能模块卡片**（图标 + 一句话描述）\n- \"用户先做 A，再做 B\" → **用户旅程图**（带步骤编号）\n- \"数据从 X 流到 Y\" → **数据流转动画**\n- 列表描述 → **带图标的卡片网格**\n- 条件逻辑 → **决策树/分支流程图**\n\n**视觉呼吸空间：**\n- 在元素之间使用充足的间距\n- 在全宽视觉元素和窄文字块之间交替，创造节奏\n- 每个模块应该至少有一个\"英雄视觉\" —— 一个主导屏幕的图表或交互元素\n\n### 业务流程图 — 课程的核心\n\n业务流程图是产品版课程中**最重要的元素**。每个模块至少一个。\n\n**流程图类型：**\n- **用户操作流程** — 用户从开始到完成某个目标的完整路径\n- **业务逻辑流程** — 系统内部业务规则的执行顺序和分支\n- **审批/状态流程** — 数据或实体的状态变迁\n- **异常处理流程** — 出错时系统的应对策略\n\n**设计原则：**\n- 用业务语言标注每个节点（\"校验用户身份\"而非\"auth middleware\"）\n- 分支条件用业务条件表述（\"库存充足？\"而非\"inventory > 0\"）\n- 标注关键的输入和输出\n- 使用颜色区分正常路径和异常路径\n\n### 系统架构概览 — 让PM看到全景\n\n用简化的架构图展示系统的\"大图\"。不需要技术细节，只需要模块边界和职责。\n\n**展示内容：**\n- 各功能模块的名称和一句话职责\n- 模块之间的调用/依赖关系（用箭头和简短标注）\n- 外部系统的集成点\n- 数据存储的位置和用途\n\n**不展示：**\n- 具体的技术实现（框架名、库名）\n- 代码层面的依赖关系\n- 部署细节\n\n### 用户旅程图 — 端到端的产品体验\n\n从用户的视角描述使用系统的完整流程。\n\n**结构：**\n- 明确的起点（用户意图）和终点（目标达成）\n- 每一步标注：用户做了什么 → 系统做了什么 → 用户看到什么\n- 标注关键的决策点和分支路径\n- 标注可能出错的地方和系统的应对\n\n### 功能模块卡片 — 职责一目了然\n\n每个模块用一张卡片展示其核心信息。\n\n**卡片包含：**\n- 模块名称（业务名称，不是代码文件名）\n- 一句话职责描述\n- 核心功能列表（3-5 条）\n- 与其他模块的关系（谁给它数据、它给谁数据）\n\n### 数据流转 — 讲清楚\"数据去哪了\"\n\n产品经理最关心的问题之一：数据从哪来、怎么处理、到哪去。\n\n**展示方式：**\n- 用动画展示数据在系统中的流动路径\n- 每个节点标注\"对数据做了什么\"（而非\"用什么技术做的\"）\n- 标注数据格式的变化（如\"用户填的表单\" → \"存储的订单记录\"）\n- 标注数据持久化的位置\n\n### 每个屏幕一个概念\n\n不要堆砌信息。模块内的每个屏幕讲清楚一件事。\n\n**好的屏幕主题：**\n- \"订单创建的完整流程\"\n- \"支付模块和库存模块如何配合\"\n- \"系统如何处理退款请求\"\n\n**差的屏幕主题：**\n- \"后端架构和所有API\" — 太大了，拆分\n- \"技术栈介绍\" — 产品经理不需要这个\n\n### 业务规则可视化\n\n系统中的业务规则是产品经理最需要理解的部分。\n\n**展示方式：**\n- 用决策树展示条件逻辑（\"如果 VIP 用户 → 免运费；如果新用户 → 首单折扣\"）\n- 用状态图展示实体的生命周期（订单：待支付 → 已支付 → 已发货 → 已完成）\n- 用表格对比不同场景的处理方式\n\n### 模块间内容递进\n\n模块之间应该从宏观到微观递进：\n\n1. **模块 1-2**：全景层 — 系统做什么、有哪些大模块、核心价值\n2. **模块 3-4**：流程层 — 核心业务流程、用户旅程、数据流转\n3. **模块 5-6**：细节层 — 模块协作细节、边界情况、扩展方向\n\n### 实用性导向\n\n每个模块都应该回答产品经理关心的问题：\n- \"这个系统的核心业务逻辑是什么？\"\n- \"用户操作后系统内部发生了什么？\"\n- \"各模块之间是怎么配合的？\"\n- \"数据是怎么流转和存储的？\"\n- \"如果要加新功能，会影响哪些模块？\"\n- \"系统的边界在哪里？和哪些外部系统有交互？\"\n\n如果模块不能帮助产品经理理解业务逻辑或做出产品决策，重新设计它。"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。 Skill: codebase-to-course-cn Owner: zhouchang1988 Summary: 将任意代码库转换为精美的交互式课程。默认产品版面向产品经理，聚焦业务逻辑、流程和整体架构；开发版面向程序员，聚焦架构图、数据流动画和技术方案。触发词：'将此转换为课程'、'交互式讲解此代码库'、'技术架构课程'、'代码库入门指南'、'从代码库制作教程'、'教授这段代码'。 Tags: latest:1.2.0 Version history: v1.2.0 | 2026-05-21T09:15:07.115Z | user Release v1.2.0 from GitHub v1.1.4 | 2026-05-19T04:32:06.966Z | user Release v1.1.4 from GitHub v1.1.3 | 2026-05-15T02:27:17.173Z | user Release v1.","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":545,"uniquenessScore":56,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T13:00:04.874Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T13:00:04.874Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T16:01:20.264Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}