@aiyiran/myclaw 1.1.24 → 1.1.26

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.
Files changed (83) hide show
  1. package/.claude/settings.local.json +25 -1
  2. package/assets/myclaw-artifacts.js +1070 -126
  3. package/assets/myclaw-inject.js +913 -121
  4. package/delete_agents.js +268 -0
  5. package/index.js +361 -20
  6. package/package.json +1 -1
  7. package/patches/patch-manifest.json +10 -0
  8. package/server/sync_workspace.py +444 -14
  9. package/skills/yiran-course-template-pipeline/README.md +127 -0
  10. package/skills/yiran-course-template-pipeline/SKILL.md +65 -0
  11. package/skills/yiran-course-template-pipeline/assets/a100-teacher.example.html +66 -0
  12. package/skills/yiran-course-template-pipeline/assets/student-template.html +64 -0
  13. package/skills/yiran-course-template-pipeline/assets/teacher-portrait-demo.html +105 -0
  14. package/skills/yiran-course-template-pipeline/assets/teacher-task-view.html +110 -0
  15. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2651-demo/347/224/237/346/210/220.md +92 -0
  16. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2652-student/347/224/237/346/210/220.md +115 -0
  17. package/skills/yiran-course-template-pipeline/prompts//351/230/266/346/256/2653-teacher/347/224/237/346/210/220.md +131 -0
  18. 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 +77 -0
  19. package/skills/yiran-course-template-pipeline/references/student-example.json +38 -0
  20. package/skills/yiran-course-template-pipeline/references/student-fields.md +195 -0
  21. package/skills/yiran-course-template-pipeline/references/student-scaffold.json +34 -0
  22. package/skills/yiran-course-template-pipeline/references/teacher-fields.md +265 -0
  23. package/skills/yiran-course-template-pipeline/references/teacher-scaffold.json +25 -0
  24. package/skills/yiran-course-template-pipeline/scripts/build_template_views.py +125 -0
  25. package/skills/yiran-course-template-pipeline/scripts/move_template_task.py +59 -0
  26. package/skills/yiran-course-template-pipeline/scripts/render_student_page.py +52 -0
  27. package/skills/yiran-course-template-pipeline/scripts/render_teacher_view.py +108 -0
  28. package/skills/yiran-playground-template-use/SKILL.md +105 -0
  29. package/skills/yiran-playground-template-use/prompts/remix-handoff.txt +11 -0
  30. package/skills/yiran-playground-template-use/scripts/build_template_index.py +103 -0
  31. package/skills/yiran-playground-template-use/scripts/deploy_template.py +34 -0
  32. package/skills/yiran-playground-template-use/scripts/deploy_to_workspace.py +211 -0
  33. package/skills/yiran-playground-template-use/scripts/prepare_playgrounds.py +39 -0
  34. package/skills/yiran-playground-template-use/scripts/query_template.py +171 -0
  35. package/skills/yiran-playground-template-use/scripts/run_playgrounds_flow.py +44 -0
  36. package/skills/yiran-playground-template-use/scripts/start_tui_handoff.py +77 -0
  37. package/skills/yiran-playground-template-use/search-agent-prompt.md +39 -0
  38. package/skills/yiran-playground-template-use/template-index.json +136 -0
  39. package/skills/yiran-playground-template-use/template-index.md +38 -0
  40. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__demo__.html +140 -0
  41. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__student-view__.html +64 -0
  42. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__student__.json +38 -0
  43. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__teacher-view__.html +52 -0
  44. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/__teacher__.json +36 -0
  45. package/skills/yiran-playground-template-use/templates/a100_/347/273/231/344/276/235/347/204/266/350/200/201/345/270/210/350/256/276/350/256/241/344/270/200/344/270/252AI/347/224/273/345/203/217/index.html +61 -0
  46. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__demo__.html +131 -0
  47. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__student-view__.html +64 -0
  48. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__student__.json +34 -0
  49. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__teacher-view__.html +52 -0
  50. package/skills/yiran-playground-template-use/templates/a101_/345/201/2323/345/274/240/345/220/214/344/270/273/351/242/230/345/233/276/347/211/207/__teacher__.json +34 -0
  51. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__demo__.html +77 -0
  52. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__student-view__.html +64 -0
  53. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__student__.json +38 -0
  54. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__teacher-view__.html +52 -0
  55. package/skills/yiran-playground-template-use/templates/a103_/345/201/232/344/270/200/344/270/252/344/273/213/347/273/215/351/241/265/351/235/242/__teacher__.json +34 -0
  56. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__demo__.html +162 -0
  57. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__student-view__.html +64 -0
  58. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__student__.json +34 -0
  59. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__teacher-view__.html +52 -0
  60. package/skills/yiran-playground-template-use/templates/b100_/345/201/232/344/270/200/344/270/252/346/214/211/351/222/256/351/241/265/351/235/242/__teacher__.json +34 -0
  61. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__demo__.html +180 -0
  62. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__student-view__.html +64 -0
  63. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__student__.json +38 -0
  64. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__teacher-view__.html +52 -0
  65. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/__teacher__.json +41 -0
  66. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/demo.html +180 -0
  67. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271/index.html +121 -0
  68. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271//345/260/217/347/273/204/345/220/211/347/245/245/347/211/251_26.png +0 -0
  69. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271//345/260/217/347/273/204/345/233/276/345/275/242/346/240/207/345/277/227_83.png +0 -0
  70. package/skills/yiran-playground-template-use/templates/c100_/347/273/231/345/260/217/347/273/204/344/275/234/345/223/201/345/201/232/344/270/200/346/254/241/345/260/217/344/277/256/346/224/271//347/217/255/347/272/247/345/260/217/347/273/204/345/276/275/347/253/240_47.png +0 -0
  71. package/skills/yiran-skill-media/SKILL.md +6 -15
  72. package/skills/yiran-skill-media/scripts/generate.py +47 -18
  73. package/skills/yiran-skill-media/scripts/generation_log.json +1 -56
  74. package/skills/yiran-skill-media/scripts/providers/__pycache__/__init__.cpython-311.pyc +0 -0
  75. package/skills/yiran-skill-media/scripts/providers/__pycache__/__init__.cpython-37.pyc +0 -0
  76. package/skills/yiran-skill-media/scripts/providers/__pycache__/jimeng_image.cpython-37.pyc +0 -0
  77. package/skills/yiran-skill-media/scripts/providers/__pycache__/jimeng_video.cpython-311.pyc +0 -0
  78. package/skills/yiran-skill-media/scripts/providers/__pycache__/jimeng_video.cpython-37.pyc +0 -0
  79. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_image.cpython-37.pyc +0 -0
  80. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_music.cpython-37.pyc +0 -0
  81. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_video.cpython-311.pyc +0 -0
  82. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_video.cpython-37.pyc +0 -0
  83. package/skills/yiran-skill-media/scripts/providers/__pycache__/vapi_image.cpython-37.pyc +0 -0
