@aiyiran/myclaw 1.1.88 → 1.1.90
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/111.html +437 -0
- package/assets/myclaw-artifacts.js +24 -9
- package/injects/inject-minimax.js +11 -3
- package/package.json +1 -1
- package/server/sync_workspace.py +62 -25
- package/skills/yiran-course-template-pipeline/scripts/render_student_page.py +7 -1
- package/skills/yiran-course-template-pipeline/template-index.json +55 -1
- package/skills/yiran-course-template-pipeline/template-index.md +36 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/1.html +1756 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/__demo__.html +1756 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/__student-view__.html +77 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/__student__.json +60 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/__teacher-view__.html +52 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/__teacher__.json +45 -0
- package//345/234/260/344/270/213/345/237/216/345/244/247/345/206/222/351/231/251/index.html +150 -0
package/111.html
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>AI学习小工具创造营|4节课课程大纲</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #f6f7fb;
|
|
10
|
+
--card: #ffffff;
|
|
11
|
+
--text: #1f2937;
|
|
12
|
+
--muted: #6b7280;
|
|
13
|
+
--line: #e5e7eb;
|
|
14
|
+
--primary: #2563eb;
|
|
15
|
+
--primary-soft: #eff6ff;
|
|
16
|
+
--accent: #f59e0b;
|
|
17
|
+
--green: #16a34a;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 28px;
|
|
27
|
+
font-family:
|
|
28
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
|
29
|
+
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
|
30
|
+
background: var(--bg);
|
|
31
|
+
color: var(--text);
|
|
32
|
+
line-height: 1.55;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.page {
|
|
36
|
+
max-width: 1180px;
|
|
37
|
+
margin: 0 auto;
|
|
38
|
+
background: var(--card);
|
|
39
|
+
border-radius: 24px;
|
|
40
|
+
padding: 34px 38px 36px;
|
|
41
|
+
box-shadow: 0 18px 50px rgba(15, 23, 42, 0.08);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.header {
|
|
45
|
+
display: grid;
|
|
46
|
+
grid-template-columns: 1.5fr 1fr;
|
|
47
|
+
gap: 24px;
|
|
48
|
+
align-items: end;
|
|
49
|
+
margin-bottom: 24px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.eyebrow {
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: 8px;
|
|
56
|
+
padding: 6px 12px;
|
|
57
|
+
border-radius: 999px;
|
|
58
|
+
background: var(--primary-soft);
|
|
59
|
+
color: var(--primary);
|
|
60
|
+
font-size: 14px;
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
margin-bottom: 12px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
h1 {
|
|
66
|
+
margin: 0;
|
|
67
|
+
font-size: 36px;
|
|
68
|
+
line-height: 1.18;
|
|
69
|
+
letter-spacing: -0.5px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.subtitle {
|
|
73
|
+
margin: 12px 0 0;
|
|
74
|
+
color: var(--muted);
|
|
75
|
+
font-size: 17px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.price-card {
|
|
79
|
+
justify-self: end;
|
|
80
|
+
width: 100%;
|
|
81
|
+
max-width: 330px;
|
|
82
|
+
border: 1px solid var(--line);
|
|
83
|
+
border-radius: 20px;
|
|
84
|
+
padding: 18px 20px;
|
|
85
|
+
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.price-title {
|
|
89
|
+
color: var(--muted);
|
|
90
|
+
font-size: 14px;
|
|
91
|
+
font-weight: 700;
|
|
92
|
+
margin-bottom: 6px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.price {
|
|
96
|
+
font-size: 30px;
|
|
97
|
+
font-weight: 850;
|
|
98
|
+
color: var(--primary);
|
|
99
|
+
margin: 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.price-note {
|
|
103
|
+
margin: 6px 0 0;
|
|
104
|
+
color: var(--muted);
|
|
105
|
+
font-size: 14px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.summary {
|
|
109
|
+
display: grid;
|
|
110
|
+
grid-template-columns: repeat(3, 1fr);
|
|
111
|
+
gap: 14px;
|
|
112
|
+
margin: 24px 0 24px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.summary-item {
|
|
116
|
+
border: 1px solid var(--line);
|
|
117
|
+
border-radius: 18px;
|
|
118
|
+
padding: 16px 18px;
|
|
119
|
+
background: #fff;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.summary-label {
|
|
123
|
+
color: var(--muted);
|
|
124
|
+
font-size: 13px;
|
|
125
|
+
font-weight: 700;
|
|
126
|
+
margin-bottom: 5px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.summary-value {
|
|
130
|
+
font-size: 17px;
|
|
131
|
+
font-weight: 800;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
table {
|
|
135
|
+
width: 100%;
|
|
136
|
+
border-collapse: separate;
|
|
137
|
+
border-spacing: 0;
|
|
138
|
+
overflow: hidden;
|
|
139
|
+
border: 1px solid var(--line);
|
|
140
|
+
border-radius: 18px;
|
|
141
|
+
font-size: 15px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
thead th {
|
|
145
|
+
background: #111827;
|
|
146
|
+
color: #ffffff;
|
|
147
|
+
text-align: left;
|
|
148
|
+
font-size: 14px;
|
|
149
|
+
padding: 14px 14px;
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
tbody td {
|
|
154
|
+
padding: 16px 14px;
|
|
155
|
+
border-top: 1px solid var(--line);
|
|
156
|
+
vertical-align: top;
|
|
157
|
+
background: #ffffff;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
tbody tr:nth-child(even) td {
|
|
161
|
+
background: #fafafa;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.lesson {
|
|
165
|
+
width: 72px;
|
|
166
|
+
font-weight: 850;
|
|
167
|
+
color: var(--primary);
|
|
168
|
+
white-space: nowrap;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.project {
|
|
172
|
+
min-width: 150px;
|
|
173
|
+
font-weight: 850;
|
|
174
|
+
font-size: 16px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.tag {
|
|
178
|
+
display: inline-block;
|
|
179
|
+
margin-top: 8px;
|
|
180
|
+
padding: 3px 8px;
|
|
181
|
+
border-radius: 999px;
|
|
182
|
+
background: var(--primary-soft);
|
|
183
|
+
color: var(--primary);
|
|
184
|
+
font-size: 12px;
|
|
185
|
+
font-weight: 750;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.outcome {
|
|
189
|
+
font-weight: 760;
|
|
190
|
+
color: #111827;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.iteration {
|
|
194
|
+
color: #374151;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.iteration strong {
|
|
198
|
+
color: var(--green);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.footer {
|
|
202
|
+
display: grid;
|
|
203
|
+
grid-template-columns: 1.2fr 1fr;
|
|
204
|
+
gap: 18px;
|
|
205
|
+
margin-top: 22px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.note-box {
|
|
209
|
+
border-radius: 18px;
|
|
210
|
+
padding: 18px 20px;
|
|
211
|
+
background: #f8fafc;
|
|
212
|
+
border: 1px solid var(--line);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.note-box h3 {
|
|
216
|
+
margin: 0 0 8px;
|
|
217
|
+
font-size: 17px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.note-box p {
|
|
221
|
+
margin: 0;
|
|
222
|
+
color: var(--muted);
|
|
223
|
+
font-size: 14.5px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.highlight {
|
|
227
|
+
color: var(--accent);
|
|
228
|
+
font-weight: 850;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@media print {
|
|
232
|
+
body {
|
|
233
|
+
background: #ffffff;
|
|
234
|
+
padding: 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.page {
|
|
238
|
+
max-width: none;
|
|
239
|
+
min-height: 100vh;
|
|
240
|
+
box-shadow: none;
|
|
241
|
+
border-radius: 0;
|
|
242
|
+
padding: 20px 22px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
h1 {
|
|
246
|
+
font-size: 30px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.subtitle {
|
|
250
|
+
font-size: 15px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
table {
|
|
254
|
+
font-size: 13px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
thead th,
|
|
258
|
+
tbody td {
|
|
259
|
+
padding: 10px 10px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.summary {
|
|
263
|
+
margin: 16px 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.footer {
|
|
267
|
+
margin-top: 14px;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@media (max-width: 900px) {
|
|
272
|
+
body {
|
|
273
|
+
padding: 14px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.page {
|
|
277
|
+
padding: 24px 18px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.header,
|
|
281
|
+
.summary,
|
|
282
|
+
.footer {
|
|
283
|
+
grid-template-columns: 1fr;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.price-card {
|
|
287
|
+
justify-self: stretch;
|
|
288
|
+
max-width: none;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
table {
|
|
292
|
+
display: block;
|
|
293
|
+
overflow-x: auto;
|
|
294
|
+
white-space: normal;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
</style>
|
|
298
|
+
</head>
|
|
299
|
+
<body>
|
|
300
|
+
<main class="page">
|
|
301
|
+
<section class="header">
|
|
302
|
+
<div>
|
|
303
|
+
<div class="eyebrow">4节课 · 每节一个作品 · 课后继续迭代</div>
|
|
304
|
+
<h1>AI学习小工具创造营</h1>
|
|
305
|
+
<p class="subtitle">
|
|
306
|
+
孩子借助AI完成4个可展示、可使用的小工具,把AI从“会聊天”变成“能帮助学习和表达”的实际能力。
|
|
307
|
+
</p>
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
<aside class="price-card">
|
|
311
|
+
<div class="price-title">课程价格</div>
|
|
312
|
+
<p class="price">600元 / 节</p>
|
|
313
|
+
<p class="price-note">
|
|
314
|
+
共4节课,总价2400元。每节课完成一个作品,课后至少迭代一个版本。
|
|
315
|
+
</p>
|
|
316
|
+
</aside>
|
|
317
|
+
</section>
|
|
318
|
+
|
|
319
|
+
<section class="summary">
|
|
320
|
+
<div class="summary-item">
|
|
321
|
+
<div class="summary-label">课程目标</div>
|
|
322
|
+
<div class="summary-value">做出自己的AI学习成长系统</div>
|
|
323
|
+
</div>
|
|
324
|
+
<div class="summary-item">
|
|
325
|
+
<div class="summary-label">学习方式</div>
|
|
326
|
+
<div class="summary-value">项目制创作 + AI协作 + 作品迭代</div>
|
|
327
|
+
</div>
|
|
328
|
+
<div class="summary-item">
|
|
329
|
+
<div class="summary-label">最终成果</div>
|
|
330
|
+
<div class="summary-value">4个作品链接 + 4次课后升级记录</div>
|
|
331
|
+
</div>
|
|
332
|
+
</section>
|
|
333
|
+
|
|
334
|
+
<table>
|
|
335
|
+
<thead>
|
|
336
|
+
<tr>
|
|
337
|
+
<th>课次</th>
|
|
338
|
+
<th>项目作品</th>
|
|
339
|
+
<th>课堂完成内容</th>
|
|
340
|
+
<th>重点能力</th>
|
|
341
|
+
<th>课后迭代</th>
|
|
342
|
+
<th>家长能看到什么</th>
|
|
343
|
+
</tr>
|
|
344
|
+
</thead>
|
|
345
|
+
<tbody>
|
|
346
|
+
<tr>
|
|
347
|
+
<td class="lesson">第1节</td>
|
|
348
|
+
<td>
|
|
349
|
+
<div class="project">AI单词卡片PK系统</div>
|
|
350
|
+
<span class="tag">英语学习工具</span>
|
|
351
|
+
</td>
|
|
352
|
+
<td>
|
|
353
|
+
制作一个单词卡片工具:正面是单词,背面是AI配图、解释和记忆提示;支持录入新单词;后半节加入PK、奖励和积分规则。
|
|
354
|
+
</td>
|
|
355
|
+
<td>把学习内容变成可交互卡片;用AI生成解释、图片和记忆线索。</td>
|
|
356
|
+
<td class="iteration">
|
|
357
|
+
<strong>新增10个单词</strong>,并调整一种奖励规则。
|
|
358
|
+
</td>
|
|
359
|
+
<td class="outcome">孩子做出一个可以真实背单词的小工具。</td>
|
|
360
|
+
</tr>
|
|
361
|
+
|
|
362
|
+
<tr>
|
|
363
|
+
<td class="lesson">第2节</td>
|
|
364
|
+
<td>
|
|
365
|
+
<div class="project">学习激励系统设计</div>
|
|
366
|
+
<span class="tag">学习习惯工具</span>
|
|
367
|
+
</td>
|
|
368
|
+
<td>
|
|
369
|
+
设计一套帮助自己坚持学习的反馈系统:每日目标、完成记录、进步提示、成就徽章、学习等级、阶段奖励等,并接入前一节作品。
|
|
370
|
+
</td>
|
|
371
|
+
<td>理解学习习惯背后的结构:目标、记录、反馈、鼓励和长期坚持。</td>
|
|
372
|
+
<td class="iteration">
|
|
373
|
+
给单词卡片增加一种<strong>学习激励机制</strong>,例如连续学习记录、进步提醒或阶段成就。
|
|
374
|
+
</td>
|
|
375
|
+
<td class="outcome">
|
|
376
|
+
孩子开始理解:好工具不只是能用,还要让人更愿意坚持使用。
|
|
377
|
+
</td>
|
|
378
|
+
</tr>
|
|
379
|
+
|
|
380
|
+
<tr>
|
|
381
|
+
<td class="lesson">第3节</td>
|
|
382
|
+
<td>
|
|
383
|
+
<div class="project">每日任务记事本</div>
|
|
384
|
+
<span class="tag">作业整理工具</span>
|
|
385
|
+
</td>
|
|
386
|
+
<td>
|
|
387
|
+
孩子口头描述今天作业,AI整理成清晰步骤;任务自动记录到数据库;前端显示成今日任务界面,完成后获得反馈。
|
|
388
|
+
</td>
|
|
389
|
+
<td>把模糊任务说清楚;让AI拆解流程;把真实作业变成可执行步骤。</td>
|
|
390
|
+
<td class="iteration">
|
|
391
|
+
用它记录一天真实作业,并完成一次<strong>任务打卡</strong>。
|
|
392
|
+
</td>
|
|
393
|
+
<td class="outcome">
|
|
394
|
+
孩子能把每天作业整理成清晰步骤,降低拖延和启动难度。
|
|
395
|
+
</td>
|
|
396
|
+
</tr>
|
|
397
|
+
|
|
398
|
+
<tr>
|
|
399
|
+
<td class="lesson">第4节</td>
|
|
400
|
+
<td>
|
|
401
|
+
<div class="project">知识答题大闯关</div>
|
|
402
|
+
<span class="tag">学科理解工具</span>
|
|
403
|
+
</td>
|
|
404
|
+
<td>
|
|
405
|
+
输入一个学不明白的知识点或题目,AI进行引导讲解;记录每日收获;一键生成相关选择题,用答题形式检查理解。
|
|
406
|
+
</td>
|
|
407
|
+
<td>
|
|
408
|
+
学会向AI提问、追问和复盘;把不会的内容转成讲解、记录和自测。
|
|
409
|
+
</td>
|
|
410
|
+
<td class="iteration">
|
|
411
|
+
选择一个真实学科问题,生成一组<strong>5题小测验</strong>。
|
|
412
|
+
</td>
|
|
413
|
+
<td class="outcome">
|
|
414
|
+
孩子能用AI辅助理解知识,并把理解结果变成可检查的练习。
|
|
415
|
+
</td>
|
|
416
|
+
</tr>
|
|
417
|
+
</tbody>
|
|
418
|
+
</table>
|
|
419
|
+
|
|
420
|
+
<section class="footer">
|
|
421
|
+
<div class="note-box">
|
|
422
|
+
<h3>对家长的核心承诺</h3>
|
|
423
|
+
<p>
|
|
424
|
+
每节课不是听概念,而是完成一个真实作品;每个作品都要求课后继续升级,让孩子经历“做出来
|
|
425
|
+
→ 用起来 → 改一版”的完整过程。
|
|
426
|
+
</p>
|
|
427
|
+
</div>
|
|
428
|
+
<div class="note-box">
|
|
429
|
+
<h3>课程边界</h3>
|
|
430
|
+
<p>
|
|
431
|
+
AI负责辅助表达、拆解、讲解和生成练习;老师会引导孩子判断结果是否合理,避免盲目依赖AI。
|
|
432
|
+
</p>
|
|
433
|
+
</div>
|
|
434
|
+
</section>
|
|
435
|
+
</main>
|
|
436
|
+
</body>
|
|
437
|
+
</html>
|
|
@@ -460,10 +460,10 @@
|
|
|
460
460
|
|
|
461
461
|
// 黑名单:隐藏以下文件(__XXX__ 格式的内部文件以及系统文件),豁免 student-view.html
|
|
462
462
|
var ARTIFACT_BLACKLIST = [
|
|
463
|
-
'__demo__.html',
|
|
464
463
|
'__student__.json',
|
|
465
464
|
'__teacher__.json',
|
|
466
465
|
'__teacher-view__.html',
|
|
466
|
+
'__任务__.html',
|
|
467
467
|
'USER.md',
|
|
468
468
|
'TOOLS.md',
|
|
469
469
|
'SOUL.md',
|
|
@@ -499,6 +499,14 @@
|
|
|
499
499
|
});
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
+
// 统计重名文件名
|
|
503
|
+
var fileNameCount = {};
|
|
504
|
+
sorted.forEach(function (asset) {
|
|
505
|
+
var p = asset.path || asset.name || '';
|
|
506
|
+
var base = p.split('/').filter(Boolean).pop() || p;
|
|
507
|
+
fileNameCount[base] = (fileNameCount[base] || 0) + 1;
|
|
508
|
+
});
|
|
509
|
+
|
|
502
510
|
sorted.forEach(function (asset, idx) {
|
|
503
511
|
// 状态判断:基于 last_open 时间戳
|
|
504
512
|
// - 无 last_open → [最新](从未点开过)
|
|
@@ -632,10 +640,18 @@
|
|
|
632
640
|
|
|
633
641
|
var fullPath = asset.path || asset.name || '未命名';
|
|
634
642
|
var parts = fullPath.split('/').filter(function (p) { return p !== ''; });
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
643
|
+
var baseName = parts[parts.length - 1] || fullPath;
|
|
644
|
+
var isDuplicated = (fileNameCount[baseName] || 0) > 1;
|
|
645
|
+
// 重名时:下行 = 父目录/文件名,上行 = 再上层路径
|
|
646
|
+
// 无重名:下行 = 文件名,上行 = 完整目录路径
|
|
647
|
+
var fileName, dirPath;
|
|
648
|
+
if (isDuplicated && parts.length >= 2) {
|
|
649
|
+
fileName = parts.slice(parts.length - 2).join('/');
|
|
650
|
+
dirPath = parts.length >= 3 ? parts.slice(0, parts.length - 2).join('/') + '/' : '';
|
|
651
|
+
} else {
|
|
652
|
+
fileName = baseName;
|
|
653
|
+
dirPath = parts.length >= 2 ? parts.slice(0, parts.length - 1).join('/') + '/' : '';
|
|
654
|
+
}
|
|
639
655
|
|
|
640
656
|
// 上行:相对路径(灰色极小字)
|
|
641
657
|
if (dirPath) {
|
|
@@ -2355,9 +2371,8 @@
|
|
|
2355
2371
|
|
|
2356
2372
|
var deployBtn = document.createElement('button');
|
|
2357
2373
|
deployBtn.textContent = '⬇创建 -> 训练场';
|
|
2358
|
-
deployBtn.
|
|
2359
|
-
deployBtn.
|
|
2360
|
-
deployBtn.onmouseleave = function () { deployBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
|
2374
|
+
deployBtn.disabled = true;
|
|
2375
|
+
deployBtn.style.cssText = _btnBase + 'background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);color:rgba(205,214,244,0.3);cursor:not-allowed;';
|
|
2361
2376
|
|
|
2362
2377
|
var copyLocalBtn = document.createElement('button');
|
|
2363
2378
|
copyLocalBtn.textContent = '⬇创建 -> 新伙伴';
|
|
@@ -2382,9 +2397,9 @@
|
|
|
2382
2397
|
var footerStatus = document.createElement('span');
|
|
2383
2398
|
footerStatus.style.cssText = 'font-size:11px;color:rgba(205,214,244,0.45);flex:1;';
|
|
2384
2399
|
|
|
2385
|
-
rightFooter.appendChild(deployBtn);
|
|
2386
2400
|
rightFooter.appendChild(copyLocalBtn);
|
|
2387
2401
|
rightFooter.appendChild(promptBtn);
|
|
2402
|
+
rightFooter.appendChild(deployBtn);
|
|
2388
2403
|
rightFooter.appendChild(footerStatus);
|
|
2389
2404
|
|
|
2390
2405
|
// currentTpl 在 setActive 时更新,按钮 onclick 通过闭包读取
|
|
@@ -27,10 +27,13 @@ const DEFAULT_MINIMAX_KEY = "sk-cp-DC5lWd2Stt9CBFzLIT2awP4K-ZEn5AkYwjl3Cdj-mIBmg
|
|
|
27
27
|
function run(cliArgs) {
|
|
28
28
|
// 解析参数
|
|
29
29
|
let apiKey = null;
|
|
30
|
+
let forceDefault = false;
|
|
30
31
|
for (let i = 0; i < cliArgs.length; i++) {
|
|
31
32
|
if (cliArgs[i] === '--key' && cliArgs[i + 1]) {
|
|
32
33
|
apiKey = cliArgs[i + 1];
|
|
33
34
|
i++;
|
|
35
|
+
} else if (cliArgs[i] === '--force') {
|
|
36
|
+
forceDefault = true;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -88,9 +91,14 @@ function run(cliArgs) {
|
|
|
88
91
|
// ─── 3. 默认聊天模型 ───
|
|
89
92
|
if (!config.agents) config.agents = {};
|
|
90
93
|
if (!config.agents.defaults) config.agents.defaults = {};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
if (forceDefault) {
|
|
95
|
+
config.agents.defaults.model = {
|
|
96
|
+
primary: "minimax/MiniMax-M2.7-highspeed"
|
|
97
|
+
};
|
|
98
|
+
console.log('⚡ --force: 已覆盖默认模型为 minimax/MiniMax-M2.7-highspeed');
|
|
99
|
+
} else {
|
|
100
|
+
console.log('⊘ 跳过默认模型设置 (传入 --force 可覆盖)');
|
|
101
|
+
}
|
|
94
102
|
|
|
95
103
|
// ─── 4. 白名单,清理残留 ───
|
|
96
104
|
if (!config.agents.defaults.models) config.agents.defaults.models = {};
|
package/package.json
CHANGED
package/server/sync_workspace.py
CHANGED
|
@@ -65,6 +65,9 @@ HISTORY_FILENAME = "history.json"
|
|
|
65
65
|
_rollback_suppressed: dict = {}
|
|
66
66
|
_ROLLBACK_SUPPRESS_TTL = 3.0 # 秒
|
|
67
67
|
|
|
68
|
+
# 时间窗口合并:同一文件在此时间窗口内的多次变更只保留最新快照(覆盖,不新增版本)
|
|
69
|
+
_HISTORY_MERGE_WINDOW = 20.0 # 秒
|
|
70
|
+
|
|
68
71
|
|
|
69
72
|
def _is_rollback_suppressed(abs_path: str) -> bool:
|
|
70
73
|
ts = _rollback_suppressed.get(abs_path)
|
|
@@ -94,36 +97,70 @@ def save_history_version(workspace_id: str, relative_path: str, file_path: str):
|
|
|
94
97
|
|
|
95
98
|
history_data = _load_history_json(history_json_path, workspace_id)
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
# 检查该文件最后一次快照是否在合并窗口内
|
|
101
|
+
now_ts = time.time()
|
|
102
|
+
records = history_data.get("records", [])
|
|
103
|
+
last_record = next(
|
|
104
|
+
(r for r in reversed(records) if r.get("path") == relative_path),
|
|
105
|
+
None
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if last_record:
|
|
109
|
+
try:
|
|
110
|
+
last_ts = datetime.fromisoformat(last_record["snapshot_at"]).timestamp()
|
|
111
|
+
except Exception:
|
|
112
|
+
last_ts = 0
|
|
113
|
+
within_window = (now_ts - last_ts) < _HISTORY_MERGE_WINDOW
|
|
114
|
+
else:
|
|
115
|
+
within_window = False
|
|
99
116
|
|
|
100
|
-
# 复制当前文件内容到 __history__/vN/<relative_path>
|
|
101
117
|
rel_parts = relative_path.replace("\\", "/").split("/")
|
|
102
|
-
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
103
|
-
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
104
|
-
try:
|
|
105
|
-
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
106
|
-
dst.write(src.read())
|
|
107
|
-
except Exception as e:
|
|
108
|
-
print(f"[history] 快照写入失败: {e}")
|
|
109
|
-
return
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
if within_window:
|
|
120
|
+
# 覆盖最后一次快照,不新增版本
|
|
121
|
+
ver_dir_name = last_record["version_dir"]
|
|
122
|
+
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
123
|
+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
124
|
+
try:
|
|
125
|
+
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
126
|
+
dst.write(src.read())
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"[history] 快照覆盖失败: {e}")
|
|
129
|
+
return
|
|
130
|
+
# 更新该 record 的时间戳
|
|
131
|
+
last_record["snapshot_at"] = now_iso()
|
|
132
|
+
with open(history_json_path, "w", encoding="utf-8") as f:
|
|
133
|
+
json.dump(history_data, f, ensure_ascii=False, indent=2)
|
|
134
|
+
print(f"[history] 合并覆盖快照 {ver_dir_name}: {relative_path}")
|
|
135
|
+
else:
|
|
136
|
+
# 超出时间窗口,新增版本
|
|
137
|
+
next_ver = history_data.get("next_version", 1)
|
|
138
|
+
ver_dir_name = f"v{next_ver}"
|
|
122
139
|
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
141
|
+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
142
|
+
try:
|
|
143
|
+
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
144
|
+
dst.write(src.read())
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(f"[history] 快照写入失败: {e}")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
history_data["next_version"] = next_ver + 1
|
|
150
|
+
history_data["records"].append({
|
|
151
|
+
"version": next_ver,
|
|
152
|
+
"version_dir": ver_dir_name,
|
|
153
|
+
"path": relative_path,
|
|
154
|
+
"snapshot_at": now_iso()
|
|
155
|
+
})
|
|
156
|
+
if "current_versions" not in history_data:
|
|
157
|
+
history_data["current_versions"] = {}
|
|
158
|
+
history_data["current_versions"][relative_path] = next_ver
|
|
159
|
+
|
|
160
|
+
with open(history_json_path, "w", encoding="utf-8") as f:
|
|
161
|
+
json.dump(history_data, f, ensure_ascii=False, indent=2)
|
|
125
162
|
|
|
126
|
-
|
|
163
|
+
print(f"[history] 已保存快照 {ver_dir_name}: {relative_path}")
|
|
127
164
|
|
|
128
165
|
|
|
129
166
|
def rollback_to_version(workspace_id: str, relative_path: str, version_dir: str):
|
|
@@ -42,13 +42,19 @@ def main():
|
|
|
42
42
|
data = json.loads(input_path.read_text(encoding='utf-8'))
|
|
43
43
|
template = template_path.read_text(encoding='utf-8')
|
|
44
44
|
|
|
45
|
+
folder = input_path.parent
|
|
46
|
+
if (folder / '__任务__.html').exists():
|
|
47
|
+
preview_file = '__任务__.html'
|
|
48
|
+
else:
|
|
49
|
+
preview_file = data.get('示例页面文件', '__demo__.html')
|
|
50
|
+
|
|
45
51
|
rendered = (template
|
|
46
52
|
.replace('{{任务标题}}', esc(data.get('任务标题', '未命名任务')))
|
|
47
53
|
.replace('{{一句话说明}}', esc(data.get('一句话说明', '')))
|
|
48
54
|
.replace('{{步骤区域}}', render_steps(data.get('步骤', [])))
|
|
49
55
|
.replace('{{评价目标区域}}', render_goals(data.get('评价目标', [])))
|
|
50
56
|
.replace('{{示例区标题}}', esc(data.get('示例区标题', '示范样例')))
|
|
51
|
-
.replace('{{示例页面文件}}', esc(
|
|
57
|
+
.replace('{{示例页面文件}}', esc(preview_file))
|
|
52
58
|
.replace('{{小提示区域}}', render_tips(data.get('小提示', [])))
|
|
53
59
|
)
|
|
54
60
|
|