@aiyiran/myclaw 1.1.87 → 1.1.89
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.
|
@@ -632,32 +632,23 @@
|
|
|
632
632
|
|
|
633
633
|
var fullPath = asset.path || asset.name || '未命名';
|
|
634
634
|
var parts = fullPath.split('/').filter(function (p) { return p !== ''; });
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
var
|
|
638
|
-
var
|
|
639
|
-
if (parts.length >= 3) {
|
|
640
|
-
// 有多层:上行 = 除最后两段外的路径,下行 = 最后两段
|
|
641
|
-
topPart = parts.slice(0, parts.length - 2).join('/') + '/';
|
|
642
|
-
bottomPart = parts.slice(parts.length - 2).join('/');
|
|
643
|
-
} else if (parts.length === 2) {
|
|
644
|
-
// 只有一层目录:只显示下行(dir/file)
|
|
645
|
-
topPart = '';
|
|
646
|
-
bottomPart = parts.join('/');
|
|
647
|
-
}
|
|
635
|
+
// 上行:相对路径(除文件名外的所有目录)
|
|
636
|
+
// 下行:文件名
|
|
637
|
+
var fileName = parts[parts.length - 1] || fullPath;
|
|
638
|
+
var dirPath = parts.length >= 2 ? parts.slice(0, parts.length - 1).join('/') + '/' : '';
|
|
648
639
|
|
|
649
|
-
//
|
|
650
|
-
if (
|
|
640
|
+
// 上行:相对路径(灰色极小字)
|
|
641
|
+
if (dirPath) {
|
|
651
642
|
var dirSpan = document.createElement('span');
|
|
652
|
-
dirSpan.textContent =
|
|
643
|
+
dirSpan.textContent = dirPath;
|
|
653
644
|
dirSpan.title = fullPath;
|
|
654
|
-
dirSpan.style.cssText = 'color:#666;font-size:9px;line-height:1.3;white-space:nowrap;';
|
|
645
|
+
dirSpan.style.cssText = 'color:#666;font-size:9px;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
|
|
655
646
|
fname.appendChild(dirSpan);
|
|
656
647
|
}
|
|
657
648
|
|
|
658
|
-
//
|
|
649
|
+
// 下行:文件名(大字体)
|
|
659
650
|
var nameSpan = document.createElement('span');
|
|
660
|
-
nameSpan.textContent =
|
|
651
|
+
nameSpan.textContent = fileName;
|
|
661
652
|
nameSpan.title = fullPath;
|
|
662
653
|
nameSpan.style.cssText = 'font-size:13px;font-weight:500;line-height:1.5;white-space:nowrap;';
|
|
663
654
|
fname.appendChild(nameSpan);
|
|
@@ -1230,8 +1221,6 @@
|
|
|
1230
1221
|
row.appendChild(row3);
|
|
1231
1222
|
leftPane.appendChild(row);
|
|
1232
1223
|
|
|
1233
|
-
// 默认选中第一条并预览
|
|
1234
|
-
if (idx === 0) setActive();
|
|
1235
1224
|
});
|
|
1236
1225
|
}
|
|
1237
1226
|
|
|
@@ -2366,9 +2355,8 @@
|
|
|
2366
2355
|
|
|
2367
2356
|
var deployBtn = document.createElement('button');
|
|
2368
2357
|
deployBtn.textContent = '⬇创建 -> 训练场';
|
|
2369
|
-
deployBtn.
|
|
2370
|
-
deployBtn.
|
|
2371
|
-
deployBtn.onmouseleave = function () { deployBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
|
2358
|
+
deployBtn.disabled = true;
|
|
2359
|
+
deployBtn.style.cssText = _btnBase + 'background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);color:rgba(205,214,244,0.3);cursor:not-allowed;';
|
|
2372
2360
|
|
|
2373
2361
|
var copyLocalBtn = document.createElement('button');
|
|
2374
2362
|
copyLocalBtn.textContent = '⬇创建 -> 新伙伴';
|
|
@@ -2393,9 +2381,9 @@
|
|
|
2393
2381
|
var footerStatus = document.createElement('span');
|
|
2394
2382
|
footerStatus.style.cssText = 'font-size:11px;color:rgba(205,214,244,0.45);flex:1;';
|
|
2395
2383
|
|
|
2396
|
-
rightFooter.appendChild(deployBtn);
|
|
2397
2384
|
rightFooter.appendChild(copyLocalBtn);
|
|
2398
2385
|
rightFooter.appendChild(promptBtn);
|
|
2386
|
+
rightFooter.appendChild(deployBtn);
|
|
2399
2387
|
rightFooter.appendChild(footerStatus);
|
|
2400
2388
|
|
|
2401
2389
|
// currentTpl 在 setActive 时更新,按钮 onclick 通过闭包读取
|
|
@@ -2594,7 +2582,7 @@
|
|
|
2594
2582
|
// ── 并行:加载本地 index + 检查 CDN 更新 ──────────────────────────────
|
|
2595
2583
|
var _pollTimer = null; // 提到外层,doSync 守卫用
|
|
2596
2584
|
|
|
2597
|
-
function doSync() {
|
|
2585
|
+
function doSync(force) {
|
|
2598
2586
|
// 轮询进行中时点击无效,避免重复起 timer
|
|
2599
2587
|
if (_pollTimer) return;
|
|
2600
2588
|
headSyncBtn.disabled = true;
|
|
@@ -2646,7 +2634,7 @@
|
|
|
2646
2634
|
}, 3000);
|
|
2647
2635
|
}
|
|
2648
2636
|
|
|
2649
|
-
fetch(MYCLAW_API_BASE + '/api/sync-templates?force=1')
|
|
2637
|
+
fetch(MYCLAW_API_BASE + '/api/sync-templates' + (force ? '?force=1' : ''))
|
|
2650
2638
|
.then(function (r) { return r.json(); })
|
|
2651
2639
|
.then(function (sync) {
|
|
2652
2640
|
headSyncBtn.disabled = false;
|
|
@@ -2680,7 +2668,7 @@
|
|
|
2680
2668
|
});
|
|
2681
2669
|
}
|
|
2682
2670
|
|
|
2683
|
-
headSyncBtn.onclick = doSync;
|
|
2671
|
+
headSyncBtn.onclick = function () { doSync(true); };
|
|
2684
2672
|
doSync();
|
|
2685
2673
|
|
|
2686
2674
|
fetch(MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(TEMPLATE_ROOT + '/template-index.json'))
|
package/package.json
CHANGED
package/server/sync_workspace.py
CHANGED
|
@@ -65,6 +65,9 @@ HISTORY_FILENAME = "history.json"
|
|
|
65
65
|
_rollback_suppressed: dict = {}
|
|
66
66
|
_ROLLBACK_SUPPRESS_TTL = 3.0 # 秒
|
|
67
67
|
|
|
68
|
+
# 时间窗口合并:同一文件在此时间窗口内的多次变更只保留最新快照(覆盖,不新增版本)
|
|
69
|
+
_HISTORY_MERGE_WINDOW = 20.0 # 秒
|
|
70
|
+
|
|
68
71
|
|
|
69
72
|
def _is_rollback_suppressed(abs_path: str) -> bool:
|
|
70
73
|
ts = _rollback_suppressed.get(abs_path)
|
|
@@ -94,36 +97,70 @@ def save_history_version(workspace_id: str, relative_path: str, file_path: str):
|
|
|
94
97
|
|
|
95
98
|
history_data = _load_history_json(history_json_path, workspace_id)
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
# 检查该文件最后一次快照是否在合并窗口内
|
|
101
|
+
now_ts = time.time()
|
|
102
|
+
records = history_data.get("records", [])
|
|
103
|
+
last_record = next(
|
|
104
|
+
(r for r in reversed(records) if r.get("path") == relative_path),
|
|
105
|
+
None
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if last_record:
|
|
109
|
+
try:
|
|
110
|
+
last_ts = datetime.fromisoformat(last_record["snapshot_at"]).timestamp()
|
|
111
|
+
except Exception:
|
|
112
|
+
last_ts = 0
|
|
113
|
+
within_window = (now_ts - last_ts) < _HISTORY_MERGE_WINDOW
|
|
114
|
+
else:
|
|
115
|
+
within_window = False
|
|
99
116
|
|
|
100
|
-
# 复制当前文件内容到 __history__/vN/<relative_path>
|
|
101
117
|
rel_parts = relative_path.replace("\\", "/").split("/")
|
|
102
|
-
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
103
|
-
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
104
|
-
try:
|
|
105
|
-
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
106
|
-
dst.write(src.read())
|
|
107
|
-
except Exception as e:
|
|
108
|
-
print(f"[history] 快照写入失败: {e}")
|
|
109
|
-
return
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
if within_window:
|
|
120
|
+
# 覆盖最后一次快照,不新增版本
|
|
121
|
+
ver_dir_name = last_record["version_dir"]
|
|
122
|
+
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
123
|
+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
124
|
+
try:
|
|
125
|
+
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
126
|
+
dst.write(src.read())
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"[history] 快照覆盖失败: {e}")
|
|
129
|
+
return
|
|
130
|
+
# 更新该 record 的时间戳
|
|
131
|
+
last_record["snapshot_at"] = now_iso()
|
|
132
|
+
with open(history_json_path, "w", encoding="utf-8") as f:
|
|
133
|
+
json.dump(history_data, f, ensure_ascii=False, indent=2)
|
|
134
|
+
print(f"[history] 合并覆盖快照 {ver_dir_name}: {relative_path}")
|
|
135
|
+
else:
|
|
136
|
+
# 超出时间窗口,新增版本
|
|
137
|
+
next_ver = history_data.get("next_version", 1)
|
|
138
|
+
ver_dir_name = f"v{next_ver}"
|
|
122
139
|
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
141
|
+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
142
|
+
try:
|
|
143
|
+
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
144
|
+
dst.write(src.read())
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(f"[history] 快照写入失败: {e}")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
history_data["next_version"] = next_ver + 1
|
|
150
|
+
history_data["records"].append({
|
|
151
|
+
"version": next_ver,
|
|
152
|
+
"version_dir": ver_dir_name,
|
|
153
|
+
"path": relative_path,
|
|
154
|
+
"snapshot_at": now_iso()
|
|
155
|
+
})
|
|
156
|
+
if "current_versions" not in history_data:
|
|
157
|
+
history_data["current_versions"] = {}
|
|
158
|
+
history_data["current_versions"][relative_path] = next_ver
|
|
159
|
+
|
|
160
|
+
with open(history_json_path, "w", encoding="utf-8") as f:
|
|
161
|
+
json.dump(history_data, f, ensure_ascii=False, indent=2)
|
|
125
162
|
|
|
126
|
-
|
|
163
|
+
print(f"[history] 已保存快照 {ver_dir_name}: {relative_path}")
|
|
127
164
|
|
|
128
165
|
|
|
129
166
|
def rollback_to_version(workspace_id: str, relative_path: str, version_dir: str):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"index_updated_at": "2026-05-
|
|
2
|
+
"index_updated_at": "2026-05-08T02:45:34Z",
|
|
3
3
|
"templates": {
|
|
4
4
|
"696de592": {
|
|
5
5
|
"id": "696de592",
|
|
@@ -329,9 +329,9 @@
|
|
|
329
329
|
"编号": "101",
|
|
330
330
|
"名称": "万物图签:我的AI卡牌宇宙",
|
|
331
331
|
"文件夹名": "c101_万物图签:我的AI卡牌宇宙",
|
|
332
|
-
"version": "
|
|
333
|
-
"路径": "templates/c101_万物图签:我的AI卡牌宇宙/
|
|
334
|
-
"入口文件": "templates/c101_万物图签:我的AI卡牌宇宙/
|
|
332
|
+
"version": "v6",
|
|
333
|
+
"路径": "templates/c101_万物图签:我的AI卡牌宇宙/v6",
|
|
334
|
+
"入口文件": "templates/c101_万物图签:我的AI卡牌宇宙/v6/index.html",
|
|
335
335
|
"一句话说明": "分四个阶段逐步做出你的精灵卡牌:先让角色诞生,再加属性和特效,最后扩展成一个系列。",
|
|
336
336
|
"主能力标签": "第四能力:迭代推进",
|
|
337
337
|
"任务类型标签": "视听表达",
|
|
@@ -348,7 +348,7 @@
|
|
|
348
348
|
"第四能力:迭代推进",
|
|
349
349
|
"视听表达"
|
|
350
350
|
],
|
|
351
|
-
"updated_at": "2026-05-
|
|
351
|
+
"updated_at": "2026-05-08T02:45:29Z"
|
|
352
352
|
},
|
|
353
353
|
"cf03758c": {
|
|
354
354
|
"id": "cf03758c",
|
|
@@ -112,9 +112,9 @@
|
|
|
112
112
|
|
|
113
113
|
## C101 万物图签:我的AI卡牌宇宙
|
|
114
114
|
- ID:a2f9502c
|
|
115
|
-
- 版本:
|
|
116
|
-
- 路径:templates/c101_万物图签:我的AI卡牌宇宙/
|
|
117
|
-
- 入口文件:templates/c101_万物图签:我的AI卡牌宇宙/
|
|
115
|
+
- 版本:v6
|
|
116
|
+
- 路径:templates/c101_万物图签:我的AI卡牌宇宙/v6
|
|
117
|
+
- 入口文件:templates/c101_万物图签:我的AI卡牌宇宙/v6/index.html
|
|
118
118
|
- 主能力标签:第四能力:迭代推进
|
|
119
119
|
- 任务类型标签:视听表达
|
|
120
120
|
- 关键词:c101_万物图签:我的AI卡牌宇宙, 分四个阶段逐步做出你的精灵卡牌:先让角色诞生,再加属性和特效,最后扩展成一个系列。, 已经做过基础卡牌任务的学生, 对卡牌游戏有兴趣、想做出有系统感作品的学生, 会做但容易停在第一版不改的学生, 在复杂框架上读懂结构,再做定向修改, 规划主题和阵营分组逻辑, 明确识别问题并做一次有意识的迭代升级, 万物图签:我的AI卡牌宇宙, 第四能力:迭代推进, 视听表达
|