@@ -0,0 +1,265 @@
1
+ # teacher-fields.md
2
+
3
+ 这个文件用于说明 `__teacher__.json` 的字段结构、分类规则、编号规则,以及教师侧固定标签的使用方法。
4
+
5
+ ---
6
+
7
+ ## 一、用途
8
+
9
+ `__teacher__.json` 不给学生看。
10
+
11
+ 它主要服务于:
12
+ - 课程筛选
13
+ - 教师判断
14
+ - 上课引导
15
+ - 模板归档
16
+ - 模板索引
17
+
18
+ 所以这里的字段重点不是“学生要看到什么”,而是“老师怎么判断这个任务”。
19
+
20
+ ---
21
+
22
+ ## 二、命名规则
23
+
24
+ 建议使用:
25
+
26
+ `任务类别 + 三位编号 + 任务名称`
27
+
28
+ 例如:
29
+ - `a100_给依然老师设计一个AI画像`
30
+ - `b203_做一个介绍页面`
31
+ - `c305_优化一个信息页`
32
+
33
+ 说明:
34
+ - 任务类别使用 A、B、C
35
+ - 文件夹命名时转成小写 a、b、c
36
+ - 任务编号固定为三位数字
37
+ - 编号范围固定为 `100 ~ 999`
38
+ - 每个类别单独编号,并且按顺序往后排
39
+ - 也就是说:A 类从 `a100` 开始,B 类从 `b100` 开始,C 类从 `c100` 开始
40
+ - 同一类别新增模板时,按 `100、101、102 ...` 这样继续往后加
41
+
42
+ ---
43
+
44
+ ## 三、任务类别(ABC)
45
+
46
+ ABC 不是按难度分。
47
+
48
+ 而是按:
49
+ **这个任务在学习过程中,主要起什么作用。**
50
+
51
+ ### A = 启动类
52
+ 定位:让学生动起来
53
+
54
+ 适合:
55
+ - 弱学生
56
+ - 卡住学生
57
+
58
+ 特点:
59
+ - 门槛低
60
+ - 容易开始
61
+ - 很快看到结果
62
+ - 重点是先做出来
63
+
64
+ ### B = 结构类
65
+ 定位:开始搭东西
66
+
67
+ 适合:
68
+ - 中间学生
69
+
70
+ 特点:
71
+ - 强调结构
72
+ - 强调信息组织
73
+ - 强调完整框架
74
+
75
+ ### C = 迭代类
76
+ 定位:重点训练改
77
+
78
+ 适合:
79
+ - 会做但不会改的人
80
+
81
+ 特点:
82
+ - 强调修改
83
+ - 强调比较前后变化
84
+ - 强调优化能力
85
+
86
+ ---
87
+
88
+ ## 四、固定标签标准
89
+
90
+ __teacher__.json 中有两组固定标签:
91
+ - 主能力标签
92
+ - 任务类型标签
93
+
94
+ 它们的作用不同,不要混用。
95
+
96
+ ### 1)主能力标签是什么
97
+ 主能力标签回答的是:
98
+ **这个任务主要在练哪种核心能力。**
99
+
100
+ 它强调的是“训练目标”。
101
+
102
+ ### 2)任务类型标签是什么
103
+ 任务类型标签回答的是:
104
+ **这个任务最终做出来的作品,属于哪种任务形态。**
105
+
106
+ 它强调的是“作品类型”。
107
+
108
+ ---
109
+
110
+ ## 五、主能力标签固定集合
111
+
112
+ 主能力标签只能从下面 4 个里选 1 个:
113
+
114
+ ### 1. 构思能力
115
+ 适合:
116
+ - 重点在想主题
117
+ - 重点在想设定
118
+ - 重点在想内容方向
119
+
120
+ 常见场景:
121
+ - 先想一个人物
122
+ - 先想一个故事
123
+ - 先想一个主题
124
+
125
+ ### 2. 表达构建
126
+ 适合:
127
+ - 重点在把内容组织出来
128
+ - 重点在把想法做成页面或作品
129
+ - 重点在图文表达、内容安排、结构搭建
130
+
131
+ 常见场景:
132
+ - 做介绍页
133
+ - 做展示页
134
+ - 做图文页面
135
+
136
+ ### 3. 问题判断
137
+ 适合:
138
+ - 重点在发现问题
139
+ - 重点在判断哪里不合适
140
+ - 重点在比较优缺点
141
+
142
+ 常见场景:
143
+ - 对比两个版本
144
+ - 找出页面问题
145
+ - 判断哪里需要调整
146
+
147
+ ### 4. 迭代推进
148
+ 适合:
149
+ - 重点在继续修改
150
+ - 重点在一版一版优化
151
+ - 重点在把已有作品改得更好
152
+
153
+ 常见场景:
154
+ - 根据反馈继续改
155
+ - 优化排版
156
+ - 优化结构层次
157
+
158
+ ---
159
+
160
+ ## 六、任务类型标签固定集合
161
+
162
+ 任务类型标签只能从下面 4 个里选 1 个:
163
+
164
+ ### 1)游戏机制
165
+ 适合:
166
+ - 作品重点是规则
167
+ - 作品重点是玩法
168
+ - 作品重点是互动机制
169
+
170
+ ### 2)角色交互
171
+ 适合:
172
+ - 作品重点是人物
173
+ - 作品重点是角色关系
174
+ - 作品重点是对话和互动
175
+
176
+ ### 3)视听表达
177
+ 适合:
178
+ - 作品重点是图像、文字、页面展示
179
+ - 作品重点是图文表达
180
+ - 作品重点是视觉呈现
181
+
182
+ ### 4)改编优化
183
+ 适合:
184
+ - 作品重点是修改已有内容
185
+ - 作品重点是优化旧版本
186
+ - 作品重点是重做和升级
187
+
188
+ ---
189
+
190
+ ## 七、怎么选标签
191
+
192
+ ### 先选主能力标签
193
+ 先看这个任务最主要练什么。
194
+
195
+ 例如:
196
+ - 重点是先想内容,用 `1.构思能力`
197
+ - 重点是做成页面,用 `2.表达构建`
198
+ - 重点是找问题,用 `3.问题判断`
199
+ - 重点是继续优化,用 `4.迭代推进`
200
+
201
+ ### 再选任务类型标签
202
+ 再看这个任务做出来的作品属于什么。
203
+
204
+ 例如:
205
+ - 做介绍页面,多半是 `视听表达`
206
+ - 做角色对话,多半是 `角色交互`
207
+ - 做玩法设计,多半是 `游戏机制`
208
+ - 做旧作品优化,多半是 `改编优化`
209
+
210
+ ---
211
+
212
+ ## 八、__teacher__.json 推荐结构
213
+
214
+ ```json
215
+ {
216
+ "任务类别": "",
217
+ "任务编号": "",
218
+ "任务名称": "",
219
+ "主能力标签": "",
220
+ "任务类型标签": "",
221
+ "适合学生类型": [],
222
+ "训练重点": [],
223
+ "卡点和解决方案": [],
224
+ "可拓展方向": [],
225
+ "课后作业": []
226
+ }
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 九、字段说明
232
+
233
+ ### 1)任务类别
234
+ 只能填写:`A`、`B`、`C`
235
+
236
+ ### 2)任务编号
237
+ - 使用三位数字字符串
238
+ - 范围固定为:`100 ~ 999`
239
+ - 例如:`100`、`203`、`305`
240
+
241
+ ### 3)任务名称
242
+ 用于表达这个任务具体要做什么。
243
+
244
+ ### 4)主能力标签
245
+ - 只能选 1 个
246
+ - 固定集合:1.构思能力 / 2.表达构建 / 3.问题判断 / 4.迭代推进
247
+
248
+ ### 5)任务类型标签
249
+ - 只能选 1 个
250
+ - 固定集合:游戏机制 / 角色交互 / 视听表达 / 改编优化
251
+
252
+ ### 6)适合学生类型
253
+ 用于说明更适合什么学生。
254
+
255
+ ### 7)训练重点
256
+ 用于说明这个任务主要练什么。
257
+
258
+ ### 8)卡点和解决方案
259
+ 用于说明老师预判到的常见问题,以及可用引导。
260
+
261
+ ### 9)可拓展方向
262
+ 用于说明后续还能往哪里继续做。
263
+
264
+ ### 10)课后作业
265
+ 用于说明课后还能延伸什么练习。
@@ -0,0 +1,25 @@
1
+ {
2
+ "任务类别": "请填写任务类别,只能使用 A、B、C。其中 A=启动类,B=结构类,C=迭代类",
3
+ "任务编号": "请填写三位编号字符串,编号范围为 100 到 999。每个类别单独从 100 开始,后续按顺序递增,例如 100、101、102",
4
+ "任务名称": "请填写任务名称,应和 __student__.json 中的任务标题一致或高度一致",
5
+ "主能力标签": "请从固定标签中只选一个。固定集合:1.构思能力 / 2.表达构建 / 3.问题判断 / 4.迭代推进。它回答的是这个任务主要训练什么能力",
6
+ "任务类型标签": "请从固定标签中只选一个。固定集合:游戏机制 / 角色交互 / 视听表达 / 改编优化。它回答的是这个任务最终属于什么作品类型",
7
+ "适合学生类型": [
8
+ "请填写一个适合学生类型标签,例如:初次接触"
9
+ ],
10
+ "训练重点": [
11
+ "请填写这个作品本身最重要的训练特点"
12
+ ],
13
+ "卡点和解决方案": [
14
+ {
15
+ "卡点": "请填写学生最容易卡住的具体问题",
16
+ "解决方案": "请填写老师可以如何引导学生解决这个问题"
17
+ }
18
+ ],
19
+ "可拓展方向": [
20
+ "请填写这个任务还能往哪里继续做"
21
+ ],
22
+ "课后作业": [
23
+ "请填写一个具体、可执行的课后作业"
24
+ ]
25
+ }
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env python3
2
+ import html
3
+ import json
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ REQUIRED_BASE_FILES = ['__demo__.html', '__student__.json', '__teacher__.json']
9
+ REQUIRED_STUDENT_KEYS = ['任务标题', '一句话说明', '步骤', '评价目标', '示例区标题', '示例页面文件']
10
+ REQUIRED_TEACHER_KEYS = ['任务类别', '任务编号', '任务名称', '主能力标签', '任务类型标签', '适合学生类型', '训练重点', '卡点和解决方案', '可拓展方向', '课后作业']
11
+ ROOT = Path(__file__).resolve().parent
12
+
13
+ INDEX_TEMPLATE = """\
14
+ <!DOCTYPE html>
15
+ <html lang="zh-CN">
16
+ <head>
17
+ <meta charset="UTF-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ <title>{title}</title>
20
+ <style>
21
+ * {{ box-sizing: border-box; }}
22
+ body {{
23
+ margin: 0;
24
+ padding: 24px;
25
+ font-family: monospace;
26
+ background: #f6f8fc;
27
+ color: #1f2937;
28
+ }}
29
+ .grid {{
30
+ display: grid;
31
+ grid-template-columns: 1fr 1fr;
32
+ gap: 20px;
33
+ align-items: start;
34
+ }}
35
+ h2 {{
36
+ margin: 0 0 10px;
37
+ font-size: 18px;
38
+ }}
39
+ pre {{
40
+ margin: 0;
41
+ white-space: pre-wrap;
42
+ word-break: break-word;
43
+ background: #ffffff;
44
+ border: 1px solid #dbe3f0;
45
+ border-radius: 12px;
46
+ padding: 16px;
47
+ line-height: 1.7;
48
+ }}
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <div class="grid">
53
+ <section>
54
+ <h2>评价目标</h2>
55
+ <pre>{goals_json}</pre>
56
+ </section>
57
+ <section>
58
+ <h2>步骤</h2>
59
+ <pre>{steps_json}</pre>
60
+ </section>
61
+ </div>
62
+
63
+ <script type="application/json" id="task-data">
64
+ {raw_task_json}
65
+ </script>
66
+ </body>
67
+ </html>
68
+ """
69
+
70
+
71
+ def load_json(path):
72
+ with path.open('r', encoding='utf-8') as f:
73
+ return json.load(f)
74
+
75
+
76
+ def check_keys(data, keys, name):
77
+ missing = [k for k in keys if k not in data]
78
+ if missing:
79
+ raise ValueError(f'{name} 缺少字段: {", ".join(missing)}')
80
+
81
+
82
+ def validate_folder(source_dir: Path):
83
+ for filename in REQUIRED_BASE_FILES:
84
+ if not (source_dir / filename).exists():
85
+ raise FileNotFoundError(f'缺少必需文件: {filename}')
86
+ student = load_json(source_dir / '__student__.json')
87
+ teacher = load_json(source_dir / '__teacher__.json')
88
+ check_keys(student, REQUIRED_STUDENT_KEYS, '__student__.json')
89
+ check_keys(teacher, REQUIRED_TEACHER_KEYS, '__teacher__.json')
90
+
91
+
92
+ def render_index_page(source_dir: Path):
93
+ student = load_json(source_dir / '__student__.json')
94
+ task_json = json.dumps(student, ensure_ascii=False, indent=2)
95
+ goals_json = json.dumps(student.get('评价目标', []), ensure_ascii=False, indent=2)
96
+ steps_json = json.dumps(student.get('步骤', []), ensure_ascii=False, indent=2)
97
+ content = INDEX_TEMPLATE.format(
98
+ title=html.escape(student.get('任务标题', '未命名任务')),
99
+ goals_json=html.escape(goals_json),
100
+ steps_json=html.escape(steps_json),
101
+ raw_task_json=task_json,
102
+ )
103
+ out = source_dir / 'index.html'
104
+ out.write_text(content, encoding='utf-8')
105
+ print(f'已生成: {out}')
106
+
107
+
108
+ def main():
109
+ if len(sys.argv) < 2:
110
+ print('用法: python3 build_template_views.py <已有文件夹>')
111
+ sys.exit(1)
112
+
113
+ source_dir = Path(sys.argv[1]).expanduser().resolve()
114
+ validate_folder(source_dir)
115
+
116
+ subprocess.run(['python3', str(ROOT / 'render_student_page.py'), str(source_dir / '__student__.json'), str(source_dir / '__student-view__.html')], check=True)
117
+ subprocess.run(['python3', str(ROOT / 'render_teacher_view.py'), str(source_dir / '__teacher__.json'), str(source_dir / '__teacher-view__.html')], check=True)
118
+ render_index_page(source_dir)
119
+
120
+ print(f'已在原文件夹生成: {source_dir}')
121
+ print('已生成 __student-view__.html、__teacher-view__.html、index.html')
122
+
123
+
124
+ if __name__ == '__main__':
125
+ main()
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ ROOT = Path(__file__).resolve().parent.parent
9
+ SEARCH_SKILL = ROOT.parent / 'yiran-playground-template-use'
10
+ DEFAULT_TARGET = SEARCH_SKILL / 'templates'
11
+ INDEX_SCRIPT = SEARCH_SKILL / 'scripts' / 'build_template_index.py'
12
+ REQUIRED_FILES = ['__demo__.html', '__student__.json', '__teacher__.json', '__student-view__.html', '__teacher-view__.html']
13
+
14
+
15
+ def load_json(path):
16
+ with path.open('r', encoding='utf-8') as f:
17
+ return json.load(f)
18
+
19
+
20
+ def task_folder_name(teacher):
21
+ category = str(teacher['任务类别']).upper()
22
+ if category not in {'A', 'B', 'C'}:
23
+ raise ValueError('任务类别只能是 A、B、C')
24
+
25
+ number = str(teacher['任务编号']).zfill(3)
26
+ if not number.isdigit() or not (100 <= int(number) <= 999):
27
+ raise ValueError('任务编号必须是 100 到 999 的三位数字字符串')
28
+
29
+ name = str(teacher['任务名称']).replace('/', '_').replace(' ', '_')
30
+ return f'{category.lower()}{number}_{name}'
31
+
32
+
33
+ def main():
34
+ if len(sys.argv) < 2:
35
+ print('用法: python3 move_template_task.py <已有文件夹> [目标目录]')
36
+ sys.exit(1)
37
+
38
+ source_dir = Path(sys.argv[1]).expanduser().resolve()
39
+ target_root = Path(sys.argv[2]).expanduser().resolve() if len(sys.argv) > 2 else DEFAULT_TARGET
40
+
41
+ for filename in REQUIRED_FILES:
42
+ if not (source_dir / filename).exists():
43
+ raise FileNotFoundError(f'缺少必需文件: {filename}')
44
+
45
+ teacher = load_json(source_dir / '__teacher__.json')
46
+ target_root.mkdir(parents=True, exist_ok=True)
47
+ target_dir = target_root / task_folder_name(teacher)
48
+ if target_dir.exists():
49
+ raise FileExistsError(f'目标目录已存在: {target_dir}')
50
+
51
+ shutil.move(str(source_dir), str(target_dir))
52
+ print(f'已移动并改名到: {target_dir}')
53
+
54
+ subprocess.run(['python3', str(INDEX_SCRIPT)], check=True)
55
+ print('已自动重建 templates 索引')
56
+
57
+
58
+ if __name__ == '__main__':
59
+ main()
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import html
4
+ import sys
5
+ from pathlib import Path
6
+
7
+
8
+ def esc(value):
9
+ return html.escape(str(value if value is not None else ''))
10
+
11
+
12
+ def render_steps(steps):
13
+ blocks = []
14
+ for idx, item in enumerate(steps, start=1):
15
+ blocks.append(f'''<div class="task-item"><div class="item-top"><span class="num">{idx}</span><span>{esc(item.get('标题', '步骤'))}</span></div><div class="item-body">{esc(item.get('说明', ''))}</div></div>''')
16
+ return ''.join(blocks)
17
+
18
+
19
+ def render_goals(goals):
20
+ blocks = []
21
+ for item in goals:
22
+ blocks.append(f'''<div class="task-item star-item"><div class="item-top"><span class="num">{esc(item.get('等级', ''))}</span><span>{esc(item.get('等级', ''))}</span></div><div class="item-body">{esc(item.get('说明', ''))}</div></div>''')
23
+ return ''.join(blocks)
24
+
25
+
26
+ def main():
27
+ if len(sys.argv) < 2:
28
+ print('用法: python3 render_student_page.py <__student__.json> [输出html] [模板html]')
29
+ sys.exit(1)
30
+
31
+ input_path = Path(sys.argv[1]).expanduser().resolve()
32
+ output_path = Path(sys.argv[2]).expanduser().resolve() if len(sys.argv) > 2 else input_path.parent / '__student-view__.html'
33
+ template_path = Path(sys.argv[3]).expanduser().resolve() if len(sys.argv) > 3 else Path(__file__).resolve().parent.parent / 'assets' / 'student-template.html'
34
+
35
+ data = json.loads(input_path.read_text(encoding='utf-8'))
36
+ template = template_path.read_text(encoding='utf-8')
37
+
38
+ rendered = (template
39
+ .replace('{{任务标题}}', esc(data.get('任务标题', '未命名任务')))
40
+ .replace('{{一句话说明}}', esc(data.get('一句话说明', '')))
41
+ .replace('{{步骤区域}}', render_steps(data.get('步骤', [])))
42
+ .replace('{{评价目标区域}}', render_goals(data.get('评价目标', [])))
43
+ .replace('{{示例区标题}}', esc(data.get('示例区标题', '示范样例')))
44
+ .replace('{{示例页面文件}}', esc(data.get('示例页面文件', 'example.html')))
45
+ )
46
+
47
+ output_path.write_text(rendered, encoding='utf-8')
48
+ print(f'已生成: {output_path}')
49
+
50
+
51
+ if __name__ == '__main__':
52
+ main()
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import html
4
+ import sys
5
+ from pathlib import Path
6
+
7
+
8
+ def esc(value):
9
+ return html.escape(str(value if value is not None else ""))
10
+
11
+
12
+ def render_tags(items):
13
+ return ''.join(f'<span class="tag">{esc(item)}</span>' for item in items)
14
+
15
+
16
+ def render_list(items):
17
+ return ''.join(f'<div class="list-item">{esc(item)}</div>' for item in items)
18
+
19
+
20
+ def render_issues(items):
21
+ blocks = []
22
+ for item in items:
23
+ card = item.get('卡点', '') if isinstance(item, dict) else ''
24
+ solution = item.get('解决方案', '') if isinstance(item, dict) else ''
25
+ blocks.append('<div class="list-item issue">' f'<strong>卡点</strong>{esc(card)}' f'<strong>解决方案</strong>{esc(solution)}' '</div>')
26
+ return ''.join(blocks)
27
+
28
+
29
+ def guess_preview_file(input_path: Path):
30
+ folder = input_path.parent
31
+ for name in ['__demo__.html', 'example.html', 'index.html', 'preview.html']:
32
+ candidate = folder / name
33
+ if candidate.exists() and candidate.name != input_path.name:
34
+ return candidate.name
35
+ return ''
36
+
37
+
38
+ def render_html(data, preview_file=''):
39
+ title = f"{data.get('任务类别', '')}{data.get('任务编号', '')} · {data.get('任务名称', '')}".strip(' ·')
40
+ iframe_html = f'<div class="preview-card"><div class="preview-head"><h2>作品预览</h2></div><iframe src="{esc(preview_file)}" title="作品预览"></iframe></div>' if preview_file else '<div class="preview-card empty"><div class="preview-head"><h2>作品预览</h2></div><div class="empty-box">当前文件夹下没有找到 example.html / index.html / preview.html</div></div>'
41
+ return f'''<!DOCTYPE html>
42
+ <html lang="zh-CN">
43
+ <head>
44
+ <meta charset="UTF-8" />
45
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
46
+ <title>{esc(title or '教师任务查看页')}</title>
47
+ <style>
48
+ * {{ box-sizing: border-box; }}
49
+ body {{ margin: 0; font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #f4f7fb; color: #24324a; }}
50
+ .page {{ max-width: 1400px; margin: 0 auto; padding: 20px; }}
51
+ .hero {{ background: linear-gradient(135deg,#5f82ff 0%,#8c74ff 100%); color: #fff; border-radius: 24px; padding: 22px 24px; box-shadow: 0 16px 36px rgba(92,108,192,.22); }}
52
+ .hero h1 {{ margin: 0; font-size: 28px; }}
53
+ .hero p {{ margin: 8px 0 0; opacity: .92; font-size: 14px; }}
54
+ .layout {{ margin-top: 18px; display: grid; grid-template-columns: 360px minmax(0,1fr); gap: 16px; align-items: start; }}
55
+ .sidebar {{ display: grid; gap: 16px; }}
56
+ .card, .preview-card {{ background: #fff; border: 1px solid #e7eefc; border-radius: 20px; padding: 18px; box-shadow: 0 10px 24px rgba(100,122,166,.08); }}
57
+ .card h2, .preview-head h2 {{ margin: 0 0 12px; font-size: 18px; color: #314a96; }}
58
+ .meta-grid {{ display: grid; grid-template-columns: 1fr; gap: 10px; }}
59
+ .meta-item {{ background: #f8faff; border: 1px solid #e7eefc; border-radius: 14px; padding: 12px; }}
60
+ .meta-item .label {{ font-size: 12px; color: #7a89ad; margin-bottom: 6px; }}
61
+ .meta-item .value {{ font-size: 14px; color: #24324a; font-weight: 700; }}
62
+ .tags {{ display: flex; flex-wrap: wrap; gap: 8px; }}
63
+ .tag {{ display: inline-flex; align-items: center; padding: 7px 12px; border-radius: 999px; background: #edf2ff; color: #4c65c3; font-size: 13px; font-weight: 700; }}
64
+ .list {{ display: grid; gap: 10px; }}
65
+ .list-item {{ background: #f8faff; border: 1px solid #e7eefc; border-radius: 14px; padding: 12px; line-height: 1.7; font-size: 14px; color: #60708b; }}
66
+ .issue {{ border-left: 4px solid #8c74ff; background: #faf8ff; }}
67
+ .issue strong {{ color: #314a96; display: block; margin: 6px 0; }}
68
+ iframe {{ width: 100%; height: 900px; border: 1px solid #dfe7fb; border-radius: 16px; background: #fff; display: block; }}
69
+ .empty-box {{ background: #f8faff; border: 1px dashed #cfdcff; border-radius: 14px; padding: 24px; color: #7a89ad; line-height: 1.8; }}
70
+ @media (max-width: 960px) {{ .layout {{ grid-template-columns: 1fr; }} iframe {{ height: 420px; }} }}
71
+ </style>
72
+ </head>
73
+ <body>
74
+ <div class="page">
75
+ <section class="hero">
76
+ <h1>{esc(title or '教师任务查看页')}</h1>
77
+ <p>这是根据教师 JSON 自动生成的可视化查看页,左边快速扫读,右边直接看作品预览。</p>
78
+ </section>
79
+ <section class="layout">
80
+ <div class="sidebar">
81
+ <div class="card"><h2>基础信息</h2><div class="meta-grid"><div class="meta-item"><div class="label">主能力标签</div><div class="value">{esc(data.get('主能力标签', '—'))}</div></div><div class="meta-item"><div class="label">任务类型标签</div><div class="value">{esc(data.get('任务类型标签', '—'))}</div></div></div></div>
82
+ <div class="card"><h2>适合学生类型</h2><div class="tags">{render_tags(data.get('适合学生类型', []))}</div></div>
83
+ <div class="card"><h2>训练重点</h2><div class="list">{render_list(data.get('训练重点', []))}</div></div>
84
+ <div class="card"><h2>卡点和解决方案</h2><div class="list">{render_issues(data.get('卡点和解决方案', []))}</div></div>
85
+ <div class="card"><h2>可拓展方向</h2><div class="list">{render_list(data.get('可拓展方向', []))}</div></div>
86
+ <div class="card"><h2>课后作业</h2><div class="list">{render_list(data.get('课后作业', []))}</div></div>
87
+ </div>
88
+ {iframe_html}
89
+ </section>
90
+ </div>
91
+ </body>
92
+ </html>'''
93
+
94
+
95
+ def main():
96
+ if len(sys.argv) < 2:
97
+ print('用法: python3 render_teacher_view.py <输入json> [输出html]')
98
+ sys.exit(1)
99
+ input_path = Path(sys.argv[1]).expanduser().resolve()
100
+ output_path = Path(sys.argv[2]).expanduser().resolve() if len(sys.argv) > 2 else input_path.parent / '__teacher-view__.html'
101
+ with input_path.open('r', encoding='utf-8') as f:
102
+ data = json.load(f)
103
+ output_path.write_text(render_html(data, guess_preview_file(input_path)), encoding='utf-8')
104
+ print(f'已生成: {output_path}')
105
+
106
+
107
+ if __name__ == '__main__':
108
+ main()