@aiyiran/myclaw 1.1.64 → 1.1.66
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/assets/myclaw-artifacts.js +157 -62
- package/package.json +1 -1
- package/server/sync_workspace.py +27 -4
- package/skills/yiran-course-template-pipeline/SKILL.md +6 -0
- package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2652-student/347/224/237/346/210/220.md +1 -1
- package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2654-/346/211/223/345/214/205/350/220/275/347/233/230.md +18 -0
- package/skills/yiran-course-template-pipeline/references/student-example.json +2 -2
- package/skills/yiran-course-template-pipeline/references/student-fields.md +1 -1
- package/skills/yiran-course-template-pipeline/scripts/identify_template.py +0 -2
- package/skills/yiran-course-template-pipeline/scripts/move_template_task.py +15 -2
- package/skills/yiran-course-template-pipeline/scripts/release_template.py +84 -0
- package/skills/yiran-course-template-pipeline/template-index.json +57 -29
- package/skills/yiran-course-template-pipeline/template-index.md +30 -21
- package/skills/yiran-playground-template-use/scripts/deploy_to_workspace.py +4 -11
- package//347/264/257/345/220/220/350/241/200/345/220/216/347/232/204/346/200/273/347/273/223.md +196 -0
|
@@ -7,7 +7,6 @@ identify_template.py
|
|
|
7
7
|
|
|
8
8
|
写入字段:
|
|
9
9
|
_id : 8位十六进制,首次颁发,后续不变
|
|
10
|
-
_version : 首次=1,更新=已有版本+1
|
|
11
10
|
任务类别 : 校准后的系列(A/B/C)
|
|
12
11
|
任务编号 : 校准后的3位编号(100起)
|
|
13
12
|
|
|
@@ -163,7 +162,6 @@ def main():
|
|
|
163
162
|
|
|
164
163
|
# ── 3. 写回 __teacher__.json ───────────────────────────────────────────
|
|
165
164
|
teacher['_id'] = tid
|
|
166
|
-
teacher['_version'] = new_version
|
|
167
165
|
teacher['任务类别'] = category
|
|
168
166
|
teacher['任务编号'] = f'{calibrated:03d}'
|
|
169
167
|
save_json(teacher_path, teacher)
|
|
@@ -42,6 +42,19 @@ def sanitize_name(name: str) -> str:
|
|
|
42
42
|
return re.sub(r'[/\\ ]', '_', str(name).strip())
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def get_latest_version_num(folder: Path) -> int:
|
|
46
|
+
"""返回 folder 下最大的版本号,没有则返回 0"""
|
|
47
|
+
if not folder.exists():
|
|
48
|
+
return 0
|
|
49
|
+
max_v = 0
|
|
50
|
+
for d in folder.iterdir():
|
|
51
|
+
if d.is_dir():
|
|
52
|
+
m = re.match(r'^v(\d+)$', d.name)
|
|
53
|
+
if m:
|
|
54
|
+
max_v = max(max_v, int(m.group(1)))
|
|
55
|
+
return max_v
|
|
56
|
+
|
|
57
|
+
|
|
45
58
|
def main():
|
|
46
59
|
if len(sys.argv) < 2:
|
|
47
60
|
print('用法: python3 move_template_task.py <模板目录> [目标根目录]')
|
|
@@ -69,10 +82,10 @@ def main():
|
|
|
69
82
|
category = teacher.get('任务类别', '').upper()
|
|
70
83
|
number = teacher.get('任务编号', '100')
|
|
71
84
|
name = sanitize_name(teacher.get('任务名称', 'untitled'))
|
|
72
|
-
version = teacher.get('_version', 1)
|
|
73
85
|
|
|
74
86
|
folder_name = f'{category.lower()}{number}_{name}'
|
|
75
|
-
|
|
87
|
+
next_version = get_latest_version_num(target_root / folder_name) + 1
|
|
88
|
+
version_str = f'v{next_version}'
|
|
76
89
|
target_dir = target_root / folder_name / version_str
|
|
77
90
|
|
|
78
91
|
if target_dir.exists():
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
release_template.py
|
|
4
|
+
|
|
5
|
+
一键发布脚本:合并 build → move → publish 三个步骤。
|
|
6
|
+
|
|
7
|
+
传入原始模板目录(或已有 vN/ 目录),自动执行:
|
|
8
|
+
Step 1: build_template_views.py <source_dir> → 生成/更新 HTML 视图
|
|
9
|
+
Step 2: move_template_task.py <source_dir> → 创建新版本目录 vN+1/
|
|
10
|
+
Step 3: publish_template.py <templates/xxx/> → 上传 CDN + 上传 index
|
|
11
|
+
|
|
12
|
+
用法:
|
|
13
|
+
python3 scripts/release_template.py <模板目录>
|
|
14
|
+
"""
|
|
15
|
+
import json
|
|
16
|
+
import re
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
22
|
+
BUILD_SCRIPT = ROOT / 'scripts' / 'build_template_views.py'
|
|
23
|
+
MOVE_SCRIPT = ROOT / 'scripts' / 'move_template_task.py'
|
|
24
|
+
PUBLISH_SCRIPT = ROOT / 'scripts' / 'publish_template.py'
|
|
25
|
+
TEMPLATES_DIR = ROOT / 'templates'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_json(path: Path) -> dict:
|
|
29
|
+
try:
|
|
30
|
+
return json.loads(path.read_text(encoding='utf-8'))
|
|
31
|
+
except Exception:
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def sanitize_name(name: str) -> str:
|
|
36
|
+
return re.sub(r'[/\\ ]', '_', str(name).strip())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main():
|
|
40
|
+
if len(sys.argv) < 2:
|
|
41
|
+
print('用法: python3 scripts/release_template.py <模板目录>')
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
|
|
44
|
+
source_dir = Path(sys.argv[1]).expanduser().resolve()
|
|
45
|
+
|
|
46
|
+
if not source_dir.is_dir():
|
|
47
|
+
print(f'❌ 目录不存在: {source_dir}')
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
teacher_path = source_dir / '__teacher__.json'
|
|
51
|
+
if not teacher_path.exists():
|
|
52
|
+
print(f'❌ 找不到 __teacher__.json: {teacher_path}')
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
# ── Step 1: Build HTML views ──────────────────────────────────────────
|
|
56
|
+
print('\n══ Step 1: 生成 HTML 视图 ══════════════════════════════')
|
|
57
|
+
subprocess.run([sys.executable, str(BUILD_SCRIPT), str(source_dir)], check=True)
|
|
58
|
+
|
|
59
|
+
# ── Step 2: Move to templates/ ────────────────────────────────────────
|
|
60
|
+
print('\n══ Step 2: 移入 templates/ ═════════════════════════════')
|
|
61
|
+
subprocess.run([sys.executable, str(MOVE_SCRIPT), str(source_dir)], check=True)
|
|
62
|
+
|
|
63
|
+
# ── Determine publish target from updated teacher.json ────────────────
|
|
64
|
+
teacher = load_json(teacher_path)
|
|
65
|
+
category = teacher.get('任务类别', '').upper()
|
|
66
|
+
number = teacher.get('任务编号', '100')
|
|
67
|
+
name = sanitize_name(teacher.get('任务名称', 'untitled'))
|
|
68
|
+
folder_name = f'{category.lower()}{number}_{name}'
|
|
69
|
+
publish_target = TEMPLATES_DIR / folder_name
|
|
70
|
+
|
|
71
|
+
if not publish_target.is_dir():
|
|
72
|
+
print(f'❌ 发布目标目录不存在: {publish_target}')
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
|
|
75
|
+
# ── Step 3: Publish to CDN ────────────────────────────────────────────
|
|
76
|
+
print('\n══ Step 3: 上传 CDN ════════════════════════════════════')
|
|
77
|
+
subprocess.run([sys.executable, str(PUBLISH_SCRIPT), str(publish_target)], check=True)
|
|
78
|
+
|
|
79
|
+
print('\n✅ 发布完成!')
|
|
80
|
+
print(f' 模板目录: {publish_target}')
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == '__main__':
|
|
84
|
+
main()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"index_updated_at": "2026-04-
|
|
2
|
+
"index_updated_at": "2026-04-22T17:22:47Z",
|
|
3
3
|
"templates": {
|
|
4
4
|
"696de592": {
|
|
5
5
|
"id": "696de592",
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"编号": "100",
|
|
8
8
|
"名称": "给依然老师设计一个AI画像",
|
|
9
9
|
"文件夹名": "a100_给依然老师设计一个AI画像",
|
|
10
|
-
"version": "
|
|
11
|
-
"路径": "templates/a100_给依然老师设计一个AI画像/
|
|
12
|
-
"入口文件": "templates/a100_给依然老师设计一个AI画像/
|
|
10
|
+
"version": "v5",
|
|
11
|
+
"路径": "templates/a100_给依然老师设计一个AI画像/v5",
|
|
12
|
+
"入口文件": "templates/a100_给依然老师设计一个AI画像/v5/index.html",
|
|
13
13
|
"一句话说明": "先让 AI 生成一个老师形象,再用网页把它展示出来,重点练习描述和小修改。",
|
|
14
14
|
"主能力标签": "表达构建",
|
|
15
15
|
"任务类型标签": "视听表达",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"表达构建",
|
|
27
27
|
"视听表达"
|
|
28
28
|
],
|
|
29
|
-
"updated_at": "2026-04-
|
|
29
|
+
"updated_at": "2026-04-22T17:22:41Z"
|
|
30
30
|
},
|
|
31
31
|
"d0ff11ee": {
|
|
32
32
|
"id": "d0ff11ee",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"编号": "101",
|
|
35
35
|
"名称": "做3张同主题图片",
|
|
36
36
|
"文件夹名": "a101_做3张同主题图片",
|
|
37
|
-
"version": "
|
|
38
|
-
"路径": "templates/a101_做3张同主题图片/
|
|
39
|
-
"入口文件": "templates/a101_做3张同主题图片/
|
|
37
|
+
"version": "v2",
|
|
38
|
+
"路径": "templates/a101_做3张同主题图片/v2",
|
|
39
|
+
"入口文件": "templates/a101_做3张同主题图片/v2/index.html",
|
|
40
40
|
"一句话说明": "先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。",
|
|
41
41
|
"主能力标签": "表达构建",
|
|
42
42
|
"任务类型标签": "视听表达",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"表达构建",
|
|
53
53
|
"视听表达"
|
|
54
54
|
],
|
|
55
|
-
"updated_at": "2026-04-
|
|
55
|
+
"updated_at": "2026-04-22T17:15:44Z"
|
|
56
56
|
},
|
|
57
57
|
"d48d94b6": {
|
|
58
58
|
"id": "d48d94b6",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"编号": "102",
|
|
61
61
|
"名称": "图片资源展示",
|
|
62
62
|
"文件夹名": "a102_图片资源展示",
|
|
63
|
-
"version": "
|
|
64
|
-
"路径": "templates/a102_图片资源展示/
|
|
65
|
-
"入口文件": "templates/a102_图片资源展示/
|
|
63
|
+
"version": "v2",
|
|
64
|
+
"路径": "templates/a102_图片资源展示/v2",
|
|
65
|
+
"入口文件": "templates/a102_图片资源展示/v2/index.html",
|
|
66
66
|
"一句话说明": "把图片放到网页上,做成一个方便点击查看的小展示页。",
|
|
67
67
|
"主能力标签": "第二能力:表达构建",
|
|
68
68
|
"任务类型标签": "视听表达",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"第二能力:表达构建",
|
|
79
79
|
"视听表达"
|
|
80
80
|
],
|
|
81
|
-
"updated_at": "2026-04-
|
|
81
|
+
"updated_at": "2026-04-22T17:15:47Z"
|
|
82
82
|
},
|
|
83
83
|
"e1aab9bf": {
|
|
84
84
|
"id": "e1aab9bf",
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
"编号": "103",
|
|
87
87
|
"名称": "做一个介绍页面",
|
|
88
88
|
"文件夹名": "a103_做一个介绍页面",
|
|
89
|
-
"version": "
|
|
90
|
-
"路径": "templates/a103_做一个介绍页面/
|
|
91
|
-
"入口文件": "templates/a103_做一个介绍页面/
|
|
89
|
+
"version": "v2",
|
|
90
|
+
"路径": "templates/a103_做一个介绍页面/v2",
|
|
91
|
+
"入口文件": "templates/a103_做一个介绍页面/v2/index.html",
|
|
92
92
|
"一句话说明": "用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。",
|
|
93
93
|
"主能力标签": "表达构建",
|
|
94
94
|
"任务类型标签": "视听表达",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"表达构建",
|
|
105
105
|
"视听表达"
|
|
106
106
|
],
|
|
107
|
-
"updated_at": "2026-04-
|
|
107
|
+
"updated_at": "2026-04-22T17:15:55Z"
|
|
108
108
|
},
|
|
109
109
|
"3c61e07d": {
|
|
110
110
|
"id": "3c61e07d",
|
|
@@ -112,9 +112,9 @@
|
|
|
112
112
|
"编号": "104",
|
|
113
113
|
"名称": "做一个会出招的神奇按钮",
|
|
114
114
|
"文件夹名": "a104_做一个会出招的神奇按钮",
|
|
115
|
-
"version": "
|
|
116
|
-
"路径": "templates/a104_做一个会出招的神奇按钮/
|
|
117
|
-
"入口文件": "templates/a104_做一个会出招的神奇按钮/
|
|
115
|
+
"version": "v3",
|
|
116
|
+
"路径": "templates/a104_做一个会出招的神奇按钮/v3",
|
|
117
|
+
"入口文件": "templates/a104_做一个会出招的神奇按钮/v3/index.html",
|
|
118
118
|
"一句话说明": "先试试按钮效果,再改一点,最后加一个你自己的新按钮。",
|
|
119
119
|
"主能力标签": "迭代推进",
|
|
120
120
|
"任务类型标签": "游戏机制",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"迭代推进",
|
|
133
133
|
"游戏机制"
|
|
134
134
|
],
|
|
135
|
-
"updated_at": "2026-04-
|
|
135
|
+
"updated_at": "2026-04-22T17:22:44Z"
|
|
136
136
|
},
|
|
137
137
|
"c360e192": {
|
|
138
138
|
"id": "c360e192",
|
|
@@ -140,9 +140,9 @@
|
|
|
140
140
|
"编号": "100",
|
|
141
141
|
"名称": "做一个按钮页面",
|
|
142
142
|
"文件夹名": "b100_做一个按钮页面",
|
|
143
|
-
"version": "
|
|
144
|
-
"路径": "templates/b100_做一个按钮页面/
|
|
145
|
-
"入口文件": "templates/b100_做一个按钮页面/
|
|
143
|
+
"version": "v2",
|
|
144
|
+
"路径": "templates/b100_做一个按钮页面/v2",
|
|
145
|
+
"入口文件": "templates/b100_做一个按钮页面/v2/index.html",
|
|
146
146
|
"一句话说明": "亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。",
|
|
147
147
|
"主能力标签": "表达构建",
|
|
148
148
|
"任务类型标签": "游戏机制",
|
|
@@ -158,7 +158,35 @@
|
|
|
158
158
|
"表达构建",
|
|
159
159
|
"游戏机制"
|
|
160
160
|
],
|
|
161
|
-
"updated_at": "2026-04-
|
|
161
|
+
"updated_at": "2026-04-22T17:16:00Z"
|
|
162
|
+
},
|
|
163
|
+
"8a7e39dd": {
|
|
164
|
+
"id": "8a7e39dd",
|
|
165
|
+
"系列": "B",
|
|
166
|
+
"编号": "101",
|
|
167
|
+
"名称": "会跳舞的机器人",
|
|
168
|
+
"文件夹名": "b101_会跳舞的机器人",
|
|
169
|
+
"version": "v2",
|
|
170
|
+
"路径": "templates/b101_会跳舞的机器人/v2",
|
|
171
|
+
"入口文件": "templates/b101_会跳舞的机器人/v2/index.html",
|
|
172
|
+
"一句话说明": "先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。",
|
|
173
|
+
"主能力标签": "表达构建",
|
|
174
|
+
"任务类型标签": "视听表达",
|
|
175
|
+
"关键词": [
|
|
176
|
+
"b101_会跳舞的机器人",
|
|
177
|
+
"先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。",
|
|
178
|
+
"已经能开始动手,但还不太会把多种素材组织到一起的学生",
|
|
179
|
+
"会做简单网页,但还不太会处理视频、音乐和动画配合的学生",
|
|
180
|
+
"想从静态页面进入多媒体网页表达的学生",
|
|
181
|
+
"把机器人视频放进网页中",
|
|
182
|
+
"让视频和音乐形成基本配合",
|
|
183
|
+
"尝试处理循环播放和播放节奏",
|
|
184
|
+
"让网页从静态展示变成更有动态感的作品",
|
|
185
|
+
"会跳舞的机器人",
|
|
186
|
+
"表达构建",
|
|
187
|
+
"视听表达"
|
|
188
|
+
],
|
|
189
|
+
"updated_at": "2026-04-22T16:52:16Z"
|
|
162
190
|
},
|
|
163
191
|
"6490af0a": {
|
|
164
192
|
"id": "6490af0a",
|
|
@@ -166,9 +194,9 @@
|
|
|
166
194
|
"编号": "100",
|
|
167
195
|
"名称": "给小组作品做一次小修改",
|
|
168
196
|
"文件夹名": "c100_给小组作品做一次小修改",
|
|
169
|
-
"version": "
|
|
170
|
-
"路径": "templates/c100_给小组作品做一次小修改/
|
|
171
|
-
"入口文件": "templates/c100_给小组作品做一次小修改/
|
|
197
|
+
"version": "v2",
|
|
198
|
+
"路径": "templates/c100_给小组作品做一次小修改/v2",
|
|
199
|
+
"入口文件": "templates/c100_给小组作品做一次小修改/v2/index.html",
|
|
172
200
|
"一句话说明": "先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。",
|
|
173
201
|
"主能力标签": "问题判断",
|
|
174
202
|
"任务类型标签": "改编优化",
|
|
@@ -186,7 +214,7 @@
|
|
|
186
214
|
"问题判断",
|
|
187
215
|
"改编优化"
|
|
188
216
|
],
|
|
189
|
-
"updated_at": "2026-04-
|
|
217
|
+
"updated_at": "2026-04-22T17:16:03Z"
|
|
190
218
|
}
|
|
191
219
|
}
|
|
192
220
|
}
|
|
@@ -4,63 +4,72 @@
|
|
|
4
4
|
|
|
5
5
|
## A100 给依然老师设计一个AI画像
|
|
6
6
|
- ID:696de592
|
|
7
|
-
- 版本:
|
|
8
|
-
- 路径:templates/a100_给依然老师设计一个AI画像/
|
|
9
|
-
- 入口文件:templates/a100_给依然老师设计一个AI画像/
|
|
7
|
+
- 版本:v5
|
|
8
|
+
- 路径:templates/a100_给依然老师设计一个AI画像/v5
|
|
9
|
+
- 入口文件:templates/a100_给依然老师设计一个AI画像/v5/index.html
|
|
10
10
|
- 主能力标签:表达构建
|
|
11
11
|
- 任务类型标签:视听表达
|
|
12
12
|
- 关键词:a100_给依然老师设计一个AI画像, 先让 AI 生成一个老师形象,再用网页把它展示出来,重点练习描述和小修改。, 初次接触, 需要明确目标, 持续迭代, 围绕明确对象生成形象, 把人物图片放进网页中完成展示, 通过小幅修改让作品逐步变好, 给依然老师设计一个AI画像, 表达构建, 视听表达
|
|
13
13
|
|
|
14
14
|
## A101 做3张同主题图片
|
|
15
15
|
- ID:d0ff11ee
|
|
16
|
-
- 版本:
|
|
17
|
-
- 路径:templates/a101_做3张同主题图片/
|
|
18
|
-
- 入口文件:templates/a101_做3张同主题图片/
|
|
16
|
+
- 版本:v2
|
|
17
|
+
- 路径:templates/a101_做3张同主题图片/v2
|
|
18
|
+
- 入口文件:templates/a101_做3张同主题图片/v2/index.html
|
|
19
19
|
- 主能力标签:表达构建
|
|
20
20
|
- 任务类型标签:视听表达
|
|
21
21
|
- 关键词:a101_做3张同主题图片, 先确定一个有趣的主题,再用 AI 生成 3 张风格统一、但情节不同的系列图片。, 初次接触, 需要明确目标, 练习通过提示词锁定画面风格, 理解作品一致性的价值, 初步接触提示词的模块化修改, 做3张同主题图片, 表达构建, 视听表达
|
|
22
22
|
|
|
23
23
|
## A102 图片资源展示
|
|
24
24
|
- ID:d48d94b6
|
|
25
|
-
- 版本:
|
|
26
|
-
- 路径:templates/a102_图片资源展示/
|
|
27
|
-
- 入口文件:templates/a102_图片资源展示/
|
|
25
|
+
- 版本:v2
|
|
26
|
+
- 路径:templates/a102_图片资源展示/v2
|
|
27
|
+
- 入口文件:templates/a102_图片资源展示/v2/index.html
|
|
28
28
|
- 主能力标签:第二能力:表达构建
|
|
29
29
|
- 任务类型标签:视听表达
|
|
30
30
|
- 关键词:a102_图片资源展示, 把图片放到网页上,做成一个方便点击查看的小展示页。, 刚开始接触网页展示的学生, 需要先从添加素材开始动手的学生, 把本地图片放进网页里, 理解图片展示卡片的基本结构, 为后续继续扩充图片资源做准备, 图片资源展示, 第二能力:表达构建, 视听表达
|
|
31
31
|
|
|
32
32
|
## A103 做一个介绍页面
|
|
33
33
|
- ID:e1aab9bf
|
|
34
|
-
- 版本:
|
|
35
|
-
- 路径:templates/a103_做一个介绍页面/
|
|
36
|
-
- 入口文件:templates/a103_做一个介绍页面/
|
|
34
|
+
- 版本:v2
|
|
35
|
+
- 路径:templates/a103_做一个介绍页面/v2
|
|
36
|
+
- 入口文件:templates/a103_做一个介绍页面/v2/index.html
|
|
37
37
|
- 主能力标签:表达构建
|
|
38
38
|
- 任务类型标签:视听表达
|
|
39
39
|
- 关键词:a103_做一个介绍页面, 用一张图片加一段文字,做一个属于自己的介绍页面,练一练图文组合的技巧。, 初次接触网页制作, 有一点基础想练排版, 图片与文字的组合方式, 网页结构的基本层次感, 排版的视觉优先级, 做一个介绍页面, 表达构建, 视听表达
|
|
40
40
|
|
|
41
41
|
## A104 做一个会出招的神奇按钮
|
|
42
42
|
- ID:3c61e07d
|
|
43
|
-
- 版本:
|
|
44
|
-
- 路径:templates/a104_做一个会出招的神奇按钮/
|
|
45
|
-
- 入口文件:templates/a104_做一个会出招的神奇按钮/
|
|
43
|
+
- 版本:v3
|
|
44
|
+
- 路径:templates/a104_做一个会出招的神奇按钮/v3
|
|
45
|
+
- 入口文件:templates/a104_做一个会出招的神奇按钮/v3/index.html
|
|
46
46
|
- 主能力标签:迭代推进
|
|
47
47
|
- 任务类型标签:游戏机制
|
|
48
48
|
- 关键词:a104_做一个会出招的神奇按钮, 先试试按钮效果,再改一点,最后加一个你自己的新按钮。, 想先动手再思考的学生, 刚开始学网页互动的学生, 容易卡在空白页不知从哪开始的学生, 先运行现成示例,快速进入任务, 从已有功能里挑一个做小改动, 理解一个按钮对应一个功能, 把新增功能接入现有页面结构, 做一个会出招的神奇按钮, 迭代推进, 游戏机制
|
|
49
49
|
|
|
50
50
|
## B100 做一个按钮页面
|
|
51
51
|
- ID:c360e192
|
|
52
|
-
- 版本:
|
|
53
|
-
- 路径:templates/b100_做一个按钮页面/
|
|
54
|
-
- 入口文件:templates/b100_做一个按钮页面/
|
|
52
|
+
- 版本:v2
|
|
53
|
+
- 路径:templates/b100_做一个按钮页面/v2
|
|
54
|
+
- 入口文件:templates/b100_做一个按钮页面/v2/index.html
|
|
55
55
|
- 主能力标签:表达构建
|
|
56
56
|
- 任务类型标签:游戏机制
|
|
57
57
|
- 关键词:b100_做一个按钮页面, 亲手做一个会“变魔术”的按钮,学习网页是如何接收指令并产生变化的。, 喜欢做项目, 需要明确目标, 掌握基础的 JavaScript 点击事件绑定, 理解 HTML/CSS/JS 三者如何协作完成交互, 学习利用 CSS Class 切换来实现视觉变化, 做一个按钮页面, 表达构建, 游戏机制
|
|
58
58
|
|
|
59
|
+
## B101 会跳舞的机器人
|
|
60
|
+
- ID:8a7e39dd
|
|
61
|
+
- 版本:v2
|
|
62
|
+
- 路径:templates/b101_会跳舞的机器人/v2
|
|
63
|
+
- 入口文件:templates/b101_会跳舞的机器人/v2/index.html
|
|
64
|
+
- 主能力标签:表达构建
|
|
65
|
+
- 任务类型标签:视听表达
|
|
66
|
+
- 关键词:b101_会跳舞的机器人, 先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。, 已经能开始动手,但还不太会把多种素材组织到一起的学生, 会做简单网页,但还不太会处理视频、音乐和动画配合的学生, 想从静态页面进入多媒体网页表达的学生, 把机器人视频放进网页中, 让视频和音乐形成基本配合, 尝试处理循环播放和播放节奏, 让网页从静态展示变成更有动态感的作品, 会跳舞的机器人, 表达构建, 视听表达
|
|
67
|
+
|
|
59
68
|
## C100 给小组作品做一次小修改
|
|
60
69
|
- ID:6490af0a
|
|
61
|
-
- 版本:
|
|
62
|
-
- 路径:templates/c100_给小组作品做一次小修改/
|
|
63
|
-
- 入口文件:templates/c100_给小组作品做一次小修改/
|
|
70
|
+
- 版本:v2
|
|
71
|
+
- 路径:templates/c100_给小组作品做一次小修改/v2
|
|
72
|
+
- 入口文件:templates/c100_给小组作品做一次小修改/v2/index.html
|
|
64
73
|
- 主能力标签:问题判断
|
|
65
74
|
- 任务类型标签:改编优化
|
|
66
75
|
- 关键词:c100_给小组作品做一次小修改, 先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。, 已经能看懂图片内容,但不太会说哪里有问题的学生, 会说喜欢或不喜欢,但不太会提出具体修改点的学生, 已经能做第一版作品,想练习小步修改的学生, 先发现一个具体问题, 把大问题收成一个最小修改点, 用简单的话把修改要求说清楚, 比较修改前后有没有更接近自己的想法, 给小组作品做一次小修改, 问题判断, 改编优化
|
|
@@ -144,17 +144,10 @@ def choose_entry_file(target_dir: Path):
|
|
|
144
144
|
|
|
145
145
|
def build_instance_name(source_dir: Path, remix_dir: Path):
|
|
146
146
|
import re
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
else:
|
|
152
|
-
folder_name = source_dir.name
|
|
153
|
-
version_str = ''
|
|
154
|
-
now = datetime.now()
|
|
155
|
-
base = f"{now.strftime('%m%d')}_{folder_name}_{now.strftime('%H%M')}"
|
|
156
|
-
if version_str:
|
|
157
|
-
base = f"{base}_{version_str}"
|
|
147
|
+
folder_name = source_dir.parent.name if re.match(r'^v\d+$', source_dir.name) else source_dir.name
|
|
148
|
+
# 去掉 a104_ 这样的系列编号前缀,只保留业务名
|
|
149
|
+
name = re.sub(r'^[a-z]+\d+_', '', folder_name) or folder_name
|
|
150
|
+
base = f"{name}_{datetime.now().strftime('%H%M')}"
|
|
158
151
|
candidate = base
|
|
159
152
|
suffix = 2
|
|
160
153
|
while (remix_dir / candidate).exists():
|
package//347/264/257/345/220/220/350/241/200/345/220/216/347/232/204/346/200/273/347/273/223.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# 模板系统设计经验总结
|
|
2
|
+
|
|
3
|
+
## 一、版本化目录结构
|
|
4
|
+
|
|
5
|
+
### 问题
|
|
6
|
+
CDN 有缓存,更新模板后旧内容仍被访问。
|
|
7
|
+
|
|
8
|
+
### 方案
|
|
9
|
+
```
|
|
10
|
+
templates/
|
|
11
|
+
a104_做一个会出招的神奇按钮/
|
|
12
|
+
v1/ ← 每次更新递增版本号,CDN 路径变了,缓存自然失效
|
|
13
|
+
v2/
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 经验
|
|
17
|
+
- **本地只保留一个版本**:下载新版前先删旧版目录,避免就绪状态歧义
|
|
18
|
+
- **目录存在即就绪**:不需要额外标记文件(`.sync-done`),目录本身就是状态
|
|
19
|
+
- **原子性替换**:下载到 `.tmp/`,完成后 `rename .tmp/ → v1/`,不会出现"半完成"状态
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 二、稳定 ID 设计
|
|
24
|
+
|
|
25
|
+
### 问题
|
|
26
|
+
模板可能改编号(A100 → A105)、改名,如果用路径/编号做唯一标识会丢失对应关系。
|
|
27
|
+
|
|
28
|
+
### 方案
|
|
29
|
+
```json
|
|
30
|
+
// __teacher__.json
|
|
31
|
+
{
|
|
32
|
+
"_id": "696de592", // 8位hex,颁发一次永不变
|
|
33
|
+
"_version": 1 // 每次更新递增
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 经验
|
|
38
|
+
- ID 在 `move` 阶段颁发,之后不再改变
|
|
39
|
+
- CDN index 以 `_id` 为 key(平铺字典),而不是 `{系列: {编号: record}}` 嵌套结构
|
|
40
|
+
- 本地/CDN 比对:用 `_id` 找记录,再比较 `version` 字段判断是否需要更新
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 三、非阻塞同步 + 轮询进度
|
|
45
|
+
|
|
46
|
+
### 问题
|
|
47
|
+
下载几十个文件要几分钟,HTTP handler 不能阻塞。
|
|
48
|
+
|
|
49
|
+
### 方案
|
|
50
|
+
```python
|
|
51
|
+
# 立刻写 index(前端列表立刻出现),然后后台下载
|
|
52
|
+
with open(TEMPLATE_LOCAL_INDEX, 'w') as f:
|
|
53
|
+
json.dump(cdn_index, f)
|
|
54
|
+
|
|
55
|
+
t = threading.Thread(target=_do_sync_templates, args=(...), daemon=True)
|
|
56
|
+
t.start()
|
|
57
|
+
self._send_json({'syncing': True, 'total': len(pending)})
|
|
58
|
+
```
|
|
59
|
+
前端每 3 秒轮询 `/api/sync-status`。
|
|
60
|
+
|
|
61
|
+
### 经验
|
|
62
|
+
- **先写 index 再下载**:用户立刻看到列表,下载在后台进行
|
|
63
|
+
- **守卫变量防重复轮询**:`if (_pollTimer) return`
|
|
64
|
+
- **轮询 vs WebSocket**:简单场景轮询够用,不要过度设计
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 四、前端就绪状态简化
|
|
69
|
+
|
|
70
|
+
### 反面教材(复杂版)
|
|
71
|
+
维护 `_readyFolders` Set + `_rowMap` + `updateActionBtns()` + 开屏预取状态,
|
|
72
|
+
按钮 disabled/opacity 联动,约 40 行状态管理代码。
|
|
73
|
+
|
|
74
|
+
### 正确做法(简单版)
|
|
75
|
+
- 按钮永远可点
|
|
76
|
+
- 点击后服务端检查就绪状态,未就绪返回 `{code: 'not_ready'}`
|
|
77
|
+
- 前端只需在 `.then()` 里判断 `res.code === 'not_ready'` 显示提示
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
if (res.code === 'not_ready') {
|
|
81
|
+
setFooterStatus('⟳ 模板文件下载中,完成后即可使用', '#f59e0b');
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 经验
|
|
86
|
+
**让服务端做校验,前端做展示**,不要在前端维护服务端状态的镜像。
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 五、同一文件存在两个副本
|
|
91
|
+
|
|
92
|
+
### 问题
|
|
93
|
+
`deploy_to_workspace.py` 存在于两个路径:
|
|
94
|
+
- `.openclaw/skills/.../deploy_to_workspace.py` ← 服务端实际运行
|
|
95
|
+
- `.openclaw/workspace-yiranclaw/myclaw/skills/.../deploy_to_workspace.py` ← 源文件
|
|
96
|
+
|
|
97
|
+
`mc patch` 会用源文件覆盖服务端文件,导致修改被还原。
|
|
98
|
+
|
|
99
|
+
### 经验
|
|
100
|
+
- **改脚本必须改两处**,或者建立符号链接
|
|
101
|
+
- 发现改动被还原时,先查是哪个路径在被覆盖,再两处同步修改
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 六、Pipeline 职责拆分
|
|
106
|
+
|
|
107
|
+
### 原则:每个脚本只做一件事
|
|
108
|
+
|
|
109
|
+
| 脚本 | 职责 |
|
|
110
|
+
|------|------|
|
|
111
|
+
| `identify_template.py` | 颁发 `_id`、校准编号、设置 `_version` |
|
|
112
|
+
| `move_template_task.py` | 调用 identify、创建 `v{N}/` 子目录、复制文件 |
|
|
113
|
+
| `build_template_index.py` | 扫描本地目录、生成 JSON index |
|
|
114
|
+
| `publish_template.py` | 上传到 CDN(带版本路径)、上传 index |
|
|
115
|
+
| `migrate_templates.py` | 一次性迁移旧结构到新结构 |
|
|
116
|
+
|
|
117
|
+
### 经验
|
|
118
|
+
- `move` 不再删除原文件夹(改为复制),避免误操作丢失源文件
|
|
119
|
+
- 版本号在 `move` 阶段确定,`publish` 不决定版本号
|
|
120
|
+
- 迁移脚本加 `--dry-run` 参数,先验证再执行
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 七、API 设计:参数复用代替接口重复
|
|
125
|
+
|
|
126
|
+
### 问题
|
|
127
|
+
"复制到 playgrounds" 和 "创建新 workspace" 流程完全相同,只是目标 workspace 名不同。
|
|
128
|
+
|
|
129
|
+
### 反面做法
|
|
130
|
+
新增 `/api/template/new-workspace` 接口,重复了 90% 的代码。
|
|
131
|
+
|
|
132
|
+
### 正确做法
|
|
133
|
+
```python
|
|
134
|
+
# POST /api/template/deploy
|
|
135
|
+
# body: {"folder": "...", "agent": "playgrounds"} ← 已有 workspace
|
|
136
|
+
# body: {"folder": "..."} ← 不传则自动生成唯一名
|
|
137
|
+
agent_name = body.get('agent') or '{}-{}-{}'.format(series, version, hhmm)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 经验
|
|
141
|
+
**加参数,不加接口**。两个行为只有一个变量不同时,用参数区分而不是新建接口。
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 八、mc 命令扩展
|
|
146
|
+
|
|
147
|
+
### 方法
|
|
148
|
+
在 `index.js` 里添加两处:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// 1. 路由
|
|
152
|
+
} else if (command === 'sync-templates') {
|
|
153
|
+
runSyncTemplates();
|
|
154
|
+
|
|
155
|
+
// 2. 函数(调本地 API,轮询进度)
|
|
156
|
+
function runSyncTemplates() {
|
|
157
|
+
const http = require('http');
|
|
158
|
+
const apiPort = process.platform === 'linux' ? '8080' : '18800';
|
|
159
|
+
// apiGet → /api/sync-templates → 轮询 /api/sync-status
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 经验
|
|
164
|
+
- `mc` 命令本质是 Node.js 脚本,扩展成本极低
|
|
165
|
+
- 本地 API 端口:Mac=18800,Linux=8080
|
|
166
|
+
- 轮询用 `setTimeout` 递归,而不是 `setInterval`(更容易控制退出)
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 九、命名规范
|
|
171
|
+
|
|
172
|
+
### 实例目录命名
|
|
173
|
+
```
|
|
174
|
+
{业务名}_{HH:MM}
|
|
175
|
+
做一个会出招的神奇按钮_1911
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 新建 workspace 命名
|
|
179
|
+
```
|
|
180
|
+
{作品编号}-{版本号}-{小时分钟}
|
|
181
|
+
a104-v1-1911
|
|
182
|
+
→ workspace-a104-v1-1911
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 原则
|
|
186
|
+
- 去掉冗余信息(日期、系列前缀 `a104` 在实例名里不需要)
|
|
187
|
+
- 保留区分信息(时间保证唯一,版本号便于追溯)
|
|
188
|
+
- 用 `-` 分隔 workspace 名的各段,用 `_` 分隔实例名各段
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 十、window.open vs window.location.href
|
|
193
|
+
|
|
194
|
+
- `window.open('url', '_blank')` 会被浏览器弹窗拦截器阻止(非用户直接触发的点击)
|
|
195
|
+
- setTimeout 内的跳转用 `window.location.href` 更可靠
|
|
196
|
+
- 如果必须新标签,需要在用户点击的同步代码里调用 `window.open`
|