@aiyiran/myclaw 1.1.37 → 1.1.39
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.
|
@@ -2208,7 +2208,15 @@
|
|
|
2208
2208
|
syncStatus.style.cssText = 'font-size:11px;color:rgba(205,214,244,0.4);transition:opacity 0.3s;';
|
|
2209
2209
|
syncStatus.textContent = '⟳ 检查更新...';
|
|
2210
2210
|
|
|
2211
|
+
var headSyncBtn = document.createElement('button');
|
|
2212
|
+
headSyncBtn.textContent = '↺';
|
|
2213
|
+
headSyncBtn.title = '检查并同步最新模板';
|
|
2214
|
+
headSyncBtn.style.cssText = 'padding:3px 9px;background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.15);border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;cursor:pointer;transition:all 0.15s;line-height:1;';
|
|
2215
|
+
headSyncBtn.onmouseenter = function () { headSyncBtn.style.background = 'rgba(255,255,255,0.14)'; };
|
|
2216
|
+
headSyncBtn.onmouseleave = function () { headSyncBtn.style.background = 'rgba(255,255,255,0.07)'; };
|
|
2217
|
+
|
|
2211
2218
|
headLeft.appendChild(headTitle);
|
|
2219
|
+
headLeft.appendChild(headSyncBtn);
|
|
2212
2220
|
headLeft.appendChild(syncStatus);
|
|
2213
2221
|
|
|
2214
2222
|
var headClose = document.createElement('span');
|
|
@@ -2387,6 +2395,17 @@
|
|
|
2387
2395
|
var infoSpan = document.createElement('span');
|
|
2388
2396
|
infoSpan.style.cssText = 'font-size:12px;color:#cdd6f4;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
|
|
2389
2397
|
infoSpan.textContent = tpl['系列'] + tpl['编号'] + ' ' + tpl['名称'];
|
|
2398
|
+
// 新窗口打开按钮
|
|
2399
|
+
var openNewBtn = document.createElement('button');
|
|
2400
|
+
openNewBtn.textContent = '↗';
|
|
2401
|
+
openNewBtn.title = '在新标签页打开';
|
|
2402
|
+
openNewBtn.style.cssText = 'flex-shrink:0;padding:5px 10px;background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.15);border-radius:4px;color:#cdd6f4;font-size:14px;font-family:monospace;cursor:pointer;transition:all 0.15s;white-space:nowrap;line-height:1;';
|
|
2403
|
+
openNewBtn.onmouseenter = function () { openNewBtn.style.background = 'rgba(255,255,255,0.14)'; };
|
|
2404
|
+
openNewBtn.onmouseleave = function () { openNewBtn.style.background = 'rgba(255,255,255,0.07)'; };
|
|
2405
|
+
openNewBtn.onclick = function () {
|
|
2406
|
+
if (previewIframe.src) window.open(previewIframe.src, '_blank');
|
|
2407
|
+
};
|
|
2408
|
+
|
|
2390
2409
|
var useBtn = document.createElement('button');
|
|
2391
2410
|
useBtn.textContent = '✨ 使用模板';
|
|
2392
2411
|
useBtn.style.cssText = 'flex-shrink:0;padding:5px 14px;background:#a78bfa;border:none;border-radius:4px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;transition:background 0.15s;white-space:nowrap;';
|
|
@@ -2401,9 +2420,11 @@
|
|
|
2401
2420
|
});
|
|
2402
2421
|
};
|
|
2403
2422
|
rightHeader.appendChild(infoSpan);
|
|
2423
|
+
rightHeader.appendChild(openNewBtn);
|
|
2404
2424
|
rightHeader.appendChild(useBtn);
|
|
2405
2425
|
previewIframe.src = MYCLAW_API_BASE + '/api/file?path='
|
|
2406
|
-
+ encodeURIComponent(TEMPLATE_ROOT + '/templates/' + tpl['文件夹名'] + '/__student-view__.html')
|
|
2426
|
+
+ encodeURIComponent(TEMPLATE_ROOT + '/templates/' + tpl['文件夹名'] + '/__student-view__.html')
|
|
2427
|
+
+ '&t=' + Date.now();
|
|
2407
2428
|
}
|
|
2408
2429
|
|
|
2409
2430
|
row.onclick = setActive;
|
|
@@ -2442,27 +2463,56 @@
|
|
|
2442
2463
|
}
|
|
2443
2464
|
|
|
2444
2465
|
// ── 并行:加载本地 index + 检查 CDN 更新 ──────────────────────────────
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
.
|
|
2448
|
-
.
|
|
2449
|
-
|
|
2466
|
+
function doSync() {
|
|
2467
|
+
headSyncBtn.disabled = true;
|
|
2468
|
+
headSyncBtn.style.opacity = '0.5';
|
|
2469
|
+
syncStatus.style.color = 'rgba(205,214,244,0.4)';
|
|
2470
|
+
syncStatus.textContent = '⟳ 检查更新...';
|
|
2471
|
+
fetch(MYCLAW_API_BASE + '/api/sync-templates')
|
|
2472
|
+
.then(function (r) { return r.json(); })
|
|
2473
|
+
.then(function (sync) {
|
|
2474
|
+
headSyncBtn.disabled = false;
|
|
2475
|
+
headSyncBtn.style.opacity = '1';
|
|
2476
|
+
if (!sync.changed) {
|
|
2477
|
+
syncStatus.textContent = '已是最新';
|
|
2478
|
+
setTimeout(function () { syncStatus.textContent = ''; }, 2000);
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
if (sync.syncing) {
|
|
2482
|
+
syncStatus.textContent = '⟳ 同步中...';
|
|
2483
|
+
setTimeout(function () {
|
|
2484
|
+
fetch(MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(TEMPLATE_ROOT + '/template-index.json'))
|
|
2485
|
+
.then(function (r) { return r.json(); })
|
|
2486
|
+
.then(function (newIndex) {
|
|
2487
|
+
renderTemplateList(flattenTemplates(newIndex));
|
|
2488
|
+
syncStatus.textContent = '✓ 已更新';
|
|
2489
|
+
syncStatus.style.color = '#10b981';
|
|
2490
|
+
setTimeout(function () { syncStatus.textContent = ''; }, 3000);
|
|
2491
|
+
})
|
|
2492
|
+
.catch(function () { syncStatus.textContent = ''; });
|
|
2493
|
+
}, 5000);
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
var msg = '';
|
|
2497
|
+
if (sync.added && sync.added.length) msg += '新增 ' + sync.added.length + ' 个';
|
|
2498
|
+
if (sync.updated && sync.updated.length) msg += (msg ? ',' : '') + '更新 ' + sync.updated.length + ' 个';
|
|
2499
|
+
syncStatus.textContent = '✓ ' + (msg || '已更新');
|
|
2500
|
+
syncStatus.style.color = '#10b981';
|
|
2501
|
+
fetch(MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(TEMPLATE_ROOT + '/template-index.json'))
|
|
2502
|
+
.then(function (r) { return r.json(); })
|
|
2503
|
+
.then(function (newIndex) { renderTemplateList(flattenTemplates(newIndex)); })
|
|
2504
|
+
.catch(function () {});
|
|
2505
|
+
setTimeout(function () { syncStatus.textContent = ''; }, 4000);
|
|
2506
|
+
})
|
|
2507
|
+
.catch(function () {
|
|
2508
|
+
headSyncBtn.disabled = false;
|
|
2509
|
+
headSyncBtn.style.opacity = '1';
|
|
2450
2510
|
syncStatus.textContent = '';
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
syncStatus.textContent = '✓ ' + msg;
|
|
2457
|
-
syncStatus.style.color = '#10b981';
|
|
2458
|
-
// 重新加载列表
|
|
2459
|
-
fetch(MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(TEMPLATE_ROOT + '/template-index.json'))
|
|
2460
|
-
.then(function (r) { return r.json(); })
|
|
2461
|
-
.then(function (newIndex) { renderTemplateList(flattenTemplates(newIndex)); })
|
|
2462
|
-
.catch(function () {});
|
|
2463
|
-
setTimeout(function () { syncStatus.textContent = ''; }, 4000);
|
|
2464
|
-
})
|
|
2465
|
-
.catch(function () { syncStatus.textContent = ''; });
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
headSyncBtn.onclick = doSync;
|
|
2515
|
+
doSync();
|
|
2466
2516
|
|
|
2467
2517
|
fetch(MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(TEMPLATE_ROOT + '/template-index.json'))
|
|
2468
2518
|
.then(function (r) {
|
package/package.json
CHANGED
package/server/sync_workspace.py
CHANGED
|
@@ -475,6 +475,92 @@ def sync_all(workspace_id):
|
|
|
475
475
|
return True
|
|
476
476
|
|
|
477
477
|
|
|
478
|
+
# ═══════════════════════════════════════════════════════════════
|
|
479
|
+
# 模板同步:从 CDN 增量下载到本地
|
|
480
|
+
# ═══════════════════════════════════════════════════════════════
|
|
481
|
+
|
|
482
|
+
_sync_state = {'running': False, 'last_result': None}
|
|
483
|
+
_sync_lock = threading.Lock()
|
|
484
|
+
|
|
485
|
+
_SYNC_LOG = os.path.join(get_openclaw_path(), 'sync-templates.log')
|
|
486
|
+
|
|
487
|
+
def _sync_log(msg):
|
|
488
|
+
line = '[{}] {}\n'.format(datetime.now(timezone.utc).strftime('%H:%M:%S'), msg)
|
|
489
|
+
print(line, end='')
|
|
490
|
+
try:
|
|
491
|
+
with open(_SYNC_LOG, 'a', encoding='utf-8') as f:
|
|
492
|
+
f.write(line)
|
|
493
|
+
except Exception:
|
|
494
|
+
pass
|
|
495
|
+
|
|
496
|
+
TEMPLATE_CDN_PREFIX_BASE = 'myclaw/live/yiran/skills/yiran-playground-template-use/templates'
|
|
497
|
+
TEMPLATE_LOCAL_DIR = os.path.join(get_openclaw_path(), 'skills', 'yiran-playground-template-use', 'templates')
|
|
498
|
+
TEMPLATE_LOCAL_INDEX = os.path.join(get_openclaw_path(), 'skills', 'yiran-playground-template-use', 'template-index.json')
|
|
499
|
+
TEMPLATE_CDN_INDEX_URL = 'https://cdn.yiranlaoshi.com/myclaw/live/yiran/skills/yiran-playground-template-use/template-index.json'
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _do_sync_templates(cdn_index, local_index):
|
|
503
|
+
"""后台线程:按 folder 全量下载有变化的模板文件"""
|
|
504
|
+
try:
|
|
505
|
+
cdn_templates = cdn_index.get('templates', {})
|
|
506
|
+
local_templates = local_index.get('templates', {})
|
|
507
|
+
added = []
|
|
508
|
+
updated = []
|
|
509
|
+
|
|
510
|
+
_sync_log('开始同步,CDN 模板系列: {}'.format(list(cdn_templates.keys())))
|
|
511
|
+
|
|
512
|
+
for series, nums in cdn_templates.items():
|
|
513
|
+
for num, record in nums.items():
|
|
514
|
+
folder_name = record.get('文件夹名', '')
|
|
515
|
+
cdn_ua = record.get('updated_at', '')
|
|
516
|
+
local_ua = local_templates.get(series, {}).get(num, {}).get('updated_at', '')
|
|
517
|
+
|
|
518
|
+
if not folder_name:
|
|
519
|
+
continue
|
|
520
|
+
|
|
521
|
+
is_new = series not in local_templates or num not in local_templates.get(series, {})
|
|
522
|
+
is_changed = not is_new and cdn_ua != local_ua
|
|
523
|
+
|
|
524
|
+
if not is_new and not is_changed:
|
|
525
|
+
_sync_log('跳过 {} (无变化)'.format(folder_name))
|
|
526
|
+
continue
|
|
527
|
+
|
|
528
|
+
_sync_log('{} {} → 下载中...'.format('新增' if is_new else '更新', folder_name))
|
|
529
|
+
cdn_prefix = TEMPLATE_CDN_PREFIX_BASE + '/{}/'.format(folder_name)
|
|
530
|
+
folder_dir = os.path.join(TEMPLATE_LOCAL_DIR, folder_name)
|
|
531
|
+
os.makedirs(folder_dir, exist_ok=True)
|
|
532
|
+
cdn_files = _list_files_from_qiniu(cdn_prefix)
|
|
533
|
+
_sync_log(' 列举到 {} 个文件'.format(len(cdn_files)))
|
|
534
|
+
for item in cdn_files:
|
|
535
|
+
key = item.get('key', '')
|
|
536
|
+
filename = key[len(cdn_prefix):]
|
|
537
|
+
if not filename:
|
|
538
|
+
continue
|
|
539
|
+
ok = _download_from_cdn(key, os.path.join(folder_dir, filename))
|
|
540
|
+
_sync_log(' {} {}'.format('✓' if ok else '✗', filename))
|
|
541
|
+
|
|
542
|
+
if is_new:
|
|
543
|
+
added.append(folder_name)
|
|
544
|
+
else:
|
|
545
|
+
updated.append(folder_name)
|
|
546
|
+
|
|
547
|
+
# 覆盖本地 index
|
|
548
|
+
os.makedirs(os.path.dirname(TEMPLATE_LOCAL_INDEX), exist_ok=True)
|
|
549
|
+
with open(TEMPLATE_LOCAL_INDEX, 'w', encoding='utf-8') as f:
|
|
550
|
+
json.dump(cdn_index, f, ensure_ascii=False, indent=2)
|
|
551
|
+
|
|
552
|
+
_sync_log('完成。新增: {}, 更新: {}'.format(added, updated))
|
|
553
|
+
with _sync_lock:
|
|
554
|
+
_sync_state['running'] = False
|
|
555
|
+
_sync_state['last_result'] = {'changed': True, 'added': added, 'updated': updated}
|
|
556
|
+
|
|
557
|
+
except Exception as e:
|
|
558
|
+
import traceback
|
|
559
|
+
_sync_log('异常: {}\n{}'.format(e, traceback.format_exc()))
|
|
560
|
+
with _sync_lock:
|
|
561
|
+
_sync_state['running'] = False
|
|
562
|
+
|
|
563
|
+
|
|
478
564
|
# ═══════════════════════════════════════════════════════════════
|
|
479
565
|
# Fork:从 CDN 克隆他人 workspace 到本地
|
|
480
566
|
# ═══════════════════════════════════════════════════════════════
|
|
@@ -1105,22 +1191,17 @@ class MyclawAPIHandler(BaseHTTPRequestHandler):
|
|
|
1105
1191
|
|
|
1106
1192
|
def _handle_sync_templates(self):
|
|
1107
1193
|
"""GET /api/sync-templates
|
|
1108
|
-
|
|
1109
|
-
|
|
1194
|
+
快速比对 CDN index_updated_at 与本地,若一致立即返回 {changed: false}。
|
|
1195
|
+
若有变化,启动后台线程下载,立即返回 {changed: true, syncing: true}。
|
|
1110
1196
|
"""
|
|
1111
|
-
|
|
1112
|
-
CDN_TEMPLATE_BASE = 'https://cdn.yiranlaoshi.com/myclaw/live/yiran/skills/yiran-playground-template-use/templates'
|
|
1113
|
-
|
|
1114
|
-
local_templates_dir = os.path.join(
|
|
1115
|
-
get_openclaw_path(), 'skills', 'yiran-playground-template-use', 'templates'
|
|
1116
|
-
)
|
|
1117
|
-
local_index_path = os.path.join(
|
|
1118
|
-
get_openclaw_path(), 'skills', 'yiran-playground-template-use', 'template-index.json'
|
|
1119
|
-
)
|
|
1120
|
-
|
|
1197
|
+
# 快速拉取 CDN index(仅 JSON,通常 <1s)
|
|
1121
1198
|
try:
|
|
1122
|
-
|
|
1123
|
-
|
|
1199
|
+
import ssl
|
|
1200
|
+
ctx = ssl.create_default_context()
|
|
1201
|
+
ctx.check_hostname = False
|
|
1202
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
1203
|
+
url_with_ts = TEMPLATE_CDN_INDEX_URL + '?t=' + str(int(time.time()))
|
|
1204
|
+
with urllib.request.urlopen(url_with_ts, timeout=10, context=ctx) as resp:
|
|
1124
1205
|
cdn_index = json.loads(resp.read().decode('utf-8'))
|
|
1125
1206
|
except Exception as e:
|
|
1126
1207
|
self._send_json({'error': 'CDN index 获取失败: ' + str(e)}, 502)
|
|
@@ -1128,9 +1209,9 @@ class MyclawAPIHandler(BaseHTTPRequestHandler):
|
|
|
1128
1209
|
|
|
1129
1210
|
# 读取本地 index
|
|
1130
1211
|
local_index = {}
|
|
1131
|
-
if os.path.isfile(
|
|
1212
|
+
if os.path.isfile(TEMPLATE_LOCAL_INDEX):
|
|
1132
1213
|
try:
|
|
1133
|
-
with open(
|
|
1214
|
+
with open(TEMPLATE_LOCAL_INDEX, 'r', encoding='utf-8') as f:
|
|
1134
1215
|
local_index = json.load(f)
|
|
1135
1216
|
except Exception:
|
|
1136
1217
|
pass
|
|
@@ -1142,55 +1223,21 @@ class MyclawAPIHandler(BaseHTTPRequestHandler):
|
|
|
1142
1223
|
self._send_json({'changed': False})
|
|
1143
1224
|
return
|
|
1144
1225
|
|
|
1145
|
-
#
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
is_new = series not in local_templates or num not in local_templates.get(series, {})
|
|
1161
|
-
is_changed = not is_new and cdn_ua != local_ua
|
|
1162
|
-
|
|
1163
|
-
if not is_new and not is_changed:
|
|
1164
|
-
continue
|
|
1165
|
-
|
|
1166
|
-
# 从七牛列举该文件夹下所有文件,全量下载
|
|
1167
|
-
cdn_prefix = 'myclaw/live/yiran/skills/yiran-playground-template-use/templates/{}/'.format(folder_name)
|
|
1168
|
-
folder_dir = os.path.join(local_templates_dir, folder_name)
|
|
1169
|
-
os.makedirs(folder_dir, exist_ok=True)
|
|
1170
|
-
cdn_files = _list_files_from_qiniu(cdn_prefix)
|
|
1171
|
-
for item in cdn_files:
|
|
1172
|
-
key = item.get('key', '')
|
|
1173
|
-
filename = key[len(cdn_prefix):]
|
|
1174
|
-
if not filename:
|
|
1175
|
-
continue
|
|
1176
|
-
dest = os.path.join(folder_dir, filename)
|
|
1177
|
-
_download_from_cdn(key, dest)
|
|
1178
|
-
|
|
1179
|
-
if is_new:
|
|
1180
|
-
added.append(folder_name)
|
|
1181
|
-
else:
|
|
1182
|
-
updated.append(folder_name)
|
|
1183
|
-
|
|
1184
|
-
# 覆盖本地 index
|
|
1185
|
-
try:
|
|
1186
|
-
os.makedirs(os.path.dirname(local_index_path), exist_ok=True)
|
|
1187
|
-
with open(local_index_path, 'w', encoding='utf-8') as f:
|
|
1188
|
-
json.dump(cdn_index, f, ensure_ascii=False, indent=2)
|
|
1189
|
-
except Exception as e:
|
|
1190
|
-
self._send_json({'error': 'index 写入失败: ' + str(e)}, 500)
|
|
1191
|
-
return
|
|
1192
|
-
|
|
1193
|
-
self._send_json({'changed': True, 'added': added, 'updated': updated})
|
|
1226
|
+
# 有变化 → 启动后台线程,立即返回
|
|
1227
|
+
with _sync_lock:
|
|
1228
|
+
if _sync_state['running']:
|
|
1229
|
+
self._send_json({'changed': True, 'syncing': True})
|
|
1230
|
+
return
|
|
1231
|
+
_sync_state['running'] = True
|
|
1232
|
+
_sync_state['last_result'] = None
|
|
1233
|
+
|
|
1234
|
+
t = threading.Thread(
|
|
1235
|
+
target=_do_sync_templates,
|
|
1236
|
+
args=(cdn_index, local_index),
|
|
1237
|
+
daemon=True,
|
|
1238
|
+
)
|
|
1239
|
+
t.start()
|
|
1240
|
+
self._send_json({'changed': True, 'syncing': True})
|
|
1194
1241
|
|
|
1195
1242
|
def log_message(self, format, *args):
|
|
1196
1243
|
# 静默日志,避免轮询刷屏
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"index_updated_at": "2026-04-
|
|
2
|
+
"index_updated_at": "2026-04-22T06:16:49Z",
|
|
3
3
|
"templates": {
|
|
4
4
|
"A": {
|
|
5
5
|
"100": {
|
|
@@ -27,14 +27,6 @@
|
|
|
27
27
|
"表达构建",
|
|
28
28
|
"视听表达"
|
|
29
29
|
],
|
|
30
|
-
"files": [
|
|
31
|
-
"__demo__.html",
|
|
32
|
-
"__student-view__.html",
|
|
33
|
-
"__student__.json",
|
|
34
|
-
"__teacher-view__.html",
|
|
35
|
-
"__teacher__.json",
|
|
36
|
-
"index.html"
|
|
37
|
-
],
|
|
38
30
|
"updated_at": "2026-04-22T04:32:31Z"
|
|
39
31
|
},
|
|
40
32
|
"101": {
|
|
@@ -61,13 +53,6 @@
|
|
|
61
53
|
"表达构建",
|
|
62
54
|
"视听表达"
|
|
63
55
|
],
|
|
64
|
-
"files": [
|
|
65
|
-
"__demo__.html",
|
|
66
|
-
"__student-view__.html",
|
|
67
|
-
"__student__.json",
|
|
68
|
-
"__teacher-view__.html",
|
|
69
|
-
"__teacher__.json"
|
|
70
|
-
],
|
|
71
56
|
"updated_at": "2026-04-22T04:32:32Z"
|
|
72
57
|
},
|
|
73
58
|
"102": {
|
|
@@ -94,26 +79,6 @@
|
|
|
94
79
|
"第二能力:表达构建",
|
|
95
80
|
"视听表达"
|
|
96
81
|
],
|
|
97
|
-
"files": [
|
|
98
|
-
"__demo__.html",
|
|
99
|
-
"__student-view__.html",
|
|
100
|
-
"__student__.json",
|
|
101
|
-
"__teacher-view__.html",
|
|
102
|
-
"__teacher__.json",
|
|
103
|
-
"index.html",
|
|
104
|
-
"tmpsz_k5dxa.png",
|
|
105
|
-
"努力学习的小朋友.png",
|
|
106
|
-
"可爱机甲.png",
|
|
107
|
-
"小组吉祥物第一版_1024x1024_20260421_173654.png",
|
|
108
|
-
"小组吉祥物第二版-中等_1024x1024_20260421_175052.png",
|
|
109
|
-
"小组图标第一版-中等-微调_1024x1024_20260421_184340.png",
|
|
110
|
-
"小组图标第一版-中等_1024x1024_20260421_180057.png",
|
|
111
|
-
"小组徽章第一版-简单_1024x1024_20260421_184717.png",
|
|
112
|
-
"小组标志第一版-中等_1024x1024_20260421_175402.png",
|
|
113
|
-
"搞笑小朋友.png",
|
|
114
|
-
"搞笑小朋友_即梦.png",
|
|
115
|
-
"班级小组徽章Logo优化版_1024x1024_20260421_185141.png"
|
|
116
|
-
],
|
|
117
82
|
"updated_at": "2026-04-22T04:47:01Z"
|
|
118
83
|
},
|
|
119
84
|
"103": {
|
|
@@ -140,13 +105,6 @@
|
|
|
140
105
|
"表达构建",
|
|
141
106
|
"视听表达"
|
|
142
107
|
],
|
|
143
|
-
"files": [
|
|
144
|
-
"__demo__.html",
|
|
145
|
-
"__student-view__.html",
|
|
146
|
-
"__student__.json",
|
|
147
|
-
"__teacher-view__.html",
|
|
148
|
-
"__teacher__.json"
|
|
149
|
-
],
|
|
150
108
|
"updated_at": "2026-04-22T04:32:34Z"
|
|
151
109
|
},
|
|
152
110
|
"104": {
|
|
@@ -175,17 +133,7 @@
|
|
|
175
133
|
"迭代推进",
|
|
176
134
|
"游戏机制"
|
|
177
135
|
],
|
|
178
|
-
"
|
|
179
|
-
"__demo__.html",
|
|
180
|
-
"__student-view__.html",
|
|
181
|
-
"__student__.json",
|
|
182
|
-
"__teacher-view__.html",
|
|
183
|
-
"__teacher__.json",
|
|
184
|
-
"bg_music.mp3",
|
|
185
|
-
"index.html",
|
|
186
|
-
"炫酷机甲_1024x1024_20260416_125127.png"
|
|
187
|
-
],
|
|
188
|
-
"updated_at": "2026-04-22T04:32:35Z"
|
|
136
|
+
"updated_at": "2026-04-22T06:16:45Z"
|
|
189
137
|
}
|
|
190
138
|
},
|
|
191
139
|
"B": {
|
|
@@ -213,13 +161,6 @@
|
|
|
213
161
|
"表达构建",
|
|
214
162
|
"游戏机制"
|
|
215
163
|
],
|
|
216
|
-
"files": [
|
|
217
|
-
"__demo__.html",
|
|
218
|
-
"__student-view__.html",
|
|
219
|
-
"__student__.json",
|
|
220
|
-
"__teacher-view__.html",
|
|
221
|
-
"__teacher__.json"
|
|
222
|
-
],
|
|
223
164
|
"updated_at": "2026-04-22T04:32:36Z"
|
|
224
165
|
}
|
|
225
166
|
},
|
|
@@ -250,18 +191,6 @@
|
|
|
250
191
|
"问题判断",
|
|
251
192
|
"改编优化"
|
|
252
193
|
],
|
|
253
|
-
"files": [
|
|
254
|
-
"__demo__.html",
|
|
255
|
-
"__student-view__.html",
|
|
256
|
-
"__student__.json",
|
|
257
|
-
"__teacher-view__.html",
|
|
258
|
-
"__teacher__.json",
|
|
259
|
-
"demo.html",
|
|
260
|
-
"index.html",
|
|
261
|
-
"小组吉祥物_26.png",
|
|
262
|
-
"小组图形标志_83.png",
|
|
263
|
-
"班级小组徽章_47.png"
|
|
264
|
-
],
|
|
265
194
|
"updated_at": "2026-04-22T04:32:36Z"
|
|
266
195
|
}
|
|
267
196
|
}
|