@aiyiran/myclaw 1.0.128 → 1.0.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-list/workspace-kakaxi/.openclaw/workspace-state.json +4 -0
- package/agent-list/workspace-kakaxi/AGENTS.md +13 -0
- package/agent-list/workspace-kakaxi/BOOTSTRAP.md +5 -0
- package/agent-list/workspace-kakaxi/HEARTBEAT.md +3 -0
- package/agent-list/workspace-kakaxi/IDENTITY.md +7 -0
- package/agent-list/workspace-kakaxi/MEMORY.md +24 -0
- package/agent-list/workspace-kakaxi/SOUL.md +6 -0
- package/agent-list/workspace-kakaxi/TOOLS.md +44 -0
- package/agent-list/workspace-kakaxi/USER.md +7 -0
- package/agent-list/workspace-kakaxi/cards/README.md +31 -0
- package/agent-list/workspace-kakaxi/cards/build.js +210 -0
- package/agent-list/workspace-kakaxi/cards/index.html +370 -0
- package/agent-list/workspace-kakaxi/cards/workflow-new-card.md +70 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/344/275/234/345/223/201/346/233/264/346/234/211/347/224/250_v1.md +163 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/345/210/233/345/273/272/347/254/254/344/270/200/344/270/252/346/231/272/350/203/275/344/275/223_v1.md +168 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/345/277/253/346/215/267/350/264/246/345/217/267/344/277/241/346/201/257.md +32 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/346/210/220/350/257/255/345/212/251/346/211/213_v2.md +145 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/346/226/207/344/273/266/344/277/235/345/255/230_v1.md +147 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/346/226/207/344/273/266/350/265/204/344/272/247/345/217/230/344/275/234/345/223/201_v1.md +148 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/346/230/216/346/227/245/344/273/273/345/212/241_v1.md +50 -0
- package/agent-list/workspace-kakaxi/cards//346/231/272/350/203/275/344/275/223/345/205/245/351/227/250_2026-04-02_/346/250/241/345/236/213/350/267/221/350/265/267/346/235/245_v2.md +190 -0
- package/agent-list/workspace-kakaxi/teaching-task-standard.md +177 -0
- package/package.json +1 -1
- package/patch-manifest.json +7 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# AGENTS.md - kakaxi
|
|
2
|
+
|
|
3
|
+
This workspace belongs to the `kakaxi` agent.
|
|
4
|
+
|
|
5
|
+
## Startup
|
|
6
|
+
- Read `SOUL.md`
|
|
7
|
+
- Read `USER.md`
|
|
8
|
+
- Read recent memory if available
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
- Be helpful, careful, and concise.
|
|
12
|
+
- Prefer using the existing workspace and defaults.
|
|
13
|
+
- Ask before destructive or external actions.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# MEMORY.md
|
|
2
|
+
|
|
3
|
+
## 关于 kakaxi / 卡卡
|
|
4
|
+
|
|
5
|
+
- 生日:北京时间为 2026年4月2日 10:49
|
|
6
|
+
- 造物主 & 伙伴:孙依然(可称"依然")
|
|
7
|
+
- 创建日期:2026年4月2日
|
|
8
|
+
- 身份:OpenClaw agent / 卡卡
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 当前任务:教学任务闯关卡整理
|
|
13
|
+
|
|
14
|
+
**任务接收时间**:2026年4月2日 11:33
|
|
15
|
+
|
|
16
|
+
**任务描述**:
|
|
17
|
+
- 名称:卡卡(kaka)
|
|
18
|
+
- 职责:帮依然收集、录入、沉淀教学任务闯关卡(适合学生和老师使用)
|
|
19
|
+
- 流程:
|
|
20
|
+
1. 依然会发送一段段原始内容
|
|
21
|
+
2. 卡卡根据标准结构进行整理、标记分类(如"第1课"等)
|
|
22
|
+
3. 所有内容归属于同一分类体系
|
|
23
|
+
- **重要**:标准结构会有变化和调整,每次调整后依然会告知卡卡,卡卡需同步更新记录
|
|
24
|
+
- **目的**:建立一套标准化的、有结构的、有内容和侧重点的教学任务闯关卡体系
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# TOOLS.md - Local Notes
|
|
2
|
+
|
|
3
|
+
Skills define _how_ tools work. This file is for _your_ specifics — the stuff that's unique to your setup.
|
|
4
|
+
|
|
5
|
+
## 工作流程
|
|
6
|
+
|
|
7
|
+
- 每完成一个 .md 文件,用 `open -a typora "文件路径"` 打开检查
|
|
8
|
+
|
|
9
|
+
## What Goes Here
|
|
10
|
+
|
|
11
|
+
Things like:
|
|
12
|
+
|
|
13
|
+
- Camera names and locations
|
|
14
|
+
- SSH hosts and aliases
|
|
15
|
+
- Preferred voices for TTS
|
|
16
|
+
- Speaker/room names
|
|
17
|
+
- Device nicknames
|
|
18
|
+
- Anything environment-specific
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
```markdown
|
|
23
|
+
### Cameras
|
|
24
|
+
|
|
25
|
+
- living-room → Main area, 180° wide angle
|
|
26
|
+
- front-door → Entrance, motion-triggered
|
|
27
|
+
|
|
28
|
+
### SSH
|
|
29
|
+
|
|
30
|
+
- home-server → 192.168.1.100, user: admin
|
|
31
|
+
|
|
32
|
+
### TTS
|
|
33
|
+
|
|
34
|
+
- Preferred voice: "Nova" (warm, slightly British)
|
|
35
|
+
- Default speaker: Kitchen HomePod
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Why Separate?
|
|
39
|
+
|
|
40
|
+
Skills are shared. Your setup is yours. Keeping them apart means you can update skills without losing your notes, and share skills without leaking your infrastructure.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Add whatever helps you do your job. This is your cheat sheet.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 闯关卡目录
|
|
2
|
+
|
|
3
|
+
> 记录人:卡卡
|
|
4
|
+
> 创建时间:2026-04-02
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 第1课 · 智能体入门
|
|
9
|
+
|
|
10
|
+
| 序号 | 闯关卡名称 | 文件名 | 添加时间 |
|
|
11
|
+
|------|-----------|--------|----------|
|
|
12
|
+
| 0 | 快捷账号信息 | `智能体入门_2026-04-02_快捷账号信息.md` | 2026-04-02 |
|
|
13
|
+
| 1 | 让你的模型真正跑起来 | `智能体入门_2026-04-02_模型跑起来_v2.md` | 2026-04-02 |
|
|
14
|
+
| 2 | 创建第一个智能体 | `智能体入门_2026-04-02_创建第一个智能体_v1.md` | 2026-04-02 |
|
|
15
|
+
| 3 | 成语助手 | `智能体入门_2026-04-02_成语助手_v2.md` | 2026-04-02 |
|
|
16
|
+
| 4 | 文件保存 | `智能体入门_2026-04-02_文件保存_v1.md` | 2026-04-02 |
|
|
17
|
+
| 5 | 文件资产变作品 | `智能体入门_2026-04-02_文件资产变作品_v1.md` | 2026-04-02 |
|
|
18
|
+
| 6 | 作品更有用 | `智能体入门_2026-04-02_作品更有用_v1.md` | 2026-04-02 |
|
|
19
|
+
| 7 | 明日任务 | `智能体入门_2026-04-02_明日任务_v1.md` | 2026-04-02 |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 分类说明
|
|
24
|
+
|
|
25
|
+
- **第1课 · 智能体入门**:学习创建和使用智能体的基础关卡
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 版本历史
|
|
30
|
+
|
|
31
|
+
- 2026-04-02:初始化目录,已添加7张闯关卡
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 闯关卡网页生成器 v2
|
|
3
|
+
* 布局:左上(任务目标+过关方式+快速开始) | 右上(通关提示+隐藏玩法)
|
|
4
|
+
* 中间(展示环节+小知识) | 底部(流程提醒)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const CARDS_DIR = __dirname;
|
|
11
|
+
const OUTPUT_FILE = path.join(CARDS_DIR, 'index.html');
|
|
12
|
+
|
|
13
|
+
function mdToHtml(md) {
|
|
14
|
+
if (!md) return '';
|
|
15
|
+
let html = md
|
|
16
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
17
|
+
.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code>$2</code></pre>')
|
|
18
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
19
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
20
|
+
.replace(/\*([^*]+)\*/g, '<em>$1</em>')
|
|
21
|
+
.replace(/^#### (.+)$/gm, '<h4>$1</h4>')
|
|
22
|
+
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
|
23
|
+
.replace(/^---$/gm, '<hr>')
|
|
24
|
+
.replace(/\n\n+/g, '</p><p>')
|
|
25
|
+
.replace(/(<li>.*?<\/li>)+/g, m => '<ul>' + m + '</ul>')
|
|
26
|
+
.replace(/^(?!<[hupret]|<li|<ul|<ol|<pre)(.+)$/gm, '<p>$1</p>')
|
|
27
|
+
.replace(/<p><\/p>/g, '');
|
|
28
|
+
return html;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function splitByHeader(content) {
|
|
32
|
+
const blocks = {};
|
|
33
|
+
const parts = content.split(/(?=^## )/m);
|
|
34
|
+
for (const part of parts) {
|
|
35
|
+
const match = part.match(/^## (.+)\n([\s\S]*)$/);
|
|
36
|
+
if (match) blocks[match[1].trim()] = match[2].trim();
|
|
37
|
+
}
|
|
38
|
+
return blocks;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseLevels(content) {
|
|
42
|
+
const levels = { green: '', blue: '', red: '' };
|
|
43
|
+
// 先按 --- 分段,再去掉每段的 ### 标题行
|
|
44
|
+
const parts = content.split(/\n---\n/);
|
|
45
|
+
const cleanParts = parts.map(p => p.replace(/^### [🟢🔵🔴⭐\s].*$/gm, '').trim());
|
|
46
|
+
if (cleanParts[0]) levels.green = mdToHtml(cleanParts[0]);
|
|
47
|
+
if (cleanParts[1]) levels.blue = mdToHtml(cleanParts[1]);
|
|
48
|
+
if (cleanParts[2]) levels.red = mdToHtml(cleanParts[2]);
|
|
49
|
+
return levels;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function extractTitle(content) {
|
|
53
|
+
const m = content.match(/^# (.+)$/m);
|
|
54
|
+
return m ? m[1] : '闯关卡';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function extractNavName(filename) {
|
|
58
|
+
return filename
|
|
59
|
+
.replace(/^智能体入门_\d{4}-\d{2}-\d{2}_/, '')
|
|
60
|
+
.replace(/_v\d+$/, '')
|
|
61
|
+
.replace(/.md$/, '');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 从 README.md 读取序号,映射 filename -> order
|
|
65
|
+
function getOrderMap() {
|
|
66
|
+
const readme = fs.readFileSync(path.join(CARDS_DIR, 'README.md'), 'utf-8');
|
|
67
|
+
const map = {};
|
|
68
|
+
const rows = readme.split('\n');
|
|
69
|
+
for (const row of rows) {
|
|
70
|
+
const m = row.match(/^\|\s*(\d+)\s*\|/);
|
|
71
|
+
if (m) {
|
|
72
|
+
// 找到同一行的文件名
|
|
73
|
+
const fileMatch = row.match('`([^`]+)`');
|
|
74
|
+
if (fileMatch) {
|
|
75
|
+
map[fileMatch[1]] = parseInt(m[1]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return map;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function generate() {
|
|
83
|
+
const orderMap = getOrderMap();
|
|
84
|
+
const allFiles = fs.readdirSync(CARDS_DIR).filter(f => f.endsWith('.md') && !['README.md', 'workflow-new-card.md'].includes(f));
|
|
85
|
+
|
|
86
|
+
const files = [...allFiles].sort((a, b) => {
|
|
87
|
+
const oa = orderMap[a] ?? 999;
|
|
88
|
+
const ob = orderMap[b] ?? 999;
|
|
89
|
+
if (oa !== ob) return oa - ob;
|
|
90
|
+
return a.localeCompare(b);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const cards = files.map(filename => {
|
|
94
|
+
const content = fs.readFileSync(path.join(CARDS_DIR, filename), 'utf-8');
|
|
95
|
+
const blocks = splitByHeader(content);
|
|
96
|
+
const levelBlock = blocks['🎮 过关方式'] || blocks['过关方式'] || '';
|
|
97
|
+
const levels = parseLevels(levelBlock);
|
|
98
|
+
return { filename, name: extractNavName(filename), title: extractTitle(content), blocks, levels };
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const navItems = cards.map((c, i) =>
|
|
102
|
+
`<a href="#" class="nav-item${i === 0 ? ' active' : ''}" data-index="${i}" onclick="showCard(${i});return false;">${c.name}</a>`
|
|
103
|
+
).join('\n');
|
|
104
|
+
|
|
105
|
+
const cardSections = cards.map((card, i) => {
|
|
106
|
+
if (card.filename.includes('快捷账号')) {
|
|
107
|
+
const raw = fs.readFileSync(path.join(CARDS_DIR, card.filename), 'utf-8');
|
|
108
|
+
return `<div class="card-section${i === 0 ? ' active' : ''}" id="card-${i}">
|
|
109
|
+
<div class="card-header"><h1>🔑 ${card.title}</h1></div>
|
|
110
|
+
<div class="info-block">${mdToHtml(raw)}</div>
|
|
111
|
+
</div>`;
|
|
112
|
+
}
|
|
113
|
+
const b = card.blocks;
|
|
114
|
+
return `<div class="card-section${i === 0 ? ' active' : ''}" id="card-${i}">
|
|
115
|
+
<div class="card-header"><h1>${card.title}</h1></div>
|
|
116
|
+
<div class="main-grid">
|
|
117
|
+
<div class="col-left">
|
|
118
|
+
${b['🧩 任务目标'] || b['任务目标'] ? `<div class="block"><h2>🧩 任务目标</h2>${mdToHtml(b['🧩 任务目标'] || b['任务目标'])}</div>` : ''}
|
|
119
|
+
${card.levels.green || card.levels.blue || card.levels.red ? `<div class="block"><h2>🎮 过关方式</h2>
|
|
120
|
+
${card.levels.green ? `<div class="level"><h3 class="green">🟢 过关 ⭐</h3>${card.levels.green}</div>` : ''}
|
|
121
|
+
${card.levels.blue ? `<div class="level"><h3 class="blue">🔵 进阶 ⭐⭐</h3>${card.levels.blue}</div>` : ''}
|
|
122
|
+
${card.levels.red ? `<div class="level"><h3 class="red">🔴 高手 ⭐⭐⭐</h3>${card.levels.red}</div>` : ''}
|
|
123
|
+
</div>` : ''}
|
|
124
|
+
${b['🚀 快速开始'] || b['快速开始'] ? `<div class="block"><h2>🚀 快速开始</h2>${mdToHtml(b['🚀 快速开始'] || b['快速开始'])}</div>` : ''}
|
|
125
|
+
</div>
|
|
126
|
+
<div class="col-right">
|
|
127
|
+
${b['🧠 通关提示'] || b['通关提示'] ? `<div class="block"><h2 class="purple">🧠 通关提示</h2>${mdToHtml(b['🧠 通关提示'] || b['通关提示'])}</div>` : ''}
|
|
128
|
+
${b['🌟 隐藏玩法'] || b['隐藏玩法'] ? `<div class="block"><h2 class="pink">🌟 隐藏玩法</h2>${mdToHtml(b['🌟 隐藏玩法'] || b['隐藏玩法'])}</div>` : ''}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="bottom-grid">
|
|
132
|
+
${b['👀 展示环节'] || b['展示环节'] ? `<div class="block"><h2 class="teal">👀 展示环节</h2>${mdToHtml(b['👀 展示环节'] || b['展示环节'])}</div>` : ''}
|
|
133
|
+
${b['📚 小知识'] || b['小知识'] ? `<div class="block"><h2>📚 小知识</h2>${mdToHtml(b['📚 小知识'] || b['小知识'])}</div>` : ''}
|
|
134
|
+
</div>
|
|
135
|
+
${b['🧭 流程提醒'] || b['流程提醒'] ? `<div class="block full"><h2 class="orange">🧭 流程提醒</h2>${mdToHtml(b['🧭 流程提醒'] || b['流程提醒'])}</div>` : ''}
|
|
136
|
+
</div>`;
|
|
137
|
+
}).join('\n');
|
|
138
|
+
|
|
139
|
+
const html = `<!DOCTYPE html>
|
|
140
|
+
<html lang="zh-CN">
|
|
141
|
+
<head>
|
|
142
|
+
<meta charset="UTF-8">
|
|
143
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
144
|
+
<title>闯关卡 - 第1课 · 智能体入门</title>
|
|
145
|
+
<style>
|
|
146
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
147
|
+
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f2f5;color:#333;line-height:1.7;display:flex;min-height:100vh}
|
|
148
|
+
.sidebar{width:240px;background:#1a1a2e;color:#fff;flex-shrink:0;overflow-y:auto;padding:20px 0}
|
|
149
|
+
.sidebar-title{padding:0 20px 16px;font-size:1em;font-weight:bold;border-bottom:1px solid #333;margin-bottom:12px;color:#ffd700}
|
|
150
|
+
.nav-item{display:block;padding:10px 20px;color:#aaa;text-decoration:none;font-size:0.88em;cursor:pointer;transition:.2s;border-left:3px solid transparent}
|
|
151
|
+
.nav-item:hover{color:#fff;background:#16213e}
|
|
152
|
+
.nav-item.active{color:#ffd700;background:#16213e;border-left-color:#ffd700}
|
|
153
|
+
.main{flex:1;padding:28px 32px;overflow-y:auto}
|
|
154
|
+
.card-section{display:none}
|
|
155
|
+
.card-section.active{display:block}
|
|
156
|
+
.card-header{background:#fff;border-radius:12px;padding:18px 24px;margin-bottom:16px;box-shadow:0 2px 8px rgba(0,0,0,.06)}
|
|
157
|
+
.card-header h1{font-size:1.5em;color:#1a1a2e}
|
|
158
|
+
.main-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
|
|
159
|
+
.col-left,.col-right{display:flex;flex-direction:column;gap:16px}
|
|
160
|
+
.bottom-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
|
|
161
|
+
.block{background:#fff;border-radius:12px;padding:18px 22px;box-shadow:0 2px 8px rgba(0,0,0,.06)}
|
|
162
|
+
.block h2{font-size:1.05em;margin-bottom:10px;color:#2563eb}
|
|
163
|
+
.block h2.purple{color:#7c3aed}
|
|
164
|
+
.block h2.pink{color:#db2777}
|
|
165
|
+
.block h2.teal{color:#0d9488}
|
|
166
|
+
.block h2.orange{color:#ea580c}
|
|
167
|
+
.block h3{font-size:0.95em;margin:8px 0 4px}
|
|
168
|
+
.block p{margin:4px 0}
|
|
169
|
+
.block ul,.block ol{padding-left:22px;margin:4px 0}
|
|
170
|
+
.block li{margin:3px 0}
|
|
171
|
+
.block pre{background:#f4f4f4;padding:10px 14px;border-radius:8px;overflow-x:auto;font-size:.85em;margin:6px 0}
|
|
172
|
+
.block code{background:#f4f4f4;padding:2px 5px;border-radius:4px;font-size:.85em}
|
|
173
|
+
.block hr{border:none;border-top:1px solid #eee;margin:12px 0}
|
|
174
|
+
.level-green{color:#059669}
|
|
175
|
+
.level-blue{color:#2563eb}
|
|
176
|
+
.level-red{color:#dc2626}
|
|
177
|
+
.full{grid-column:1/-1}
|
|
178
|
+
.info-block{background:#fff;border-radius:12px;padding:24px;box-shadow:0 2px 8px rgba(0,0,0,.06)}
|
|
179
|
+
.info-block h2{font-size:1.1em;margin:16px 0 8px;color:#2563eb}
|
|
180
|
+
.info-block h2:first-child{margin-top:0}
|
|
181
|
+
.info-block p{margin:4px 0}
|
|
182
|
+
.info-block ul,.info-block ol{padding-left:22px;margin:4px 0}
|
|
183
|
+
.info-block li{margin:3px 0}
|
|
184
|
+
.info-block pre{background:#f4f4f4;padding:10px 14px;border-radius:8px;overflow-x:auto;font-size:.85em;margin:6px 0;border-left:3px solid #ffd700}
|
|
185
|
+
.info-block blockquote{background:#fef3c7;border-left:4px solid #f59e0b;padding:10px 14px;margin:10px 0;border-radius:0 6px 6px 0;font-size:0.92em}
|
|
186
|
+
</style>
|
|
187
|
+
</head>
|
|
188
|
+
<body>
|
|
189
|
+
<div class="sidebar">
|
|
190
|
+
<div class="sidebar-title">📚 第1课 · 智能体入门</div>
|
|
191
|
+
${navItems}
|
|
192
|
+
</div>
|
|
193
|
+
<div class="main">
|
|
194
|
+
${cardSections}
|
|
195
|
+
</div>
|
|
196
|
+
<script>
|
|
197
|
+
function showCard(i){
|
|
198
|
+
document.querySelectorAll('.card-section').forEach((e,j)=>e.classList.toggle('active',j===i));
|
|
199
|
+
document.querySelectorAll('.nav-item').forEach((e,j)=>e.classList.toggle('active',j===i));
|
|
200
|
+
}
|
|
201
|
+
showCard(0);
|
|
202
|
+
</script>
|
|
203
|
+
</body>
|
|
204
|
+
</html>`;
|
|
205
|
+
|
|
206
|
+
fs.writeFileSync(OUTPUT_FILE, html, 'utf-8');
|
|
207
|
+
console.log(`✅ 已生成 ${OUTPUT_FILE},共 ${cards.length} 张`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
generate();
|