@aiyiran/myclaw 1.1.65 → 1.1.67
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 +124 -43
- package/assets/myclaw-inject.js +5 -1
- package/index.js +3 -3
- package/package.json +1 -1
- 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 +36 -36
- package/skills/yiran-course-template-pipeline/template-index.md +26 -26
- 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
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
leftGroup.appendChild(titleSpan);
|
|
214
214
|
|
|
215
215
|
var eyeBtn = document.createElement('span');
|
|
216
|
-
var updateEyeUI = function() {
|
|
216
|
+
var updateEyeUI = function () {
|
|
217
217
|
if (showHiddenArtifacts) {
|
|
218
218
|
eyeBtn.textContent = '👁';
|
|
219
219
|
eyeBtn.style.opacity = '1';
|
|
@@ -228,9 +228,9 @@
|
|
|
228
228
|
};
|
|
229
229
|
eyeBtn.style.cssText = 'cursor:pointer;border-radius:3px;padding:2px 4px;transition:all 0.15s;font-size:13px;';
|
|
230
230
|
updateEyeUI();
|
|
231
|
-
eyeBtn.onmouseenter = function() { eyeBtn.style.background = 'rgba(255,255,255,0.1)'; };
|
|
232
|
-
eyeBtn.onmouseleave = function() { eyeBtn.style.background = 'none'; };
|
|
233
|
-
eyeBtn.onclick = function() {
|
|
231
|
+
eyeBtn.onmouseenter = function () { eyeBtn.style.background = 'rgba(255,255,255,0.1)'; };
|
|
232
|
+
eyeBtn.onmouseleave = function () { eyeBtn.style.background = 'none'; };
|
|
233
|
+
eyeBtn.onclick = function () {
|
|
234
234
|
showHiddenArtifacts = !showHiddenArtifacts;
|
|
235
235
|
updateEyeUI();
|
|
236
236
|
var contentEl = document.querySelector('#myclaw-artifacts-content');
|
|
@@ -249,19 +249,19 @@
|
|
|
249
249
|
searchBoxHeader.id = 'myclaw-search-box';
|
|
250
250
|
searchBoxHeader.placeholder = '搜索...';
|
|
251
251
|
searchBoxHeader.style.cssText = 'max-width:120px;height:22px;padding:2px 8px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:4px;color:#cdd6f4;font-size:11px;font-family:monospace;box-sizing:border-box;';
|
|
252
|
-
searchBoxHeader.onkeyup = function() {
|
|
252
|
+
searchBoxHeader.onkeyup = function () {
|
|
253
253
|
applySearch();
|
|
254
254
|
};
|
|
255
255
|
header.appendChild(searchBoxHeader);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// 搜索过滤函数
|
|
259
|
-
window.applySearch = function() {
|
|
259
|
+
window.applySearch = function () {
|
|
260
260
|
var searchBox = document.querySelector('#myclaw-search-box');
|
|
261
261
|
if (!searchBox || !searchBox.value) return;
|
|
262
262
|
var keyword = searchBox.value.toLowerCase();
|
|
263
263
|
var items = document.querySelectorAll('[data-artifact-item]');
|
|
264
|
-
items.forEach(function(item) {
|
|
264
|
+
items.forEach(function (item) {
|
|
265
265
|
var fileName = item.getAttribute('data-artifact-name') || '';
|
|
266
266
|
item.style.display = (keyword === '' || fileName.toLowerCase().indexOf(keyword) !== -1) ? 'flex' : 'none';
|
|
267
267
|
});
|
|
@@ -478,33 +478,33 @@
|
|
|
478
478
|
'TOOLS.md',
|
|
479
479
|
'SOUL.md',
|
|
480
480
|
'MEMORY.md',
|
|
481
|
-
'IDENTITY.md',
|
|
482
481
|
'HEARTBEAT.md',
|
|
483
482
|
'BOOTSTRAP.md',
|
|
484
483
|
'AGENTS.md'
|
|
485
484
|
];
|
|
486
|
-
|
|
485
|
+
|
|
487
486
|
// 黑名单目录:隐藏特定的系统目录
|
|
488
487
|
var DIR_BLACKLIST = [
|
|
489
488
|
'memory/',
|
|
490
|
-
'.openclaw/'
|
|
489
|
+
'.openclaw/',
|
|
490
|
+
'.git/'
|
|
491
491
|
];
|
|
492
492
|
|
|
493
493
|
if (!showHiddenArtifacts) {
|
|
494
494
|
sorted = sorted.filter(function (asset) {
|
|
495
495
|
var path = asset.path || asset.name || '';
|
|
496
496
|
var basename = path.split('/').pop();
|
|
497
|
-
|
|
497
|
+
|
|
498
498
|
// 1. 检查文件名是否包含在黑名单中
|
|
499
499
|
if (ARTIFACT_BLACKLIST.indexOf(basename) !== -1) return false;
|
|
500
|
-
|
|
500
|
+
|
|
501
501
|
// 2. 检查路径是否以黑名单目录开头
|
|
502
502
|
for (var i = 0; i < DIR_BLACKLIST.length; i++) {
|
|
503
503
|
if (path.indexOf(DIR_BLACKLIST[i]) === 0) {
|
|
504
504
|
return false;
|
|
505
505
|
}
|
|
506
506
|
}
|
|
507
|
-
|
|
507
|
+
|
|
508
508
|
return true;
|
|
509
509
|
});
|
|
510
510
|
}
|
|
@@ -641,7 +641,7 @@
|
|
|
641
641
|
fname.style.cssText = 'flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:2px;';
|
|
642
642
|
|
|
643
643
|
var fullPath = asset.path || asset.name || '未命名';
|
|
644
|
-
var parts = fullPath.split('/').filter(function(p) { return p !== ''; });
|
|
644
|
+
var parts = fullPath.split('/').filter(function (p) { return p !== ''; });
|
|
645
645
|
// 上行:倒数第二层以上的路径(若只有一层目录则无上行)
|
|
646
646
|
// 下行:直接父文件夹/文件名
|
|
647
647
|
var topPart = '';
|
|
@@ -674,6 +674,22 @@
|
|
|
674
674
|
|
|
675
675
|
row.appendChild(leftCol);
|
|
676
676
|
row.appendChild(fname);
|
|
677
|
+
|
|
678
|
+
// HTML 文件:右侧显示源码查看图标
|
|
679
|
+
var assetExtForSrc = (asset.path || '').split('.').pop().toLowerCase();
|
|
680
|
+
if (assetExtForSrc === 'html' || assetExtForSrc === 'htm') {
|
|
681
|
+
var srcIcon = document.createElement('span');
|
|
682
|
+
srcIcon.textContent = '</>';
|
|
683
|
+
srcIcon.title = '查看源代码';
|
|
684
|
+
srcIcon.style.cssText = 'flex-shrink:0;align-self:center;color:#555;font-size:11px;font-family:monospace;cursor:pointer;padding:2px 5px;border-radius:3px;border:1px solid transparent;transition:color 0.15s,border-color 0.15s,background 0.15s;margin-left:4px;white-space:nowrap;';
|
|
685
|
+
srcIcon.onmouseenter = function () { srcIcon.style.color = '#a78bfa'; srcIcon.style.borderColor = 'rgba(167,139,250,0.4)'; srcIcon.style.background = 'rgba(167,139,250,0.08)'; };
|
|
686
|
+
srcIcon.onmouseleave = function () { srcIcon.style.color = '#555'; srcIcon.style.borderColor = 'transparent'; srcIcon.style.background = ''; };
|
|
687
|
+
srcIcon.onclick = (function (a) {
|
|
688
|
+
return function (e) { e.stopPropagation(); openSourceModal(a); };
|
|
689
|
+
}(asset));
|
|
690
|
+
row.appendChild(srcIcon);
|
|
691
|
+
}
|
|
692
|
+
|
|
677
693
|
listContent.appendChild(row);
|
|
678
694
|
});
|
|
679
695
|
|
|
@@ -878,6 +894,71 @@
|
|
|
878
894
|
if (modal) modal.remove();
|
|
879
895
|
}
|
|
880
896
|
|
|
897
|
+
// ═══ 源代码查看弹框 ═══
|
|
898
|
+
function openSourceModal(asset) {
|
|
899
|
+
if (document.querySelector('#myclaw-source-modal')) return;
|
|
900
|
+
|
|
901
|
+
var overlay = document.createElement('div');
|
|
902
|
+
overlay.id = 'myclaw-source-modal';
|
|
903
|
+
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;font-family:monospace;';
|
|
904
|
+
overlay.onclick = function (e) { if (e.target === overlay) overlay.remove(); };
|
|
905
|
+
|
|
906
|
+
var box = document.createElement('div');
|
|
907
|
+
box.style.cssText = 'background:#1e1e2e;border:1px solid rgba(255,255,255,0.1);border-radius:8px;width:calc(100vw - 40px);height:calc(100vh - 40px);display:flex;flex-direction:column;color:#cdd6f4;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,0.6);';
|
|
908
|
+
|
|
909
|
+
var head = document.createElement('div');
|
|
910
|
+
head.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-bottom:1px solid rgba(255,255,255,0.1);flex-shrink:0;';
|
|
911
|
+
|
|
912
|
+
var headTitle = document.createElement('span');
|
|
913
|
+
headTitle.textContent = '</> ' + (asset.path || '');
|
|
914
|
+
headTitle.style.cssText = 'font-size:13px;color:#cdd6f4;';
|
|
915
|
+
|
|
916
|
+
var headRight = document.createElement('span');
|
|
917
|
+
headRight.style.cssText = 'display:flex;align-items:center;gap:10px;';
|
|
918
|
+
|
|
919
|
+
var copyBtn = document.createElement('span');
|
|
920
|
+
copyBtn.textContent = '复制';
|
|
921
|
+
copyBtn.style.cssText = 'cursor:pointer;padding:4px 10px;border-radius:4px;font-size:12px;background:rgba(255,255,255,0.08);transition:background 0.15s;';
|
|
922
|
+
copyBtn.onmouseenter = function () { copyBtn.style.background = 'rgba(255,255,255,0.18)'; };
|
|
923
|
+
copyBtn.onmouseleave = function () { copyBtn.style.background = 'rgba(255,255,255,0.08)'; };
|
|
924
|
+
|
|
925
|
+
var closeBtn = document.createElement('span');
|
|
926
|
+
closeBtn.textContent = '✕';
|
|
927
|
+
closeBtn.style.cssText = 'cursor:pointer;padding:4px 10px;border-radius:4px;font-size:18px;font-weight:bold;transition:background 0.15s;';
|
|
928
|
+
closeBtn.onmouseenter = function () { closeBtn.style.background = 'rgba(255,255,255,0.15)'; };
|
|
929
|
+
closeBtn.onmouseleave = function () { closeBtn.style.background = 'none'; };
|
|
930
|
+
closeBtn.onclick = function () { overlay.remove(); };
|
|
931
|
+
|
|
932
|
+
headRight.appendChild(copyBtn);
|
|
933
|
+
headRight.appendChild(closeBtn);
|
|
934
|
+
head.appendChild(headTitle);
|
|
935
|
+
head.appendChild(headRight);
|
|
936
|
+
box.appendChild(head);
|
|
937
|
+
|
|
938
|
+
var pre = document.createElement('pre');
|
|
939
|
+
pre.style.cssText = 'flex:1;margin:0;padding:16px;overflow:auto;font-size:12px;line-height:1.6;color:#cdd6f4;background:#1a1a2e;white-space:pre;';
|
|
940
|
+
pre.textContent = '加载中…';
|
|
941
|
+
|
|
942
|
+
var apiUrl = MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(getWorkspaceId() + '/' + asset.path) + '&t=' + Date.now();
|
|
943
|
+
fetch(apiUrl)
|
|
944
|
+
.then(function (r) { return r.ok ? r.arrayBuffer() : Promise.reject('HTTP ' + r.status); })
|
|
945
|
+
.then(function (buf) {
|
|
946
|
+
var text = new TextDecoder('utf-8').decode(buf);
|
|
947
|
+
pre.textContent = text;
|
|
948
|
+
copyBtn.onclick = function () {
|
|
949
|
+
navigator.clipboard.writeText(text).then(function () {
|
|
950
|
+
copyBtn.textContent = '✓ 已复制';
|
|
951
|
+
setTimeout(function () { copyBtn.textContent = '复制'; }, 1500);
|
|
952
|
+
});
|
|
953
|
+
};
|
|
954
|
+
})
|
|
955
|
+
.catch(function (e) { pre.textContent = '加载失败: ' + e; });
|
|
956
|
+
|
|
957
|
+
box.appendChild(pre);
|
|
958
|
+
overlay.appendChild(box);
|
|
959
|
+
document.body.appendChild(overlay);
|
|
960
|
+
}
|
|
961
|
+
|
|
881
962
|
// ═══ 历史记录弹框 ═══
|
|
882
963
|
function openHistoryModal(defaultPath) {
|
|
883
964
|
if (document.querySelector('#myclaw-history-modal')) return;
|
|
@@ -1199,21 +1280,21 @@
|
|
|
1199
1280
|
mTitleTime.style.cssText = 'font-size:11px;color:#888;font-weight:normal;';
|
|
1200
1281
|
function updateStatsTime() {
|
|
1201
1282
|
var now = new Date();
|
|
1202
|
-
var pad = function(n){return n<10?'0'+n:n;};
|
|
1203
|
-
mTitleTime.textContent = now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(now.getDate())+' '+pad(now.getHours())+':'+pad(now.getMinutes())+':'+pad(now.getSeconds());
|
|
1283
|
+
var pad = function (n) { return n < 10 ? '0' + n : n; };
|
|
1284
|
+
mTitleTime.textContent = now.getFullYear() + '-' + pad(now.getMonth() + 1) + '-' + pad(now.getDate()) + ' ' + pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds());
|
|
1204
1285
|
}
|
|
1205
1286
|
updateStatsTime();
|
|
1206
1287
|
var statsTimerInterval = setInterval(updateStatsTime, 1000);
|
|
1207
|
-
overlay.addEventListener('remove', function(){ clearInterval(statsTimerInterval); }, {once:true});
|
|
1288
|
+
overlay.addEventListener('remove', function () { clearInterval(statsTimerInterval); }, { once: true });
|
|
1208
1289
|
// 监听 overlay 被移除时清除 timer
|
|
1209
|
-
var statsTimerObserver = new MutationObserver(function(mutations){
|
|
1210
|
-
mutations.forEach(function(m){
|
|
1211
|
-
m.removedNodes.forEach(function(n){
|
|
1212
|
-
if(n === overlay){ clearInterval(statsTimerInterval); statsTimerObserver.disconnect(); }
|
|
1290
|
+
var statsTimerObserver = new MutationObserver(function (mutations) {
|
|
1291
|
+
mutations.forEach(function (m) {
|
|
1292
|
+
m.removedNodes.forEach(function (n) {
|
|
1293
|
+
if (n === overlay) { clearInterval(statsTimerInterval); statsTimerObserver.disconnect(); }
|
|
1213
1294
|
});
|
|
1214
1295
|
});
|
|
1215
1296
|
});
|
|
1216
|
-
statsTimerObserver.observe(document.body, {childList:true});
|
|
1297
|
+
statsTimerObserver.observe(document.body, { childList: true });
|
|
1217
1298
|
mTitle.appendChild(mTitleText);
|
|
1218
1299
|
mTitle.appendChild(mTitleTime);
|
|
1219
1300
|
var mClose = document.createElement('span');
|
|
@@ -1259,7 +1340,7 @@
|
|
|
1259
1340
|
function openJsonPreviewModal(title, jsonUrl, sortKey) {
|
|
1260
1341
|
var jOverlay = document.createElement('div');
|
|
1261
1342
|
jOverlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:100000;display:flex;align-items:center;justify-content:center;';
|
|
1262
|
-
jOverlay.onclick = function(e){ if(e.target===jOverlay) jOverlay.remove(); };
|
|
1343
|
+
jOverlay.onclick = function (e) { if (e.target === jOverlay) jOverlay.remove(); };
|
|
1263
1344
|
|
|
1264
1345
|
var jBox = document.createElement('div');
|
|
1265
1346
|
jBox.style.cssText = 'background:#1e1e2e;border-radius:10px;width:min(94vw,700px);height:min(80vh,600px);overflow:hidden;box-shadow:0 16px 48px rgba(0,0,0,0.8);font-family:monospace;color:#cdd6f4;display:flex;flex-direction:column;';
|
|
@@ -1271,9 +1352,9 @@
|
|
|
1271
1352
|
var jClose = document.createElement('span');
|
|
1272
1353
|
jClose.textContent = '✕';
|
|
1273
1354
|
jClose.style.cssText = 'cursor:pointer;padding:2px 6px;border-radius:3px;font-size:14px;transition:background 0.15s;';
|
|
1274
|
-
jClose.onmouseenter = function(){ jClose.style.background='rgba(255,255,255,0.1)'; };
|
|
1275
|
-
jClose.onmouseleave = function(){ jClose.style.background='none'; };
|
|
1276
|
-
jClose.onclick = function(){ jOverlay.remove(); };
|
|
1355
|
+
jClose.onmouseenter = function () { jClose.style.background = 'rgba(255,255,255,0.1)'; };
|
|
1356
|
+
jClose.onmouseleave = function () { jClose.style.background = 'none'; };
|
|
1357
|
+
jClose.onclick = function () { jOverlay.remove(); };
|
|
1277
1358
|
jHead.appendChild(jTitle);
|
|
1278
1359
|
jHead.appendChild(jClose);
|
|
1279
1360
|
|
|
@@ -1287,15 +1368,15 @@
|
|
|
1287
1368
|
document.body.appendChild(jOverlay);
|
|
1288
1369
|
|
|
1289
1370
|
fetch(jsonUrl)
|
|
1290
|
-
.then(function(r){ return r.ok ? r.json() : Promise.reject('HTTP ' + r.status); })
|
|
1291
|
-
.then(function(data){
|
|
1371
|
+
.then(function (r) { return r.ok ? r.json() : Promise.reject('HTTP ' + r.status); })
|
|
1372
|
+
.then(function (data) {
|
|
1292
1373
|
if (sortKey) {
|
|
1293
1374
|
// 支持顶层数组 或 对象内某个数组字段(如 { assets: [...] })
|
|
1294
1375
|
var arr = Array.isArray(data) ? data : (data && typeof data === 'object' ? (
|
|
1295
|
-
Object.values(data).find(function(v){ return Array.isArray(v); })
|
|
1376
|
+
Object.values(data).find(function (v) { return Array.isArray(v); })
|
|
1296
1377
|
) : null);
|
|
1297
1378
|
if (arr) {
|
|
1298
|
-
arr.sort(function(a, b){
|
|
1379
|
+
arr.sort(function (a, b) {
|
|
1299
1380
|
var ta = a[sortKey] ? new Date(a[sortKey]).getTime() : 0;
|
|
1300
1381
|
var tb = b[sortKey] ? new Date(b[sortKey]).getTime() : 0;
|
|
1301
1382
|
return tb - ta;
|
|
@@ -1305,7 +1386,7 @@
|
|
|
1305
1386
|
jBody.style.color = '#a6e3a1';
|
|
1306
1387
|
jBody.textContent = JSON.stringify(data, null, 2);
|
|
1307
1388
|
})
|
|
1308
|
-
.catch(function(err){
|
|
1389
|
+
.catch(function (err) {
|
|
1309
1390
|
jBody.style.color = '#f87171';
|
|
1310
1391
|
jBody.textContent = '加载失败: ' + err;
|
|
1311
1392
|
});
|
|
@@ -1315,9 +1396,9 @@
|
|
|
1315
1396
|
var sec1 = makeSection('📁', '文件资产');
|
|
1316
1397
|
sec1.style.cursor = 'pointer';
|
|
1317
1398
|
sec1.title = '点击查看 JSON';
|
|
1318
|
-
sec1.onmouseenter = function(){ sec1.style.background='rgba(255,255,255,0.04)'; };
|
|
1319
|
-
sec1.onmouseleave = function(){ sec1.style.background=''; };
|
|
1320
|
-
sec1.onclick = function(){
|
|
1399
|
+
sec1.onmouseenter = function () { sec1.style.background = 'rgba(255,255,255,0.04)'; };
|
|
1400
|
+
sec1.onmouseleave = function () { sec1.style.background = ''; };
|
|
1401
|
+
sec1.onclick = function () {
|
|
1321
1402
|
var wsPrefix = getWorkspaceId();
|
|
1322
1403
|
openJsonPreviewModal('📁 文件资产 — __MY_ARTIFACTS__.json', MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(wsPrefix + '/.myclaw/__MY_ARTIFACTS__.json'), 'updated_at');
|
|
1323
1404
|
};
|
|
@@ -1355,9 +1436,9 @@
|
|
|
1355
1436
|
var sec2 = makeSection('🎨', '美术资源');
|
|
1356
1437
|
sec2.style.cursor = 'pointer';
|
|
1357
1438
|
sec2.title = '点击查看 JSON';
|
|
1358
|
-
sec2.onmouseenter = function(){ sec2.style.background='rgba(255,255,255,0.04)'; };
|
|
1359
|
-
sec2.onmouseleave = function(){ sec2.style.background=''; };
|
|
1360
|
-
sec2.onclick = function(){
|
|
1439
|
+
sec2.onmouseenter = function () { sec2.style.background = 'rgba(255,255,255,0.04)'; };
|
|
1440
|
+
sec2.onmouseleave = function () { sec2.style.background = ''; };
|
|
1441
|
+
sec2.onclick = function () {
|
|
1361
1442
|
openJsonPreviewModal('🎨 美术资源 — media-generation-log.json', MYCLAW_API_BASE + '/api/file?path=myclaw/log/media-generation-log.json', 'started_at');
|
|
1362
1443
|
};
|
|
1363
1444
|
var mediaPlaceholder = document.createElement('div');
|
|
@@ -1401,8 +1482,8 @@
|
|
|
1401
1482
|
var syncBtn = document.createElement('button');
|
|
1402
1483
|
syncBtn.textContent = '全量同步';
|
|
1403
1484
|
syncBtn.style.cssText = 'padding:2px 10px;background:none;color:#555;border:1px solid rgba(255,255,255,0.1);border-radius:4px;font-size:11px;font-family:monospace;cursor:pointer;transition:color 0.15s,border-color 0.15s;';
|
|
1404
|
-
syncBtn.onmouseenter = function () { syncBtn.style.color='#aaa'; syncBtn.style.borderColor='rgba(255,255,255,0.25)'; };
|
|
1405
|
-
syncBtn.onmouseleave = function () { syncBtn.style.color='#555'; syncBtn.style.borderColor='rgba(255,255,255,0.1)'; };
|
|
1485
|
+
syncBtn.onmouseenter = function () { syncBtn.style.color = '#aaa'; syncBtn.style.borderColor = 'rgba(255,255,255,0.25)'; };
|
|
1486
|
+
syncBtn.onmouseleave = function () { syncBtn.style.color = '#555'; syncBtn.style.borderColor = 'rgba(255,255,255,0.1)'; };
|
|
1406
1487
|
syncBtn.onclick = function () {
|
|
1407
1488
|
var confirm = document.createElement('div');
|
|
1408
1489
|
confirm.style.cssText = 'margin-top:10px;padding:10px;background:rgba(250,204,21,0.08);border:1px solid rgba(250,204,21,0.2);border-radius:6px;font-size:11px;color:#facc15;line-height:1.7;';
|
|
@@ -1456,7 +1537,7 @@
|
|
|
1456
1537
|
|
|
1457
1538
|
// 读取上次发布缓存
|
|
1458
1539
|
var savedForm = {};
|
|
1459
|
-
try { savedForm = JSON.parse(localStorage.getItem('myclaw-publish-cache')) || {}; } catch (e) {}
|
|
1540
|
+
try { savedForm = JSON.parse(localStorage.getItem('myclaw-publish-cache')) || {}; } catch (e) { }
|
|
1460
1541
|
|
|
1461
1542
|
var overlay = document.createElement('div');
|
|
1462
1543
|
overlay.id = 'myclaw-artifacts-publish-modal';
|
|
@@ -1936,7 +2017,7 @@
|
|
|
1936
2017
|
}
|
|
1937
2018
|
|
|
1938
2019
|
// ═══ QR 码生成(qrcodejs 内联,无外部依赖,无 CSP 问题) ═══
|
|
1939
|
-
var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c<a.length&&0==a[c];)c++;this.num=new Array(a.length-c+b);for(var d=0;d<a.length-c;d++)this.num[d]=a[d+c]}function j(a,b){this.totalCount=a,this.dataCount=b}function k(){this.buffer=[],this.length=0}function m(){return"undefined"!=typeof CanvasRenderingContext2D}function n(){var a=!1,b=navigator.userAgent;return/android/i.test(b)&&(a=!0,aMat=b.toString().match(/android ([0-9]\.[0-9])/i),aMat&&aMat[1]&&(a=parseFloat(aMat[1]))),a}function r(a,b){for(var c=1,e=s(a),f=0,g=l.length;g>=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=new Array(this.moduleCount);for(var e=0;e<this.moduleCount;e++)this.modules[d][e]=null}this.setupPositionProbePattern(0,0),this.setupPositionProbePattern(this.moduleCount-7,0),this.setupPositionProbePattern(0,this.moduleCount-7),this.setupPositionAdjustPattern(),this.setupTimingPattern(),this.setupTypeInfo(a,c),this.typeNumber>=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f<this.modules.length;f++)for(var g=f*e,h=0;h<this.modules[f].length;h++){var i=h*e,j=this.modules[f][h];j&&(d.beginFill(0,100),d.moveTo(i,g),d.lineTo(i+e,g),d.lineTo(i+e,g+e),d.lineTo(i,g+e),d.endFill())}return d},setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(var b=8;b<this.moduleCount-8;b++)null==this.modules[6][b]&&(this.modules[6][b]=0==b%2)},setupPositionAdjustPattern:function(){for(var a=f.getPatternPosition(this.typeNumber),b=0;b<a.length;b++)for(var c=0;c<a.length;c++){var d=a[b],e=a[c];if(null==this.modules[d][e])for(var g=-2;2>=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g<a.length&&(j=1==(1&a[g]>>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h<d.length;h++){var i=d[h];g.put(i.mode,4),g.put(i.getLength(),f.getLengthInBits(i.mode,a)),i.write(g)}for(var l=0,h=0;h<e.length;h++)l+=e[h].dataCount;if(g.getLengthInBits()>8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j<b.length;j++){var k=b[j].dataCount,l=b[j].totalCount-k;d=Math.max(d,k),e=Math.max(e,l),g[j]=new Array(k);for(var m=0;m<g[j].length;m++)g[j][m]=255&a.buffer[m+c];c+=k;var n=f.getErrorCorrectPolynomial(l),o=new i(g[j],n.getLength()-1),p=o.mod(n);h[j]=new Array(n.getLength()-1);for(var m=0;m<h[j].length;m++){var q=m+p.getLength()-h[j].length;h[j][m]=q>=0?p.get(q):0}}for(var r=0,m=0;m<b.length;m++)r+=b[m].totalCount;for(var s=new Array(r),t=0,m=0;d>m;m++)for(var j=0;j<b.length;j++)m<g[j].length&&(s[t++]=g[j][m]);for(var m=0;e>m;m++)for(var j=0;j<b.length;j++)m<h[j].length&&(s[t++]=h[j][m]);return s};for(var c={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},d={L:1,M:0,Q:3,H:2},e={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},f={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var b=a<<10;f.getBCHDigit(b)-f.getBCHDigit(f.G15)>=0;)b^=f.G15<<f.getBCHDigit(b)-f.getBCHDigit(f.G15);return(a<<10|b)^f.G15_MASK},getBCHTypeNumber:function(a){for(var b=a<<12;f.getBCHDigit(b)-f.getBCHDigit(f.G18)>=0;)b^=f.G18<<f.getBCHDigit(b)-f.getBCHDigit(f.G18);return a<<12|b},getBCHDigit:function(a){for(var b=0;0!=a;)b++,a>>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<<h;for(var h=8;256>h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;c<this.getLength();c++)for(var d=0;d<a.getLength();d++)b[c+d]^=g.gexp(g.glog(this.get(c))+g.glog(a.get(d)));return new i(b,0)},mod:function(a){if(this.getLength()-a.getLength()<0)return this;for(var b=g.glog(this.get(0))-g.glog(a.get(0)),c=new Array(this.getLength()),d=0;d<this.getLength();d++)c[d]=this.get(d);for(var d=0;d<a.getLength();d++)c[d]^=g.gexp(g.glog(a.get(d))+b);return new i(c,0).mod(a)}},j.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],j.getRSBlocks=function(a,b){var c=j.getRsBlockTable(a,b);if(void 0==c)throw new Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+b);for(var d=c.length/3,e=[],f=0;d>f;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(false/*android2.1 fix disabled*/){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=['<table style="border:0;border-collapse:collapse;">'],h=0;d>h;h++){g.push("<tr>");for(var i=0;d>i;i++)g.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:'+e+"px;height:"+f+"px;background-color:"+(a.isDark(h,i)?b.colorDark:b.colorLight)+';"></td>');g.push("</tr>")}g.push("</table>"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}();
|
|
2020
|
+
var QRCode; !function () { function a(a) { this.mode = c.MODE_8BIT_BYTE, this.data = a, this.parsedData = []; for (var b = [], d = 0, e = this.data.length; e > d; d++) { var f = this.data.charCodeAt(d); f > 65536 ? (b[0] = 240 | (1835008 & f) >>> 18, b[1] = 128 | (258048 & f) >>> 12, b[2] = 128 | (4032 & f) >>> 6, b[3] = 128 | 63 & f) : f > 2048 ? (b[0] = 224 | (61440 & f) >>> 12, b[1] = 128 | (4032 & f) >>> 6, b[2] = 128 | 63 & f) : f > 128 ? (b[0] = 192 | (1984 & f) >>> 6, b[1] = 128 | 63 & f) : b[0] = f, this.parsedData = this.parsedData.concat(b) } this.parsedData.length != this.data.length && (this.parsedData.unshift(191), this.parsedData.unshift(187), this.parsedData.unshift(239)) } function b(a, b) { this.typeNumber = a, this.errorCorrectLevel = b, this.modules = null, this.moduleCount = 0, this.dataCache = null, this.dataList = [] } function i(a, b) { if (void 0 == a.length) throw new Error(a.length + "/" + b); for (var c = 0; c < a.length && 0 == a[c];)c++; this.num = new Array(a.length - c + b); for (var d = 0; d < a.length - c; d++)this.num[d] = a[d + c] } function j(a, b) { this.totalCount = a, this.dataCount = b } function k() { this.buffer = [], this.length = 0 } function m() { return "undefined" != typeof CanvasRenderingContext2D } function n() { var a = !1, b = navigator.userAgent; return /android/i.test(b) && (a = !0, aMat = b.toString().match(/android ([0-9]\.[0-9])/i), aMat && aMat[1] && (a = parseFloat(aMat[1]))), a } function r(a, b) { for (var c = 1, e = s(a), f = 0, g = l.length; g >= f; f++) { var h = 0; switch (b) { case d.L: h = l[f][0]; break; case d.M: h = l[f][1]; break; case d.Q: h = l[f][2]; break; case d.H: h = l[f][3] }if (h >= e) break; c++ } if (c > l.length) throw new Error("Too long data"); return c } function s(a) { var b = encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g, "a"); return b.length + (b.length != a ? 3 : 0) } a.prototype = { getLength: function () { return this.parsedData.length }, write: function (a) { for (var b = 0, c = this.parsedData.length; c > b; b++)a.put(this.parsedData[b], 8) } }, b.prototype = { addData: function (b) { var c = new a(b); this.dataList.push(c), this.dataCache = null }, isDark: function (a, b) { if (0 > a || this.moduleCount <= a || 0 > b || this.moduleCount <= b) throw new Error(a + "," + b); return this.modules[a][b] }, getModuleCount: function () { return this.moduleCount }, make: function () { this.makeImpl(!1, this.getBestMaskPattern()) }, makeImpl: function (a, c) { this.moduleCount = 4 * this.typeNumber + 17, this.modules = new Array(this.moduleCount); for (var d = 0; d < this.moduleCount; d++) { this.modules[d] = new Array(this.moduleCount); for (var e = 0; e < this.moduleCount; e++)this.modules[d][e] = null } this.setupPositionProbePattern(0, 0), this.setupPositionProbePattern(this.moduleCount - 7, 0), this.setupPositionProbePattern(0, this.moduleCount - 7), this.setupPositionAdjustPattern(), this.setupTimingPattern(), this.setupTypeInfo(a, c), this.typeNumber >= 7 && this.setupTypeNumber(a), null == this.dataCache && (this.dataCache = b.createData(this.typeNumber, this.errorCorrectLevel, this.dataList)), this.mapData(this.dataCache, c) }, setupPositionProbePattern: function (a, b) { for (var c = -1; 7 >= c; c++)if (!(-1 >= a + c || this.moduleCount <= a + c)) for (var d = -1; 7 >= d; d++)-1 >= b + d || this.moduleCount <= b + d || (this.modules[a + c][b + d] = c >= 0 && 6 >= c && (0 == d || 6 == d) || d >= 0 && 6 >= d && (0 == c || 6 == c) || c >= 2 && 4 >= c && d >= 2 && 4 >= d ? !0 : !1) }, getBestMaskPattern: function () { for (var a = 0, b = 0, c = 0; 8 > c; c++) { this.makeImpl(!0, c); var d = f.getLostPoint(this); (0 == c || a > d) && (a = d, b = c) } return b }, createMovieClip: function (a, b, c) { var d = a.createEmptyMovieClip(b, c), e = 1; this.make(); for (var f = 0; f < this.modules.length; f++)for (var g = f * e, h = 0; h < this.modules[f].length; h++) { var i = h * e, j = this.modules[f][h]; j && (d.beginFill(0, 100), d.moveTo(i, g), d.lineTo(i + e, g), d.lineTo(i + e, g + e), d.lineTo(i, g + e), d.endFill()) } return d }, setupTimingPattern: function () { for (var a = 8; a < this.moduleCount - 8; a++)null == this.modules[a][6] && (this.modules[a][6] = 0 == a % 2); for (var b = 8; b < this.moduleCount - 8; b++)null == this.modules[6][b] && (this.modules[6][b] = 0 == b % 2) }, setupPositionAdjustPattern: function () { for (var a = f.getPatternPosition(this.typeNumber), b = 0; b < a.length; b++)for (var c = 0; c < a.length; c++) { var d = a[b], e = a[c]; if (null == this.modules[d][e]) for (var g = -2; 2 >= g; g++)for (var h = -2; 2 >= h; h++)this.modules[d + g][e + h] = -2 == g || 2 == g || -2 == h || 2 == h || 0 == g && 0 == h ? !0 : !1 } }, setupTypeNumber: function (a) { for (var b = f.getBCHTypeNumber(this.typeNumber), c = 0; 18 > c; c++) { var d = !a && 1 == (1 & b >> c); this.modules[Math.floor(c / 3)][c % 3 + this.moduleCount - 8 - 3] = d } for (var c = 0; 18 > c; c++) { var d = !a && 1 == (1 & b >> c); this.modules[c % 3 + this.moduleCount - 8 - 3][Math.floor(c / 3)] = d } }, setupTypeInfo: function (a, b) { for (var c = this.errorCorrectLevel << 3 | b, d = f.getBCHTypeInfo(c), e = 0; 15 > e; e++) { var g = !a && 1 == (1 & d >> e); 6 > e ? this.modules[e][8] = g : 8 > e ? this.modules[e + 1][8] = g : this.modules[this.moduleCount - 15 + e][8] = g } for (var e = 0; 15 > e; e++) { var g = !a && 1 == (1 & d >> e); 8 > e ? this.modules[8][this.moduleCount - e - 1] = g : 9 > e ? this.modules[8][15 - e - 1 + 1] = g : this.modules[8][15 - e - 1] = g } this.modules[this.moduleCount - 8][8] = !a }, mapData: function (a, b) { for (var c = -1, d = this.moduleCount - 1, e = 7, g = 0, h = this.moduleCount - 1; h > 0; h -= 2)for (6 == h && h--; ;) { for (var i = 0; 2 > i; i++)if (null == this.modules[d][h - i]) { var j = !1; g < a.length && (j = 1 == (1 & a[g] >>> e)); var k = f.getMask(b, d, h - i); k && (j = !j), this.modules[d][h - i] = j, e--, -1 == e && (g++, e = 7) } if (d += c, 0 > d || this.moduleCount <= d) { d -= c, c = -c; break } } } }, b.PAD0 = 236, b.PAD1 = 17, b.createData = function (a, c, d) { for (var e = j.getRSBlocks(a, c), g = new k, h = 0; h < d.length; h++) { var i = d[h]; g.put(i.mode, 4), g.put(i.getLength(), f.getLengthInBits(i.mode, a)), i.write(g) } for (var l = 0, h = 0; h < e.length; h++)l += e[h].dataCount; if (g.getLengthInBits() > 8 * l) throw new Error("code length overflow. (" + g.getLengthInBits() + ">" + 8 * l + ")"); for (g.getLengthInBits() + 4 <= 8 * l && g.put(0, 4); 0 != g.getLengthInBits() % 8;)g.putBit(!1); for (; ;) { if (g.getLengthInBits() >= 8 * l) break; if (g.put(b.PAD0, 8), g.getLengthInBits() >= 8 * l) break; g.put(b.PAD1, 8) } return b.createBytes(g, e) }, b.createBytes = function (a, b) { for (var c = 0, d = 0, e = 0, g = new Array(b.length), h = new Array(b.length), j = 0; j < b.length; j++) { var k = b[j].dataCount, l = b[j].totalCount - k; d = Math.max(d, k), e = Math.max(e, l), g[j] = new Array(k); for (var m = 0; m < g[j].length; m++)g[j][m] = 255 & a.buffer[m + c]; c += k; var n = f.getErrorCorrectPolynomial(l), o = new i(g[j], n.getLength() - 1), p = o.mod(n); h[j] = new Array(n.getLength() - 1); for (var m = 0; m < h[j].length; m++) { var q = m + p.getLength() - h[j].length; h[j][m] = q >= 0 ? p.get(q) : 0 } } for (var r = 0, m = 0; m < b.length; m++)r += b[m].totalCount; for (var s = new Array(r), t = 0, m = 0; d > m; m++)for (var j = 0; j < b.length; j++)m < g[j].length && (s[t++] = g[j][m]); for (var m = 0; e > m; m++)for (var j = 0; j < b.length; j++)m < h[j].length && (s[t++] = h[j][m]); return s }; for (var c = { MODE_NUMBER: 1, MODE_ALPHA_NUM: 2, MODE_8BIT_BYTE: 4, MODE_KANJI: 8 }, d = { L: 1, M: 0, Q: 3, H: 2 }, e = { PATTERN000: 0, PATTERN001: 1, PATTERN010: 2, PATTERN011: 3, PATTERN100: 4, PATTERN101: 5, PATTERN110: 6, PATTERN111: 7 }, f = { PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]], G15: 1335, G18: 7973, G15_MASK: 21522, getBCHTypeInfo: function (a) { for (var b = a << 10; f.getBCHDigit(b) - f.getBCHDigit(f.G15) >= 0;)b ^= f.G15 << f.getBCHDigit(b) - f.getBCHDigit(f.G15); return (a << 10 | b) ^ f.G15_MASK }, getBCHTypeNumber: function (a) { for (var b = a << 12; f.getBCHDigit(b) - f.getBCHDigit(f.G18) >= 0;)b ^= f.G18 << f.getBCHDigit(b) - f.getBCHDigit(f.G18); return a << 12 | b }, getBCHDigit: function (a) { for (var b = 0; 0 != a;)b++, a >>>= 1; return b }, getPatternPosition: function (a) { return f.PATTERN_POSITION_TABLE[a - 1] }, getMask: function (a, b, c) { switch (a) { case e.PATTERN000: return 0 == (b + c) % 2; case e.PATTERN001: return 0 == b % 2; case e.PATTERN010: return 0 == c % 3; case e.PATTERN011: return 0 == (b + c) % 3; case e.PATTERN100: return 0 == (Math.floor(b / 2) + Math.floor(c / 3)) % 2; case e.PATTERN101: return 0 == b * c % 2 + b * c % 3; case e.PATTERN110: return 0 == (b * c % 2 + b * c % 3) % 2; case e.PATTERN111: return 0 == (b * c % 3 + (b + c) % 2) % 2; default: throw new Error("bad maskPattern:" + a) } }, getErrorCorrectPolynomial: function (a) { for (var b = new i([1], 0), c = 0; a > c; c++)b = b.multiply(new i([1, g.gexp(c)], 0)); return b }, getLengthInBits: function (a, b) { if (b >= 1 && 10 > b) switch (a) { case c.MODE_NUMBER: return 10; case c.MODE_ALPHA_NUM: return 9; case c.MODE_8BIT_BYTE: return 8; case c.MODE_KANJI: return 8; default: throw new Error("mode:" + a) } else if (27 > b) switch (a) { case c.MODE_NUMBER: return 12; case c.MODE_ALPHA_NUM: return 11; case c.MODE_8BIT_BYTE: return 16; case c.MODE_KANJI: return 10; default: throw new Error("mode:" + a) } else { if (!(41 > b)) throw new Error("type:" + b); switch (a) { case c.MODE_NUMBER: return 14; case c.MODE_ALPHA_NUM: return 13; case c.MODE_8BIT_BYTE: return 16; case c.MODE_KANJI: return 12; default: throw new Error("mode:" + a) } } }, getLostPoint: function (a) { for (var b = a.getModuleCount(), c = 0, d = 0; b > d; d++)for (var e = 0; b > e; e++) { for (var f = 0, g = a.isDark(d, e), h = -1; 1 >= h; h++)if (!(0 > d + h || d + h >= b)) for (var i = -1; 1 >= i; i++)0 > e + i || e + i >= b || (0 != h || 0 != i) && g == a.isDark(d + h, e + i) && f++; f > 5 && (c += 3 + f - 5) } for (var d = 0; b - 1 > d; d++)for (var e = 0; b - 1 > e; e++) { var j = 0; a.isDark(d, e) && j++, a.isDark(d + 1, e) && j++, a.isDark(d, e + 1) && j++, a.isDark(d + 1, e + 1) && j++, (0 == j || 4 == j) && (c += 3) } for (var d = 0; b > d; d++)for (var e = 0; b - 6 > e; e++)a.isDark(d, e) && !a.isDark(d, e + 1) && a.isDark(d, e + 2) && a.isDark(d, e + 3) && a.isDark(d, e + 4) && !a.isDark(d, e + 5) && a.isDark(d, e + 6) && (c += 40); for (var e = 0; b > e; e++)for (var d = 0; b - 6 > d; d++)a.isDark(d, e) && !a.isDark(d + 1, e) && a.isDark(d + 2, e) && a.isDark(d + 3, e) && a.isDark(d + 4, e) && !a.isDark(d + 5, e) && a.isDark(d + 6, e) && (c += 40); for (var k = 0, e = 0; b > e; e++)for (var d = 0; b > d; d++)a.isDark(d, e) && k++; var l = Math.abs(100 * k / b / b - 50) / 5; return c += 10 * l } }, g = { glog: function (a) { if (1 > a) throw new Error("glog(" + a + ")"); return g.LOG_TABLE[a] }, gexp: function (a) { for (; 0 > a;)a += 255; for (; a >= 256;)a -= 255; return g.EXP_TABLE[a] }, EXP_TABLE: new Array(256), LOG_TABLE: new Array(256) }, h = 0; 8 > h; h++)g.EXP_TABLE[h] = 1 << h; for (var h = 8; 256 > h; h++)g.EXP_TABLE[h] = g.EXP_TABLE[h - 4] ^ g.EXP_TABLE[h - 5] ^ g.EXP_TABLE[h - 6] ^ g.EXP_TABLE[h - 8]; for (var h = 0; 255 > h; h++)g.LOG_TABLE[g.EXP_TABLE[h]] = h; i.prototype = { get: function (a) { return this.num[a] }, getLength: function () { return this.num.length }, multiply: function (a) { for (var b = new Array(this.getLength() + a.getLength() - 1), c = 0; c < this.getLength(); c++)for (var d = 0; d < a.getLength(); d++)b[c + d] ^= g.gexp(g.glog(this.get(c)) + g.glog(a.get(d))); return new i(b, 0) }, mod: function (a) { if (this.getLength() - a.getLength() < 0) return this; for (var b = g.glog(this.get(0)) - g.glog(a.get(0)), c = new Array(this.getLength()), d = 0; d < this.getLength(); d++)c[d] = this.get(d); for (var d = 0; d < a.getLength(); d++)c[d] ^= g.gexp(g.glog(a.get(d)) + b); return new i(c, 0).mod(a) } }, j.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]], j.getRSBlocks = function (a, b) { var c = j.getRsBlockTable(a, b); if (void 0 == c) throw new Error("bad rs block @ typeNumber:" + a + "/errorCorrectLevel:" + b); for (var d = c.length / 3, e = [], f = 0; d > f; f++)for (var g = c[3 * f + 0], h = c[3 * f + 1], i = c[3 * f + 2], k = 0; g > k; k++)e.push(new j(h, i)); return e }, j.getRsBlockTable = function (a, b) { switch (b) { case d.L: return j.RS_BLOCK_TABLE[4 * (a - 1) + 0]; case d.M: return j.RS_BLOCK_TABLE[4 * (a - 1) + 1]; case d.Q: return j.RS_BLOCK_TABLE[4 * (a - 1) + 2]; case d.H: return j.RS_BLOCK_TABLE[4 * (a - 1) + 3]; default: return void 0 } }, k.prototype = { get: function (a) { var b = Math.floor(a / 8); return 1 == (1 & this.buffer[b] >>> 7 - a % 8) }, put: function (a, b) { for (var c = 0; b > c; c++)this.putBit(1 == (1 & a >>> b - c - 1)) }, getLengthInBits: function () { return this.length }, putBit: function (a) { var b = Math.floor(this.length / 8); this.buffer.length <= b && this.buffer.push(0), a && (this.buffer[b] |= 128 >>> this.length % 8), this.length++ } }; var l = [[17, 14, 11, 7], [32, 26, 20, 14], [53, 42, 32, 24], [78, 62, 46, 34], [106, 84, 60, 44], [134, 106, 74, 58], [154, 122, 86, 64], [192, 152, 108, 84], [230, 180, 130, 98], [271, 213, 151, 119], [321, 251, 177, 137], [367, 287, 203, 155], [425, 331, 241, 177], [458, 362, 258, 194], [520, 412, 292, 220], [586, 450, 322, 250], [644, 504, 364, 280], [718, 560, 394, 310], [792, 624, 442, 338], [858, 666, 482, 382], [929, 711, 509, 403], [1003, 779, 565, 439], [1091, 857, 611, 461], [1171, 911, 661, 511], [1273, 997, 715, 535], [1367, 1059, 751, 593], [1465, 1125, 805, 625], [1528, 1190, 868, 658], [1628, 1264, 908, 698], [1732, 1370, 982, 742], [1840, 1452, 1030, 790], [1952, 1538, 1112, 842], [2068, 1628, 1168, 898], [2188, 1722, 1228, 958], [2303, 1809, 1283, 983], [2431, 1911, 1351, 1051], [2563, 1989, 1423, 1093], [2699, 2099, 1499, 1139], [2809, 2213, 1579, 1219], [2953, 2331, 1663, 1273]], o = function () { var a = function (a, b) { this._el = a, this._htOption = b }; return a.prototype.draw = function (a) { function g(a, b) { var c = document.createElementNS("http://www.w3.org/2000/svg", a); for (var d in b) b.hasOwnProperty(d) && c.setAttribute(d, b[d]); return c } var b = this._htOption, c = this._el, d = a.getModuleCount(); Math.floor(b.width / d), Math.floor(b.height / d), this.clear(); var h = g("svg", { viewBox: "0 0 " + String(d) + " " + String(d), width: "100%", height: "100%", fill: b.colorLight }); h.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"), c.appendChild(h), h.appendChild(g("rect", { fill: b.colorDark, width: "1", height: "1", id: "template" })); for (var i = 0; d > i; i++)for (var j = 0; d > j; j++)if (a.isDark(i, j)) { var k = g("use", { x: String(i), y: String(j) }); k.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template"), h.appendChild(k) } }, a.prototype.clear = function () { for (; this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild) }, a }(), p = "svg" === document.documentElement.tagName.toLowerCase(), q = p ? o : m() ? function () { function a() { this._elImage.src = this._elCanvas.toDataURL("image/png"), this._elImage.style.display = "block", this._elCanvas.style.display = "none" } function d(a, b) { var c = this; if (c._fFail = b, c._fSuccess = a, null === c._bSupportDataURI) { var d = document.createElement("img"), e = function () { c._bSupportDataURI = !1, c._fFail && _fFail.call(c) }, f = function () { c._bSupportDataURI = !0, c._fSuccess && c._fSuccess.call(c) }; return d.onabort = e, d.onerror = e, d.onload = f, d.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", void 0 } c._bSupportDataURI === !0 && c._fSuccess ? c._fSuccess.call(c) : c._bSupportDataURI === !1 && c._fFail && c._fFail.call(c) } if (false/*android2.1 fix disabled*/) { var b = 1 / window.devicePixelRatio, c = CanvasRenderingContext2D.prototype.drawImage; CanvasRenderingContext2D.prototype.drawImage = function (a, d, e, f, g, h, i, j) { if ("nodeName" in a && /img/i.test(a.nodeName)) for (var l = arguments.length - 1; l >= 1; l--)arguments[l] = arguments[l] * b; else "undefined" == typeof j && (arguments[1] *= b, arguments[2] *= b, arguments[3] *= b, arguments[4] *= b); c.apply(this, arguments) } } var e = function (a, b) { this._bIsPainted = !1, this._android = n(), this._htOption = b, this._elCanvas = document.createElement("canvas"), this._elCanvas.width = b.width, this._elCanvas.height = b.height, a.appendChild(this._elCanvas), this._el = a, this._oContext = this._elCanvas.getContext("2d"), this._bIsPainted = !1, this._elImage = document.createElement("img"), this._elImage.style.display = "none", this._el.appendChild(this._elImage), this._bSupportDataURI = null }; return e.prototype.draw = function (a) { var b = this._elImage, c = this._oContext, d = this._htOption, e = a.getModuleCount(), f = d.width / e, g = d.height / e, h = Math.round(f), i = Math.round(g); b.style.display = "none", this.clear(); for (var j = 0; e > j; j++)for (var k = 0; e > k; k++) { var l = a.isDark(j, k), m = k * f, n = j * g; c.strokeStyle = l ? d.colorDark : d.colorLight, c.lineWidth = 1, c.fillStyle = l ? d.colorDark : d.colorLight, c.fillRect(m, n, f, g), c.strokeRect(Math.floor(m) + .5, Math.floor(n) + .5, h, i), c.strokeRect(Math.ceil(m) - .5, Math.ceil(n) - .5, h, i) } this._bIsPainted = !0 }, e.prototype.makeImage = function () { this._bIsPainted && d.call(this, a) }, e.prototype.isPainted = function () { return this._bIsPainted }, e.prototype.clear = function () { this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height), this._bIsPainted = !1 }, e.prototype.round = function (a) { return a ? Math.floor(1e3 * a) / 1e3 : a }, e }() : function () { var a = function (a, b) { this._el = a, this._htOption = b }; return a.prototype.draw = function (a) { for (var b = this._htOption, c = this._el, d = a.getModuleCount(), e = Math.floor(b.width / d), f = Math.floor(b.height / d), g = ['<table style="border:0;border-collapse:collapse;">'], h = 0; d > h; h++) { g.push("<tr>"); for (var i = 0; d > i; i++)g.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + e + "px;height:" + f + "px;background-color:" + (a.isDark(h, i) ? b.colorDark : b.colorLight) + ';"></td>'); g.push("</tr>") } g.push("</table>"), c.innerHTML = g.join(""); var j = c.childNodes[0], k = (b.width - j.offsetWidth) / 2, l = (b.height - j.offsetHeight) / 2; k > 0 && l > 0 && (j.style.margin = l + "px " + k + "px") }, a.prototype.clear = function () { this._el.innerHTML = "" }, a }(); QRCode = function (a, b) { if (this._htOption = { width: 256, height: 256, typeNumber: 4, colorDark: "#000000", colorLight: "#ffffff", correctLevel: d.H }, "string" == typeof b && (b = { text: b }), b) for (var c in b) this._htOption[c] = b[c]; "string" == typeof a && (a = document.getElementById(a)), this._android = n(), this._el = a, this._oQRCode = null, this._oDrawing = new q(this._el, this._htOption), this._htOption.text && this.makeCode(this._htOption.text) }, QRCode.prototype.makeCode = function (a) { this._oQRCode = new b(r(a, this._htOption.correctLevel), this._htOption.correctLevel), this._oQRCode.addData(a), this._oQRCode.make(), this._el.title = a, this._oDrawing.draw(this._oQRCode), this.makeImage() }, QRCode.prototype.makeImage = function () { "function" == typeof this._oDrawing.makeImage && (!this._android || this._android >= 3) && this._oDrawing.makeImage() }, QRCode.prototype.clear = function () { this._oDrawing.clear() }, QRCode.CorrectLevel = d }();
|
|
1940
2021
|
|
|
1941
2022
|
function loadAndGenerateQR(container, text, size) {
|
|
1942
2023
|
try {
|
|
@@ -2256,13 +2337,13 @@
|
|
|
2256
2337
|
var _btnMutedLeave = 'background:rgba(167,139,250,0.12);';
|
|
2257
2338
|
|
|
2258
2339
|
var deployBtn = document.createElement('button');
|
|
2259
|
-
deployBtn.textContent = '
|
|
2340
|
+
deployBtn.textContent = '⬇创建 -> 训练场';
|
|
2260
2341
|
deployBtn.style.cssText = _btnMuted;
|
|
2261
2342
|
deployBtn.onmouseenter = function () { deployBtn.style.background = 'rgba(167,139,250,0.22)'; };
|
|
2262
2343
|
deployBtn.onmouseleave = function () { deployBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
|
2263
2344
|
|
|
2264
2345
|
var copyLocalBtn = document.createElement('button');
|
|
2265
|
-
copyLocalBtn.textContent = '
|
|
2346
|
+
copyLocalBtn.textContent = '⬇创建 -> 新伙伴';
|
|
2266
2347
|
copyLocalBtn.style.cssText = _btnMuted;
|
|
2267
2348
|
copyLocalBtn.onmouseenter = function () { copyLocalBtn.style.background = 'rgba(167,139,250,0.22)'; };
|
|
2268
2349
|
copyLocalBtn.onmouseleave = function () { copyLocalBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
package/assets/myclaw-inject.js
CHANGED
|
@@ -1615,7 +1615,11 @@ btn.addEventListener("click", function () {
|
|
|
1615
1615
|
form.appendChild(lockRow);
|
|
1616
1616
|
|
|
1617
1617
|
// 批量删除伙伴弹框
|
|
1618
|
-
var MYCLAW_API_BASE_DEL =
|
|
1618
|
+
var MYCLAW_API_BASE_DEL = (function () {
|
|
1619
|
+
var h = window.location.hostname;
|
|
1620
|
+
var isRemote = h === 'claw.yiranlaoshi.com' || h.endsWith('.kekouen.cn');
|
|
1621
|
+
return isRemote ? window.location.origin + '/sync' : 'http://127.0.0.1:18800';
|
|
1622
|
+
})();
|
|
1619
1623
|
|
|
1620
1624
|
function fmtDate(ms) {
|
|
1621
1625
|
if (!ms) return '未知';
|
package/index.js
CHANGED
|
@@ -1851,7 +1851,7 @@ function runSyncTemplates() {
|
|
|
1851
1851
|
}).on('error', cb);
|
|
1852
1852
|
}
|
|
1853
1853
|
|
|
1854
|
-
console.log(colors.blue + '[
|
|
1854
|
+
console.log(colors.blue + '[muban]' + colors.nc + ' 正在检查模板更新...');
|
|
1855
1855
|
|
|
1856
1856
|
apiGet('/api/sync-templates', (err, res) => {
|
|
1857
1857
|
if (err) {
|
|
@@ -2396,7 +2396,7 @@ function showHelp() {
|
|
|
2396
2396
|
console.log(' open 打开浏览器控制台(自动带 token)');
|
|
2397
2397
|
console.log(' tui 唤起新对话上下文 (用法: mc tui <agentname>)');
|
|
2398
2398
|
console.log(' delete 批量删除 Agent(交互式勾选 + 二次确认)');
|
|
2399
|
-
console.log('
|
|
2399
|
+
console.log(' muban 更新本地课程模板(从 CDN 增量下载)');
|
|
2400
2400
|
console.log(' list-agents 列出所有 Agent(--json 输出机器可读)');
|
|
2401
2401
|
console.log(' trash-paths <p1> <p2> 将路径移入回收站(跨平台)');
|
|
2402
2402
|
console.log(' fix 兜底修复(自动补装 WSL + Chrome,仅限 Windows)');
|
|
@@ -2602,7 +2602,7 @@ if (!command) {
|
|
|
2602
2602
|
runServer(args[1]); // args[1] 是可选的 name
|
|
2603
2603
|
} else if (command === 'sync') {
|
|
2604
2604
|
runSync(args[1]); // args[1] 是可选的 workspace 名称
|
|
2605
|
-
} else if (command === '
|
|
2605
|
+
} else if (command === 'muban') {
|
|
2606
2606
|
runSyncTemplates();
|
|
2607
2607
|
} else if (command === 'init') {
|
|
2608
2608
|
console.log('init 还没实现,后续添加钩子');
|
package/package.json
CHANGED
|
@@ -55,6 +55,12 @@ description: 面向中文课堂的小专题课程模板流水线。适用于把
|
|
|
55
55
|
- move 负责把任务文件夹移动到 `yiran-playground-template-use` skill 的 `templates/`,并自动触发索引重建
|
|
56
56
|
- publish 负责把模板文件夹内所有资源上传到七牛 CDN,并重写 HTML 中的资源引用为 CDN 绝对地址(见 `cdn-convention.md`)
|
|
57
57
|
|
|
58
|
+
**推荐发布入口(内容确认后):**
|
|
59
|
+
```bash
|
|
60
|
+
python3 scripts/release_template.py <模板目录>
|
|
61
|
+
```
|
|
62
|
+
`release_template.py` 自动依次执行 build → move → publish,对新目录和已入库的 vN/ 目录均适用。
|
|
63
|
+
|
|
58
64
|
## 原则
|
|
59
65
|
|
|
60
66
|
- 先做 demo
|
|
@@ -102,6 +102,24 @@ python3 scripts/publish_template.py --all
|
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
105
|
+
## 一键发布:release_template.py(推荐)
|
|
106
|
+
|
|
107
|
+
如果已确认内容没问题,可跳过手动执行脚本2 + 脚本3,直接用一键发布脚本:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python3 scripts/release_template.py 任务文件夹
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
该脚本自动依次执行:
|
|
114
|
+
1. `build_template_views.py` — 生成 HTML 视图(Step 1)
|
|
115
|
+
2. `move_template_task.py` — 创建新版本目录 vN+1/(Step 2)
|
|
116
|
+
3. `publish_template.py` — 上传 CDN + 上传索引(Step 3)
|
|
117
|
+
|
|
118
|
+
> **注意:** build 阶段仍可单独运行(用于调试预览),脚本1无需通过 release 脚本触发。
|
|
119
|
+
> release_template.py 对已入库的 vN/ 目录同样适用,会自动产出 vN+1/。
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
105
123
|
## 原则
|
|
106
124
|
|
|
107
125
|
1. 先在当前工作目录完成中间产物
|
|
@@ -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,22 +158,22 @@
|
|
|
158
158
|
"表达构建",
|
|
159
159
|
"游戏机制"
|
|
160
160
|
],
|
|
161
|
-
"updated_at": "2026-04-
|
|
161
|
+
"updated_at": "2026-04-22T17:16:00Z"
|
|
162
162
|
},
|
|
163
163
|
"8a7e39dd": {
|
|
164
164
|
"id": "8a7e39dd",
|
|
165
165
|
"系列": "B",
|
|
166
|
-
"编号": "
|
|
166
|
+
"编号": "101",
|
|
167
167
|
"名称": "会跳舞的机器人",
|
|
168
|
-
"文件夹名": "
|
|
169
|
-
"version": "
|
|
170
|
-
"路径": "templates/
|
|
171
|
-
"入口文件": "templates/
|
|
168
|
+
"文件夹名": "b101_会跳舞的机器人",
|
|
169
|
+
"version": "v2",
|
|
170
|
+
"路径": "templates/b101_会跳舞的机器人/v2",
|
|
171
|
+
"入口文件": "templates/b101_会跳舞的机器人/v2/index.html",
|
|
172
172
|
"一句话说明": "先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。",
|
|
173
173
|
"主能力标签": "表达构建",
|
|
174
174
|
"任务类型标签": "视听表达",
|
|
175
175
|
"关键词": [
|
|
176
|
-
"
|
|
176
|
+
"b101_会跳舞的机器人",
|
|
177
177
|
"先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。",
|
|
178
178
|
"已经能开始动手,但还不太会把多种素材组织到一起的学生",
|
|
179
179
|
"会做简单网页,但还不太会处理视频、音乐和动画配合的学生",
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
"表达构建",
|
|
187
187
|
"视听表达"
|
|
188
188
|
],
|
|
189
|
-
"updated_at": "2026-04-22T16:
|
|
189
|
+
"updated_at": "2026-04-22T16:52:16Z"
|
|
190
190
|
},
|
|
191
191
|
"6490af0a": {
|
|
192
192
|
"id": "6490af0a",
|
|
@@ -194,9 +194,9 @@
|
|
|
194
194
|
"编号": "100",
|
|
195
195
|
"名称": "给小组作品做一次小修改",
|
|
196
196
|
"文件夹名": "c100_给小组作品做一次小修改",
|
|
197
|
-
"version": "
|
|
198
|
-
"路径": "templates/c100_给小组作品做一次小修改/
|
|
199
|
-
"入口文件": "templates/c100_给小组作品做一次小修改/
|
|
197
|
+
"version": "v2",
|
|
198
|
+
"路径": "templates/c100_给小组作品做一次小修改/v2",
|
|
199
|
+
"入口文件": "templates/c100_给小组作品做一次小修改/v2/index.html",
|
|
200
200
|
"一句话说明": "先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。",
|
|
201
201
|
"主能力标签": "问题判断",
|
|
202
202
|
"任务类型标签": "改编优化",
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
"问题判断",
|
|
215
215
|
"改编优化"
|
|
216
216
|
],
|
|
217
|
-
"updated_at": "2026-04-
|
|
217
|
+
"updated_at": "2026-04-22T17:16:03Z"
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
}
|
|
@@ -4,72 +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
|
-
##
|
|
59
|
+
## B101 会跳舞的机器人
|
|
60
60
|
- ID:8a7e39dd
|
|
61
|
-
- 版本:
|
|
62
|
-
- 路径:templates/
|
|
63
|
-
- 入口文件:templates/
|
|
61
|
+
- 版本:v2
|
|
62
|
+
- 路径:templates/b101_会跳舞的机器人/v2
|
|
63
|
+
- 入口文件:templates/b101_会跳舞的机器人/v2/index.html
|
|
64
64
|
- 主能力标签:表达构建
|
|
65
65
|
- 任务类型标签:视听表达
|
|
66
|
-
- 关键词:
|
|
66
|
+
- 关键词:b101_会跳舞的机器人, 先把机器人视频放到网页上,再配上音乐,最后调一调播放效果,让机器人更像在跳舞。, 已经能开始动手,但还不太会把多种素材组织到一起的学生, 会做简单网页,但还不太会处理视频、音乐和动画配合的学生, 想从静态页面进入多媒体网页表达的学生, 把机器人视频放进网页中, 让视频和音乐形成基本配合, 尝试处理循环播放和播放节奏, 让网页从静态展示变成更有动态感的作品, 会跳舞的机器人, 表达构建, 视听表达
|
|
67
67
|
|
|
68
68
|
## C100 给小组作品做一次小修改
|
|
69
69
|
- ID:6490af0a
|
|
70
|
-
- 版本:
|
|
71
|
-
- 路径:templates/c100_给小组作品做一次小修改/
|
|
72
|
-
- 入口文件:templates/c100_给小组作品做一次小修改/
|
|
70
|
+
- 版本:v2
|
|
71
|
+
- 路径:templates/c100_给小组作品做一次小修改/v2
|
|
72
|
+
- 入口文件:templates/c100_给小组作品做一次小修改/v2/index.html
|
|
73
73
|
- 主能力标签:问题判断
|
|
74
74
|
- 任务类型标签:改编优化
|
|
75
75
|
- 关键词:c100_给小组作品做一次小修改, 先看这 3 个小组作品,找出一个你最想改的小地方,再把这个修改要求说给 AI。, 已经能看懂图片内容,但不太会说哪里有问题的学生, 会说喜欢或不喜欢,但不太会提出具体修改点的学生, 已经能做第一版作品,想练习小步修改的学生, 先发现一个具体问题, 把大问题收成一个最小修改点, 用简单的话把修改要求说清楚, 比较修改前后有没有更接近自己的想法, 给小组作品做一次小修改, 问题判断, 改编优化
|
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`
|